Clean up the 3D renderer for enhanced flexibility (#1895)

* Give `GPU2D::Unit` a virtual destructor

- Undefined behavior avoided!

* Add an `array2d` alias

* Move various parts of `GPU2D::SoftRenderer` to `constexpr`

- `SoftRenderer::MosaicTable` is now initialized at compile-time
- Most of the `SoftRenderer::Color*` functions are now `constexpr`
- The aforementioned functions are used with a constant value in at least one place, so they'll be at least partially computed at compile-time

* Generalize `GLRenderer::PrepareCaptureFrame`

- Declare it in the base `Renderer3D` class, but make it empty

* Remove unneeded `virtual` specifiers

* Store `Framebuffer`'s memory in `unique_ptr`s

- Reduce the risk of leaks this way

* Clean up how `GLCompositor` is initialized

- Return it as an `std::optional` instead of a `std::unique_ptr`
- Make `GLCompositor` movable
- Replace `GLCompositor`'s plain arrays with `std::array` to simplify moving

* Pass `GPU` to `GLCompositor`'s important functions instead of passing it to the constructor

* Move `GLCompositor` to be a field within `GLRenderer`

- Some methods were moved up and made `virtual`

* Fix some linker errors

* Set the renderer in the frontend

* Remove unneeded `virtual` specifiers

* Remove `RenderSettings` in favor of just exposing the relevant member variables

* Update the frontend to accommodate the core changes

* Add `constexpr` and `const` to places in the interpolator

* Qualify references to `size_t`

* Construct the `optional` directly instead of using `make_optional`

- It makes the Linux build choke
- I think it's because `GLCompositor`'s constructor is `private`
This commit is contained in:
Jesse Talavera-Greenberg 2023-11-29 09:23:11 -05:00 committed by GitHub
parent e973236203
commit 7caddf9615
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 366 additions and 334 deletions

View File

@ -24,7 +24,6 @@
#include "GPU2D_Soft.h" #include "GPU2D_Soft.h"
#include "GPU3D_Soft.h" #include "GPU3D_Soft.h"
#include "GPU3D_OpenGL.h"
namespace melonDS namespace melonDS
{ {
@ -64,33 +63,24 @@ enum
VRAMDirty need to be reset for the respective VRAM bank. VRAMDirty need to be reset for the respective VRAM bank.
*/ */
GPU::GPU(melonDS::NDS& nds) noexcept : NDS(nds), GPU2D_A(0, *this), GPU2D_B(1, *this), GPU3D(nds) GPU::GPU(melonDS::NDS& nds, std::unique_ptr<Renderer3D>&& renderer3d, std::unique_ptr<GPU2D::Renderer2D>&& renderer2d) noexcept :
NDS(nds),
GPU2D_A(0, *this),
GPU2D_B(1, *this),
GPU3D(nds, renderer3d ? std::move(renderer3d) : std::make_unique<SoftRenderer>(*this)),
GPU2D_Renderer(renderer2d ? std::move(renderer2d) : std::make_unique<GPU2D::SoftRenderer>(*this))
{ {
NDS.RegisterEventFunc(Event_LCD, LCD_StartHBlank, MemberEventFunc(GPU, StartHBlank)); NDS.RegisterEventFunc(Event_LCD, LCD_StartHBlank, MemberEventFunc(GPU, StartHBlank));
NDS.RegisterEventFunc(Event_LCD, LCD_StartScanline, MemberEventFunc(GPU, StartScanline)); NDS.RegisterEventFunc(Event_LCD, LCD_StartScanline, MemberEventFunc(GPU, StartScanline));
NDS.RegisterEventFunc(Event_LCD, LCD_FinishFrame, MemberEventFunc(GPU, FinishFrame)); NDS.RegisterEventFunc(Event_LCD, LCD_FinishFrame, MemberEventFunc(GPU, FinishFrame));
NDS.RegisterEventFunc(Event_DisplayFIFO, 0, MemberEventFunc(GPU, DisplayFIFO)); NDS.RegisterEventFunc(Event_DisplayFIFO, 0, MemberEventFunc(GPU, DisplayFIFO));
GPU2D_Renderer = std::make_unique<GPU2D::SoftRenderer>(*this);
FrontBuffer = 0; FrontBuffer = 0;
Framebuffer[0][0] = NULL; Framebuffer[0][1] = NULL;
Framebuffer[1][0] = NULL; Framebuffer[1][1] = NULL;
Renderer = 0;
} }
GPU::~GPU() noexcept GPU::~GPU() noexcept
{ {
// All unique_ptr fields are automatically cleaned up // All unique_ptr fields are automatically cleaned up
if (Framebuffer[0][0]) delete[] Framebuffer[0][0];
if (Framebuffer[0][1]) delete[] Framebuffer[0][1];
if (Framebuffer[1][0]) delete[] Framebuffer[1][0];
if (Framebuffer[1][1]) delete[] Framebuffer[1][1];
Framebuffer[0][0] = nullptr;
Framebuffer[0][1] = nullptr;
Framebuffer[1][0] = nullptr;
Framebuffer[1][1] = nullptr;
NDS.UnregisterEventFunc(Event_LCD, LCD_StartHBlank); NDS.UnregisterEventFunc(Event_LCD, LCD_StartHBlank);
NDS.UnregisterEventFunc(Event_LCD, LCD_StartScanline); NDS.UnregisterEventFunc(Event_LCD, LCD_StartScanline);
@ -198,9 +188,7 @@ void GPU::Reset() noexcept
GPU3D.Reset(); GPU3D.Reset();
int backbuf = FrontBuffer ? 0 : 1; int backbuf = FrontBuffer ? 0 : 1;
GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1], Framebuffer[backbuf][0]); GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1].get(), Framebuffer[backbuf][0].get());
ResetRenderer();
ResetVRAMCache(); ResetVRAMCache();
@ -216,17 +204,12 @@ void GPU::Stop() noexcept
else else
fbsize = 256 * 192; fbsize = 256 * 192;
memset(Framebuffer[0][0], 0, fbsize*4); memset(Framebuffer[0][0].get(), 0, fbsize*4);
memset(Framebuffer[0][1], 0, fbsize*4); memset(Framebuffer[0][1].get(), 0, fbsize*4);
memset(Framebuffer[1][0], 0, fbsize*4); memset(Framebuffer[1][0].get(), 0, fbsize*4);
memset(Framebuffer[1][1], 0, fbsize*4); memset(Framebuffer[1][1].get(), 0, fbsize*4);
#ifdef OGLRENDERER_ENABLED GPU3D.Stop();
// This needs a better way to know that we're
// using the OpenGL renderer specifically
if (GPU3D.IsRendererAccelerated())
CurGLCompositor->Stop();
#endif
} }
void GPU::DoSavestate(Savestate* file) noexcept void GPU::DoSavestate(Savestate* file) noexcept
@ -300,78 +283,20 @@ void GPU::AssignFramebuffers() noexcept
int backbuf = FrontBuffer ? 0 : 1; int backbuf = FrontBuffer ? 0 : 1;
if (NDS.PowerControl9 & (1<<15)) if (NDS.PowerControl9 & (1<<15))
{ {
GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][0], Framebuffer[backbuf][1]); GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][0].get(), Framebuffer[backbuf][1].get());
} }
else else
{ {
GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1], Framebuffer[backbuf][0]); GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1].get(), Framebuffer[backbuf][0].get());
} }
} }
void GPU::InitRenderer(int renderer) noexcept void GPU::SetRenderer3D(std::unique_ptr<Renderer3D>&& renderer) noexcept
{ {
#ifdef OGLRENDERER_ENABLED if (renderer == nullptr)
if (renderer == 1)
{
CurGLCompositor = GLCompositor::New(*this);
// Create opengl renderer
if (!CurGLCompositor)
{
// Fallback on software renderer
renderer = 0;
GPU3D.SetCurrentRenderer(std::make_unique<SoftRenderer>(*this)); GPU3D.SetCurrentRenderer(std::make_unique<SoftRenderer>(*this));
}
GPU3D.SetCurrentRenderer(GLRenderer::New(*this));
if (!GPU3D.GetCurrentRenderer())
{
// Fallback on software renderer
CurGLCompositor.reset();
renderer = 0;
GPU3D.SetCurrentRenderer(std::make_unique<SoftRenderer>(*this));
}
}
else else
#endif GPU3D.SetCurrentRenderer(std::move(renderer));
{
GPU3D.SetCurrentRenderer(std::make_unique<SoftRenderer>(*this));
}
Renderer = renderer;
}
void GPU::DeInitRenderer() noexcept
{
// Delete the 3D renderer, if it exists
GPU3D.SetCurrentRenderer(nullptr);
#ifdef OGLRENDERER_ENABLED
// Delete the compositor, if one exists
CurGLCompositor.reset();
#endif
}
void GPU::ResetRenderer() noexcept
{
if (Renderer == 0)
{
GPU3D.GetCurrentRenderer()->Reset();
}
#ifdef OGLRENDERER_ENABLED
else
{
CurGLCompositor->Reset();
GPU3D.GetCurrentRenderer()->Reset();
}
#endif
}
void GPU::SetRenderSettings(int renderer, RenderSettings& settings) noexcept
{
if (renderer != Renderer)
{
DeInitRenderer();
InitRenderer(renderer);
}
int fbsize; int fbsize;
if (GPU3D.IsRendererAccelerated()) if (GPU3D.IsRendererAccelerated())
@ -379,34 +304,17 @@ void GPU::SetRenderSettings(int renderer, RenderSettings& settings) noexcept
else else
fbsize = 256 * 192; fbsize = 256 * 192;
if (Framebuffer[0][0]) { delete[] Framebuffer[0][0]; Framebuffer[0][0] = nullptr; } Framebuffer[0][0] = std::make_unique<u32[]>(fbsize);
if (Framebuffer[1][0]) { delete[] Framebuffer[1][0]; Framebuffer[1][0] = nullptr; } Framebuffer[1][0] = std::make_unique<u32[]>(fbsize);
if (Framebuffer[0][1]) { delete[] Framebuffer[0][1]; Framebuffer[0][1] = nullptr; } Framebuffer[0][1] = std::make_unique<u32[]>(fbsize);
if (Framebuffer[1][1]) { delete[] Framebuffer[1][1]; Framebuffer[1][1] = nullptr; } Framebuffer[1][1] = std::make_unique<u32[]>(fbsize);
Framebuffer[0][0] = new u32[fbsize]; memset(Framebuffer[0][0].get(), 0, fbsize*4);
Framebuffer[1][0] = new u32[fbsize]; memset(Framebuffer[1][0].get(), 0, fbsize*4);
Framebuffer[0][1] = new u32[fbsize]; memset(Framebuffer[0][1].get(), 0, fbsize*4);
Framebuffer[1][1] = new u32[fbsize]; memset(Framebuffer[1][1].get(), 0, fbsize*4);
memset(Framebuffer[0][0], 0, fbsize*4);
memset(Framebuffer[1][0], 0, fbsize*4);
memset(Framebuffer[0][1], 0, fbsize*4);
memset(Framebuffer[1][1], 0, fbsize*4);
AssignFramebuffers(); AssignFramebuffers();
if (Renderer == 0)
{
GPU3D.GetCurrentRenderer()->SetRenderSettings(settings);
}
#ifdef OGLRENDERER_ENABLED
else
{
CurGLCompositor->SetRenderSettings(settings);
GPU3D.GetCurrentRenderer()->SetRenderSettings(settings);
}
#endif
} }
@ -1026,8 +934,8 @@ void GPU::BlankFrame() noexcept
else else
fbsize = 256 * 192; fbsize = 256 * 192;
memset(Framebuffer[backbuf][0], 0, fbsize*4); memset(Framebuffer[backbuf][0].get(), 0, fbsize*4);
memset(Framebuffer[backbuf][1], 0, fbsize*4); memset(Framebuffer[backbuf][1].get(), 0, fbsize*4);
FrontBuffer = backbuf; FrontBuffer = backbuf;
AssignFramebuffers(); AssignFramebuffers();
@ -1123,11 +1031,9 @@ void GPU::StartScanline(u32 line) noexcept
GPU2D_B.VBlank(); GPU2D_B.VBlank();
GPU3D.VBlank(); GPU3D.VBlank();
#ifdef OGLRENDERER_ENABLED
// Need a better way to identify the openGL renderer in particular // Need a better way to identify the openGL renderer in particular
if (GPU3D.IsRendererAccelerated()) if (GPU3D.IsRendererAccelerated())
CurGLCompositor->RenderFrame(); GPU3D.Blit();
#endif
} }
} }

View File

@ -25,10 +25,6 @@
#include "GPU3D.h" #include "GPU3D.h"
#include "NonStupidBitfield.h" #include "NonStupidBitfield.h"
#ifdef OGLRENDERER_ENABLED
#include "GPU_OpenGL.h"
#endif
namespace melonDS namespace melonDS
{ {
class GPU3D; class GPU3D;
@ -56,33 +52,30 @@ struct VRAMTrackingSet
NonStupidBitField<Size/VRAMDirtyGranularity> DeriveState(u32* currentMappings, GPU& gpu); NonStupidBitField<Size/VRAMDirtyGranularity> DeriveState(u32* currentMappings, GPU& gpu);
}; };
struct RenderSettings
{
bool Soft_Threaded;
int GL_ScaleFactor;
bool GL_BetterPolygons;
};
class GPU class GPU
{ {
public: public:
GPU(melonDS::NDS& nds) noexcept; explicit GPU(melonDS::NDS& nds, std::unique_ptr<Renderer3D>&& renderer3d = nullptr, std::unique_ptr<GPU2D::Renderer2D>&& renderer2d = nullptr) noexcept;
~GPU() noexcept; ~GPU() noexcept;
void Reset() noexcept; void Reset() noexcept;
void Stop() noexcept; void Stop() noexcept;
void DoSavestate(Savestate* file) noexcept; void DoSavestate(Savestate* file) noexcept;
[[deprecated("Set the renderer directly instead of using an integer code")]] void InitRenderer(int renderer) noexcept; /// Sets the active renderer to the renderer given in the provided pointer.
void DeInitRenderer() noexcept; /// The pointer is moved-from, so it will be \c nullptr after this method is called.
void ResetRenderer() noexcept; /// If the pointer is \c nullptr, the renderer is reset to the default renderer.
void SetRenderer3D(std::unique_ptr<Renderer3D>&& renderer) noexcept;
void SetRenderSettings(int renderer, RenderSettings& settings) noexcept; [[nodiscard]] const Renderer3D& GetRenderer3D() const noexcept { return GPU3D.GetCurrentRenderer(); }
[[nodiscard]] Renderer3D& GetRenderer3D() noexcept { return GPU3D.GetCurrentRenderer(); }
u8* GetUniqueBankPtr(u32 mask, u32 offset) noexcept; u8* GetUniqueBankPtr(u32 mask, u32 offset) noexcept;
const u8* GetUniqueBankPtr(u32 mask, u32 offset) const noexcept; const u8* GetUniqueBankPtr(u32 mask, u32 offset) const noexcept;
void SetRenderer2D(std::unique_ptr<GPU2D::Renderer2D>&& renderer) noexcept { GPU2D_Renderer = std::move(renderer); }
[[nodiscard]] const GPU2D::Renderer2D& GetRenderer2D() const noexcept { return *GPU2D_Renderer; }
[[nodiscard]] GPU2D::Renderer2D& GetRenderer2D() noexcept { return *GPU2D_Renderer; }
void MapVRAM_AB(u32 bank, u8 cnt) noexcept; void MapVRAM_AB(u32 bank, u8 cnt) noexcept;
void MapVRAM_CD(u32 bank, u8 cnt) noexcept; void MapVRAM_CD(u32 bank, u8 cnt) noexcept;
void MapVRAM_E(u32 bank, u8 cnt) noexcept; void MapVRAM_E(u32 bank, u8 cnt) noexcept;
@ -578,7 +571,7 @@ public:
u8* VRAMPtr_BOBJ[0x8] {}; u8* VRAMPtr_BOBJ[0x8] {};
int FrontBuffer = 0; int FrontBuffer = 0;
u32* Framebuffer[2][2] {}; std::unique_ptr<u32[]> Framebuffer[2][2] {};
GPU2D::Unit GPU2D_A; GPU2D::Unit GPU2D_A;
GPU2D::Unit GPU2D_B; GPU2D::Unit GPU2D_B;
@ -611,11 +604,6 @@ public:
u8 VRAMFlat_Texture[512*1024] {}; u8 VRAMFlat_Texture[512*1024] {};
u8 VRAMFlat_TexPal[128*1024] {}; u8 VRAMFlat_TexPal[128*1024] {};
int Renderer = 0;
#ifdef OGLRENDERER_ENABLED
std::unique_ptr<GLCompositor> CurGLCompositor = nullptr;
#endif
private: private:
void ResetVRAMCache() noexcept; void ResetVRAMCache() noexcept;
void AssignFramebuffers() noexcept; void AssignFramebuffers() noexcept;

View File

@ -35,7 +35,7 @@ public:
// take a reference to the GPU so we can access its state // take a reference to the GPU so we can access its state
// and ensure that it's not null // and ensure that it's not null
Unit(u32 num, melonDS::GPU& gpu); Unit(u32 num, melonDS::GPU& gpu);
virtual ~Unit() = default;
Unit(const Unit&) = delete; Unit(const Unit&) = delete;
Unit& operator=(const Unit&) = delete; Unit& operator=(const Unit&) = delete;

View File

@ -27,68 +27,7 @@ namespace GPU2D
SoftRenderer::SoftRenderer(melonDS::GPU& gpu) SoftRenderer::SoftRenderer(melonDS::GPU& gpu)
: Renderer2D(), GPU(gpu) : Renderer2D(), GPU(gpu)
{ {
// initialize mosaic table // mosaic table is initialized at compile-time
for (int m = 0; m < 16; m++)
{
for (int x = 0; x < 256; x++)
{
int offset = x % (m+1);
MosaicTable[m][x] = offset;
}
}
}
u32 SoftRenderer::ColorBlend4(u32 val1, u32 val2, u32 eva, u32 evb)
{
u32 r = (((val1 & 0x00003F) * eva) + ((val2 & 0x00003F) * evb) + 0x000008) >> 4;
u32 g = ((((val1 & 0x003F00) * eva) + ((val2 & 0x003F00) * evb) + 0x000800) >> 4) & 0x007F00;
u32 b = ((((val1 & 0x3F0000) * eva) + ((val2 & 0x3F0000) * evb) + 0x080000) >> 4) & 0x7F0000;
if (r > 0x00003F) r = 0x00003F;
if (g > 0x003F00) g = 0x003F00;
if (b > 0x3F0000) b = 0x3F0000;
return r | g | b | 0xFF000000;
}
u32 SoftRenderer::ColorBlend5(u32 val1, u32 val2)
{
u32 eva = ((val1 >> 24) & 0x1F) + 1;
u32 evb = 32 - eva;
if (eva == 32) return val1;
u32 r = (((val1 & 0x00003F) * eva) + ((val2 & 0x00003F) * evb) + 0x000010) >> 5;
u32 g = ((((val1 & 0x003F00) * eva) + ((val2 & 0x003F00) * evb) + 0x001000) >> 5) & 0x007F00;
u32 b = ((((val1 & 0x3F0000) * eva) + ((val2 & 0x3F0000) * evb) + 0x100000) >> 5) & 0x7F0000;
if (r > 0x00003F) r = 0x00003F;
if (g > 0x003F00) g = 0x003F00;
if (b > 0x3F0000) b = 0x3F0000;
return r | g | b | 0xFF000000;
}
u32 SoftRenderer::ColorBrightnessUp(u32 val, u32 factor, u32 bias)
{
u32 rb = val & 0x3F003F;
u32 g = val & 0x003F00;
rb += (((((0x3F003F - rb) * factor) + (bias*0x010001)) >> 4) & 0x3F003F);
g += (((((0x003F00 - g ) * factor) + (bias*0x000100)) >> 4) & 0x003F00);
return rb | g | 0xFF000000;
}
u32 SoftRenderer::ColorBrightnessDown(u32 val, u32 factor, u32 bias)
{
u32 rb = val & 0x3F003F;
u32 g = val & 0x003F00;
rb -= ((((rb * factor) + (bias*0x010001)) >> 4) & 0x3F003F);
g -= ((((g * factor) + (bias*0x000100)) >> 4) & 0x003F00);
return rb | g | 0xFF000000;
} }
u32 SoftRenderer::ColorComposite(int i, u32 val1, u32 val2) u32 SoftRenderer::ColorComposite(int i, u32 val1, u32 val2)
@ -365,11 +304,11 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit)
void SoftRenderer::VBlankEnd(Unit* unitA, Unit* unitB) void SoftRenderer::VBlankEnd(Unit* unitA, Unit* unitB)
{ {
#ifdef OGLRENDERER_ENABLED #ifdef OGLRENDERER_ENABLED
if (GPU.GPU3D.IsRendererAccelerated()) if (Renderer3D& renderer3d = GPU.GPU3D.GetCurrentRenderer(); renderer3d.Accelerated)
{ {
if ((unitA->CaptureCnt & (1<<31)) && (((unitA->CaptureCnt >> 29) & 0x3) != 1)) if ((unitA->CaptureCnt & (1<<31)) && (((unitA->CaptureCnt >> 29) & 0x3) != 1))
{ {
reinterpret_cast<GLRenderer*>(GPU.GPU3D.GetCurrentRenderer())->PrepareCaptureFrame(); renderer3d.PrepareCaptureFrame();
} }
} }
#endif #endif
@ -779,7 +718,7 @@ void SoftRenderer::DrawScanline_BGOBJ(u32 line)
memset(WindowMask, 0xFF, 256); memset(WindowMask, 0xFF, 256);
ApplySpriteMosaicX(); ApplySpriteMosaicX();
CurBGXMosaicTable = MosaicTable[CurUnit->BGMosaicSize[0]]; CurBGXMosaicTable = MosaicTable[CurUnit->BGMosaicSize[0]].data();
switch (CurUnit->DispCnt & 0x7) switch (CurUnit->DispCnt & 0x7)
{ {
@ -1564,7 +1503,7 @@ void SoftRenderer::ApplySpriteMosaicX()
u32* objLine = OBJLine[CurUnit->Num]; u32* objLine = OBJLine[CurUnit->Num];
u8* curOBJXMosaicTable = MosaicTable[CurUnit->OBJMosaicSize[1]]; u8* curOBJXMosaicTable = MosaicTable[CurUnit->OBJMosaicSize[1]].data();
u32 lastcolor = objLine[0]; u32 lastcolor = objLine[0];

View File

@ -49,12 +49,74 @@ private:
u32 NumSprites[2]; u32 NumSprites[2];
u8* CurBGXMosaicTable; u8* CurBGXMosaicTable;
u8 MosaicTable[16][256]; array2d<u8, 16, 256> MosaicTable = []() constexpr
{
array2d<u8, 16, 256> table {};
// initialize mosaic table
for (int m = 0; m < 16; m++)
{
for (int x = 0; x < 256; x++)
{
int offset = x % (m+1);
table[m][x] = offset;
}
}
u32 ColorBlend4(u32 val1, u32 val2, u32 eva, u32 evb); return table;
u32 ColorBlend5(u32 val1, u32 val2); }();
u32 ColorBrightnessUp(u32 val, u32 factor, u32 bias);
u32 ColorBrightnessDown(u32 val, u32 factor, u32 bias); static constexpr u32 ColorBlend4(u32 val1, u32 val2, u32 eva, u32 evb) noexcept
{
u32 r = (((val1 & 0x00003F) * eva) + ((val2 & 0x00003F) * evb) + 0x000008) >> 4;
u32 g = ((((val1 & 0x003F00) * eva) + ((val2 & 0x003F00) * evb) + 0x000800) >> 4) & 0x007F00;
u32 b = ((((val1 & 0x3F0000) * eva) + ((val2 & 0x3F0000) * evb) + 0x080000) >> 4) & 0x7F0000;
if (r > 0x00003F) r = 0x00003F;
if (g > 0x003F00) g = 0x003F00;
if (b > 0x3F0000) b = 0x3F0000;
return r | g | b | 0xFF000000;
}
static constexpr u32 ColorBlend5(u32 val1, u32 val2) noexcept
{
u32 eva = ((val1 >> 24) & 0x1F) + 1;
u32 evb = 32 - eva;
if (eva == 32) return val1;
u32 r = (((val1 & 0x00003F) * eva) + ((val2 & 0x00003F) * evb) + 0x000010) >> 5;
u32 g = ((((val1 & 0x003F00) * eva) + ((val2 & 0x003F00) * evb) + 0x001000) >> 5) & 0x007F00;
u32 b = ((((val1 & 0x3F0000) * eva) + ((val2 & 0x3F0000) * evb) + 0x100000) >> 5) & 0x7F0000;
if (r > 0x00003F) r = 0x00003F;
if (g > 0x003F00) g = 0x003F00;
if (b > 0x3F0000) b = 0x3F0000;
return r | g | b | 0xFF000000;
}
static constexpr u32 ColorBrightnessUp(u32 val, u32 factor, u32 bias) noexcept
{
u32 rb = val & 0x3F003F;
u32 g = val & 0x003F00;
rb += (((((0x3F003F - rb) * factor) + (bias*0x010001)) >> 4) & 0x3F003F);
g += (((((0x003F00 - g ) * factor) + (bias*0x000100)) >> 4) & 0x003F00);
return rb | g | 0xFF000000;
}
static constexpr u32 ColorBrightnessDown(u32 val, u32 factor, u32 bias) noexcept
{
u32 rb = val & 0x3F003F;
u32 g = val & 0x003F00;
rb -= ((((rb * factor) + (bias*0x010001)) >> 4) & 0x3F003F);
g -= ((((g * factor) + (bias*0x000100)) >> 4) & 0x003F00);
return rb | g | 0xFF000000;
}
u32 ColorComposite(int i, u32 val1, u32 val2); u32 ColorComposite(int i, u32 val1, u32 val2);
template<u32 bgmode> void DrawScanlineBGMode(u32 line); template<u32 bgmode> void DrawScanlineBGMode(u32 line);

View File

@ -22,6 +22,7 @@
#include "NDS.h" #include "NDS.h"
#include "GPU.h" #include "GPU.h"
#include "FIFO.h" #include "FIFO.h"
#include "GPU3D_Soft.h"
#include "Platform.h" #include "Platform.h"
namespace melonDS namespace melonDS
@ -139,7 +140,9 @@ const u8 CmdNumParams[256] =
void MatrixLoadIdentity(s32* m); void MatrixLoadIdentity(s32* m);
GPU3D::GPU3D(melonDS::NDS& nds) noexcept : NDS(nds) GPU3D::GPU3D(melonDS::NDS& nds, std::unique_ptr<Renderer3D>&& renderer) noexcept :
NDS(nds),
CurrentRenderer(renderer ? std::move(renderer) : std::make_unique<SoftRenderer>(nds.GPU))
{ {
} }
@ -2336,6 +2339,12 @@ void GPU3D::RestartFrame() noexcept
CurrentRenderer->RestartFrame(); CurrentRenderer->RestartFrame();
} }
void GPU3D::Stop() noexcept
{
if (CurrentRenderer)
CurrentRenderer->Stop();
}
bool YSort(Polygon* a, Polygon* b) bool YSort(Polygon* a, Polygon* b)
{ {
@ -2888,6 +2897,12 @@ void GPU3D::Write32(u32 addr, u32 val) noexcept
Log(LogLevel::Debug, "unknown GPU3D write32 %08X %08X\n", addr, val); Log(LogLevel::Debug, "unknown GPU3D write32 %08X %08X\n", addr, val);
} }
void GPU3D::Blit() noexcept
{
if (CurrentRenderer)
CurrentRenderer->Blit();
}
Renderer3D::Renderer3D(bool Accelerated) Renderer3D::Renderer3D(bool Accelerated)
: Accelerated(Accelerated) : Accelerated(Accelerated)
{ } { }

View File

@ -27,7 +27,7 @@
namespace melonDS namespace melonDS
{ {
struct RenderSettings; class GPU;
struct Vertex struct Vertex
{ {
@ -86,7 +86,7 @@ class NDS;
class GPU3D class GPU3D
{ {
public: public:
GPU3D(melonDS::NDS& nds) noexcept; GPU3D(melonDS::NDS& nds, std::unique_ptr<Renderer3D>&& renderer = nullptr) noexcept;
~GPU3D() noexcept = default; ~GPU3D() noexcept = default;
void Reset() noexcept; void Reset() noexcept;
@ -106,6 +106,7 @@ public:
void VCount215() noexcept; void VCount215() noexcept;
void RestartFrame() noexcept; void RestartFrame() noexcept;
void Stop() noexcept;
void SetRenderXPos(u16 xpos) noexcept; void SetRenderXPos(u16 xpos) noexcept;
[[nodiscard]] u16 GetRenderXPos() const noexcept { return RenderXPos; } [[nodiscard]] u16 GetRenderXPos() const noexcept { return RenderXPos; }
@ -114,8 +115,8 @@ public:
void WriteToGXFIFO(u32 val) noexcept; void WriteToGXFIFO(u32 val) noexcept;
[[nodiscard]] bool IsRendererAccelerated() const noexcept; [[nodiscard]] bool IsRendererAccelerated() const noexcept;
[[nodiscard]] Renderer3D* GetCurrentRenderer() noexcept { return CurrentRenderer.get(); } [[nodiscard]] Renderer3D& GetCurrentRenderer() noexcept { return *CurrentRenderer; }
[[nodiscard]] const Renderer3D* GetCurrentRenderer() const noexcept { return CurrentRenderer.get(); } [[nodiscard]] const Renderer3D& GetCurrentRenderer() const noexcept { return *CurrentRenderer; }
void SetCurrentRenderer(std::unique_ptr<Renderer3D>&& renderer) noexcept { CurrentRenderer = std::move(renderer); } void SetCurrentRenderer(std::unique_ptr<Renderer3D>&& renderer) noexcept { CurrentRenderer = std::move(renderer); }
u8 Read8(u32 addr) noexcept; u8 Read8(u32 addr) noexcept;
@ -124,6 +125,7 @@ public:
void Write8(u32 addr, u8 val) noexcept; void Write8(u32 addr, u8 val) noexcept;
void Write16(u32 addr, u16 val) noexcept; void Write16(u32 addr, u16 val) noexcept;
void Write32(u32 addr, u32 val) noexcept; void Write32(u32 addr, u32 val) noexcept;
void Blit() noexcept;
private: private:
melonDS::NDS& NDS; melonDS::NDS& NDS;
typedef union typedef union
@ -338,13 +340,13 @@ public:
// are more detailed "traits" that we can ask of the Renderer3D type // are more detailed "traits" that we can ask of the Renderer3D type
const bool Accelerated; const bool Accelerated;
virtual void SetRenderSettings(const RenderSettings& settings) noexcept = 0;
virtual void VCount144() {}; virtual void VCount144() {};
virtual void Stop() {}
virtual void RenderFrame() = 0; virtual void RenderFrame() = 0;
virtual void RestartFrame() {}; virtual void RestartFrame() {};
virtual u32* GetLine(int line) = 0; virtual u32* GetLine(int line) = 0;
virtual void Blit() {};
virtual void PrepareCaptureFrame() {}
protected: protected:
Renderer3D(bool Accelerated); Renderer3D(bool Accelerated);
}; };

View File

@ -97,7 +97,10 @@ void SetupDefaultTexParams(GLuint tex)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
} }
GLRenderer::GLRenderer(melonDS::GPU& gpu) noexcept : Renderer3D(true), GPU(gpu) GLRenderer::GLRenderer(GLCompositor&& compositor, melonDS::GPU& gpu) noexcept :
Renderer3D(true),
GPU(gpu),
CurGLCompositor(std::move(compositor))
{ {
// GLRenderer::New() will be used to actually initialize the renderer; // GLRenderer::New() will be used to actually initialize the renderer;
// The various glDelete* functions silently ignore invalid IDs, // The various glDelete* functions silently ignore invalid IDs,
@ -108,9 +111,14 @@ std::unique_ptr<GLRenderer> GLRenderer::New(melonDS::GPU& gpu) noexcept
{ {
assert(glEnable != nullptr); assert(glEnable != nullptr);
std::optional<GLCompositor> compositor = GLCompositor::New();
if (!compositor)
return nullptr;
// Will be returned if the initialization succeeds, // Will be returned if the initialization succeeds,
// or cleaned up via RAII if it fails. // or cleaned up via RAII if it fails.
std::unique_ptr<GLRenderer> result = std::unique_ptr<GLRenderer>(new GLRenderer(gpu)); std::unique_ptr<GLRenderer> result = std::unique_ptr<GLRenderer>(new GLRenderer(std::move(*compositor), gpu));
compositor = std::nullopt;
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST); glEnable(GL_STENCIL_TEST);
@ -327,14 +335,29 @@ GLRenderer::~GLRenderer()
void GLRenderer::Reset() void GLRenderer::Reset()
{ {
// This is where the compositor's Reset() method would be called,
// except there's no such method right now.
} }
void GLRenderer::SetRenderSettings(const RenderSettings& settings) noexcept void GLRenderer::SetBetterPolygons(bool betterpolygons) noexcept
{ {
int scale = settings.GL_ScaleFactor; SetRenderSettings(betterpolygons, ScaleFactor);
}
void GLRenderer::SetScaleFactor(int scale) noexcept
{
SetRenderSettings(BetterPolygons, scale);
}
void GLRenderer::SetRenderSettings(bool betterpolygons, int scale) noexcept
{
if (betterpolygons == BetterPolygons && scale == ScaleFactor)
return;
CurGLCompositor.SetScaleFactor(scale);
ScaleFactor = scale; ScaleFactor = scale;
BetterPolygons = settings.GL_BetterPolygons; BetterPolygons = betterpolygons;
ScreenW = 256 * scale; ScreenW = 256 * scale;
ScreenH = 192 * scale; ScreenH = 192 * scale;
@ -1302,6 +1325,11 @@ void GLRenderer::RenderFrame()
FrontBuffer = FrontBuffer ? 0 : 1; FrontBuffer = FrontBuffer ? 0 : 1;
} }
void GLRenderer::Stop()
{
CurGLCompositor.Stop(GPU);
}
void GLRenderer::PrepareCaptureFrame() void GLRenderer::PrepareCaptureFrame()
{ {
// TODO: make sure this picks the right buffer when doing antialiasing // TODO: make sure this picks the right buffer when doing antialiasing
@ -1317,6 +1345,11 @@ void GLRenderer::PrepareCaptureFrame()
glReadPixels(0, 0, 256, 192, GL_BGRA, GL_UNSIGNED_BYTE, NULL); glReadPixels(0, 0, 256, 192, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
} }
void GLRenderer::Blit()
{
CurGLCompositor.RenderFrame(GPU, *this);
}
u32* GLRenderer::GetLine(int line) u32* GLRenderer::GetLine(int line)
{ {
int stride = 256; int stride = 256;

View File

@ -20,7 +20,7 @@
#ifdef OGLRENDERER_ENABLED #ifdef OGLRENDERER_ENABLED
#include "GPU3D.h" #include "GPU3D.h"
#include "GPU_OpenGL.h"
#include "OpenGLSupport.h" #include "OpenGLSupport.h"
namespace melonDS namespace melonDS
@ -30,22 +30,31 @@ class GPU;
class GLRenderer : public Renderer3D class GLRenderer : public Renderer3D
{ {
public: public:
virtual ~GLRenderer() override; ~GLRenderer() override;
virtual void Reset() override; void Reset() override;
virtual void SetRenderSettings(const RenderSettings& settings) noexcept override; void SetRenderSettings(bool betterpolygons, int scale) noexcept;
void SetBetterPolygons(bool betterpolygons) noexcept;
void SetScaleFactor(int scale) noexcept;
[[nodiscard]] bool GetBetterPolygons() const noexcept { return BetterPolygons; }
[[nodiscard]] int GetScaleFactor() const noexcept { return ScaleFactor; }
virtual void VCount144() override {}; void VCount144() override {};
virtual void RenderFrame() override; void RenderFrame() override;
virtual u32* GetLine(int line) override; void Stop() override;
u32* GetLine(int line) override;
void SetupAccelFrame(); void SetupAccelFrame();
void PrepareCaptureFrame(); void PrepareCaptureFrame() override;
void Blit() override;
[[nodiscard]] const GLCompositor& GetCompositor() const noexcept { return CurGLCompositor; }
GLCompositor& GetCompositor() noexcept { return CurGLCompositor; }
static std::unique_ptr<GLRenderer> New(melonDS::GPU& gpu) noexcept; static std::unique_ptr<GLRenderer> New(melonDS::GPU& gpu) noexcept;
private: private:
// Used by New() // Used by New()
GLRenderer(melonDS::GPU& gpu) noexcept; GLRenderer(GLCompositor&& compositor, GPU& gpu) noexcept;
// GL version requirements // GL version requirements
// * texelFetch: 3.0 (GLSL 1.30) (3.2/1.50 for MS) // * texelFetch: 3.0 (GLSL 1.30) (3.2/1.50 for MS)
@ -66,6 +75,7 @@ private:
}; };
melonDS::GPU& GPU; melonDS::GPU& GPU;
GLCompositor CurGLCompositor;
RendererPolygon PolygonList[2048] {}; RendererPolygon PolygonList[2048] {};
bool BuildRenderShader(u32 flags, const char* vs, const char* fs); bool BuildRenderShader(u32 flags, const char* vs, const char* fs);

View File

@ -71,14 +71,13 @@ void SoftRenderer::SetupRenderThread()
} }
SoftRenderer::SoftRenderer(melonDS::GPU& gpu) noexcept SoftRenderer::SoftRenderer(melonDS::GPU& gpu, bool threaded) noexcept
: Renderer3D(false), GPU(gpu) : Renderer3D(false), GPU(gpu), Threaded(threaded)
{ {
Sema_RenderStart = Platform::Semaphore_Create(); Sema_RenderStart = Platform::Semaphore_Create();
Sema_RenderDone = Platform::Semaphore_Create(); Sema_RenderDone = Platform::Semaphore_Create();
Sema_ScanlineCount = Platform::Semaphore_Create(); Sema_ScanlineCount = Platform::Semaphore_Create();
Threaded = false;
RenderThreadRunning = false; RenderThreadRunning = false;
RenderThreadRendering = false; RenderThreadRendering = false;
RenderThread = nullptr; RenderThread = nullptr;
@ -104,10 +103,13 @@ void SoftRenderer::Reset()
SetupRenderThread(); SetupRenderThread();
} }
void SoftRenderer::SetRenderSettings(const RenderSettings& settings) noexcept void SoftRenderer::SetThreaded(bool threaded) noexcept
{ {
Threaded = settings.Soft_Threaded; if (Threaded != threaded)
{
Threaded = threaded;
SetupRenderThread(); SetupRenderThread();
}
} }
void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha) void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha)

View File

@ -29,16 +29,17 @@ namespace melonDS
class SoftRenderer : public Renderer3D class SoftRenderer : public Renderer3D
{ {
public: public:
SoftRenderer(melonDS::GPU& gpu) noexcept; SoftRenderer(melonDS::GPU& gpu, bool threaded = false) noexcept;
virtual ~SoftRenderer() override; ~SoftRenderer() override;
virtual void Reset() override; void Reset() override;
virtual void SetRenderSettings(const RenderSettings& settings) noexcept override; void SetThreaded(bool threaded) noexcept;
[[nodiscard]] bool IsThreaded() const noexcept { return Threaded; }
virtual void VCount144() override; void VCount144() override;
virtual void RenderFrame() override; void RenderFrame() override;
virtual void RestartFrame() override; void RestartFrame() override;
virtual u32* GetLine(int line) override; u32* GetLine(int line) override;
void SetupRenderThread(); void SetupRenderThread();
void StopRenderThread(); void StopRenderThread();
@ -65,13 +66,13 @@ private:
class Interpolator class Interpolator
{ {
public: public:
Interpolator() {} constexpr Interpolator() {}
Interpolator(s32 x0, s32 x1, s32 w0, s32 w1) constexpr Interpolator(s32 x0, s32 x1, s32 w0, s32 w1)
{ {
Setup(x0, x1, w0, w1); Setup(x0, x1, w0, w1);
} }
void Setup(s32 x0, s32 x1, s32 w0, s32 w1) constexpr void Setup(s32 x0, s32 x1, s32 w0, s32 w1)
{ {
this->x0 = x0; this->x0 = x0;
this->x1 = x1; this->x1 = x1;
@ -123,7 +124,7 @@ private:
} }
} }
void SetX(s32 x) constexpr void SetX(s32 x)
{ {
x -= x0; x -= x0;
this->x = x; this->x = x;
@ -139,7 +140,7 @@ private:
} }
} }
s32 Interpolate(s32 y0, s32 y1) constexpr s32 Interpolate(s32 y0, s32 y1) const
{ {
if (xdiff == 0 || y0 == y1) return y0; if (xdiff == 0 || y0 == y1) return y0;
@ -161,7 +162,7 @@ private:
} }
} }
s32 InterpolateZ(s32 z0, s32 z1, bool wbuffer) constexpr s32 InterpolateZ(s32 z0, s32 z1, bool wbuffer) const
{ {
if (xdiff == 0 || z0 == z1) return z0; if (xdiff == 0 || z0 == z1) return z0;
@ -228,9 +229,9 @@ private:
class Slope class Slope
{ {
public: public:
Slope() {} constexpr Slope() {}
s32 SetupDummy(s32 x0) constexpr s32 SetupDummy(s32 x0)
{ {
dx = 0; dx = 0;
@ -249,7 +250,7 @@ private:
return x0; return x0;
} }
s32 Setup(s32 x0, s32 x1, s32 y0, s32 y1, s32 w0, s32 w1, s32 y) constexpr s32 Setup(s32 x0, s32 x1, s32 y0, s32 y1, s32 w0, s32 w1, s32 y)
{ {
this->x0 = x0; this->x0 = x0;
this->y = y; this->y = y;
@ -293,7 +294,7 @@ private:
XMajor = (Increment > 0x40000); XMajor = (Increment > 0x40000);
if (side) if constexpr (side)
{ {
// right // right
@ -324,7 +325,7 @@ private:
return x; return x;
} }
s32 Step() constexpr s32 Step()
{ {
dx += Increment; dx += Increment;
y++; y++;
@ -334,7 +335,7 @@ private:
return x; return x;
} }
s32 XVal() constexpr s32 XVal() const
{ {
s32 ret; s32 ret;
if (Negative) ret = x0 - (dx >> 18); if (Negative) ret = x0 - (dx >> 18);
@ -346,7 +347,7 @@ private:
} }
template<bool swapped> template<bool swapped>
void EdgeParams_XMajor(s32* length, s32* coverage) constexpr void EdgeParams_XMajor(s32* length, s32* coverage) const
{ {
// only do length calc for right side when swapped as it's // only do length calc for right side when swapped as it's
// only needed for aa calcs, as actual line spans are broken // only needed for aa calcs, as actual line spans are broken
@ -372,7 +373,7 @@ private:
} }
template<bool swapped> template<bool swapped>
void EdgeParams_YMajor(s32* length, s32* coverage) constexpr void EdgeParams_YMajor(s32* length, s32* coverage) const
{ {
*length = 1; *length = 1;
@ -404,7 +405,7 @@ private:
} }
template<bool swapped> template<bool swapped>
void EdgeParams(s32* length, s32* coverage) constexpr void EdgeParams(s32* length, s32* coverage) const
{ {
if (XMajor) if (XMajor)
return EdgeParams_XMajor<swapped>(length, coverage); return EdgeParams_XMajor<swapped>(length, coverage);

View File

@ -33,13 +33,13 @@ namespace melonDS
using namespace OpenGL; using namespace OpenGL;
std::unique_ptr<GLCompositor> GLCompositor::New(melonDS::GPU& gpu) noexcept std::optional<GLCompositor> GLCompositor::New() noexcept
{ {
assert(glBindAttribLocation != nullptr); assert(glBindAttribLocation != nullptr);
std::array<GLuint, 3> CompShader {}; std::array<GLuint, 3> CompShader {};
if (!OpenGL::BuildShaderProgram(kCompositorVS, kCompositorFS_Nearest, &CompShader[0], "CompositorShader")) if (!OpenGL::BuildShaderProgram(kCompositorVS, kCompositorFS_Nearest, &CompShader[0], "CompositorShader"))
return nullptr; return std::nullopt;
glBindAttribLocation(CompShader[2], 0, "vPosition"); glBindAttribLocation(CompShader[2], 0, "vPosition");
glBindAttribLocation(CompShader[2], 1, "vTexcoord"); glBindAttribLocation(CompShader[2], 1, "vTexcoord");
@ -48,12 +48,12 @@ std::unique_ptr<GLCompositor> GLCompositor::New(melonDS::GPU& gpu) noexcept
if (!OpenGL::LinkShaderProgram(CompShader.data())) if (!OpenGL::LinkShaderProgram(CompShader.data()))
// OpenGL::LinkShaderProgram already deletes the shader program object // OpenGL::LinkShaderProgram already deletes the shader program object
// if linking the shaders together failed. // if linking the shaders together failed.
return nullptr; return std::nullopt;
return std::unique_ptr<GLCompositor>(new GLCompositor(CompShader, gpu)); return { GLCompositor(CompShader) };
} }
GLCompositor::GLCompositor(std::array<GLuint, 3> compShader, melonDS::GPU& gpu) noexcept : CompShader(compShader), GPU(gpu) GLCompositor::GLCompositor(std::array<GLuint, 3> compShader) noexcept : CompShader(compShader)
{ {
CompScaleLoc = glGetUniformLocation(CompShader[2], "u3DScale"); CompScaleLoc = glGetUniformLocation(CompShader[2], "u3DScale");
Comp3DXPosLoc = glGetUniformLocation(CompShader[2], "u3DXPos"); Comp3DXPosLoc = glGetUniformLocation(CompShader[2], "u3DXPos");
@ -92,7 +92,7 @@ GLCompositor::GLCompositor(std::array<GLuint, 3> compShader, melonDS::GPU& gpu)
glGenBuffers(1, &CompVertexBufferID); glGenBuffers(1, &CompVertexBufferID);
glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID); glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID);
glBufferData(GL_ARRAY_BUFFER, sizeof(CompVertices), CompVertices, GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, sizeof(CompVertices), &CompVertices[0], GL_STATIC_DRAW);
glGenVertexArrays(1, &CompVertexArrayID); glGenVertexArrays(1, &CompVertexArrayID);
glBindVertexArray(CompVertexArrayID); glBindVertexArray(CompVertexArrayID);
@ -101,7 +101,7 @@ GLCompositor::GLCompositor(std::array<GLuint, 3> compShader, melonDS::GPU& gpu)
glEnableVertexAttribArray(1); // texcoord glEnableVertexAttribArray(1); // texcoord
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(CompVertex), (void*)(offsetof(CompVertex, Texcoord))); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(CompVertex), (void*)(offsetof(CompVertex, Texcoord)));
glGenFramebuffers(2, CompScreenOutputFB); glGenFramebuffers(CompScreenOutputFB.size(), &CompScreenOutputFB[0]);
glGenTextures(1, &CompScreenInputTex); glGenTextures(1, &CompScreenInputTex);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
@ -112,10 +112,10 @@ GLCompositor::GLCompositor(std::array<GLuint, 3> compShader, melonDS::GPU& gpu)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, 256*3 + 1, 192*2, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, NULL); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, 256*3 + 1, 192*2, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, NULL);
glGenTextures(2, CompScreenOutputTex); glGenTextures(CompScreenOutputTex.size(), &CompScreenOutputTex[0]);
for (int i = 0; i < 2; i++) for (GLuint i : CompScreenOutputTex)
{ {
glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex[i]); glBindTexture(GL_TEXTURE_2D, i);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
@ -129,9 +129,9 @@ GLCompositor::~GLCompositor()
{ {
assert(glDeleteFramebuffers != nullptr); assert(glDeleteFramebuffers != nullptr);
glDeleteFramebuffers(2, CompScreenOutputFB); glDeleteFramebuffers(CompScreenOutputFB.size(), &CompScreenOutputFB[0]);
glDeleteTextures(1, &CompScreenInputTex); glDeleteTextures(1, &CompScreenInputTex);
glDeleteTextures(2, CompScreenOutputTex); glDeleteTextures(CompScreenOutputTex.size(), &CompScreenOutputTex[0]);
glDeleteVertexArrays(1, &CompVertexArrayID); glDeleteVertexArrays(1, &CompVertexArrayID);
glDeleteBuffers(1, &CompVertexBufferID); glDeleteBuffers(1, &CompVertexBufferID);
@ -139,14 +139,75 @@ GLCompositor::~GLCompositor()
OpenGL::DeleteShaderProgram(CompShader.data()); OpenGL::DeleteShaderProgram(CompShader.data());
} }
void GLCompositor::Reset()
GLCompositor::GLCompositor(GLCompositor&& other) noexcept :
Scale(other.Scale),
ScreenH(other.ScreenH),
ScreenW(other.ScreenW),
CompScaleLoc(other.CompScaleLoc),
Comp3DXPosLoc(other.Comp3DXPosLoc),
CompVertices(other.CompVertices),
CompShader(other.CompShader),
CompVertexBufferID(other.CompVertexBufferID),
CompVertexArrayID(other.CompVertexArrayID),
CompScreenInputTex(other.CompScreenInputTex),
CompScreenOutputTex(other.CompScreenOutputTex),
CompScreenOutputFB(other.CompScreenOutputFB)
{ {
other.CompScreenOutputFB = {};
other.CompScreenInputTex = {};
other.CompScreenOutputTex = {};
other.CompVertexArrayID = {};
other.CompVertexBufferID = {};
other.CompShader = {};
}
GLCompositor& GLCompositor::operator=(GLCompositor&& other) noexcept
{
if (this != &other)
{
Scale = other.Scale;
ScreenH = other.ScreenH;
ScreenW = other.ScreenW;
CompScaleLoc = other.CompScaleLoc;
Comp3DXPosLoc = other.Comp3DXPosLoc;
CompVertices = other.CompVertices;
// Clean up these resources before overwriting them
OpenGL::DeleteShaderProgram(CompShader.data());
CompShader = other.CompShader;
glDeleteBuffers(1, &CompVertexBufferID);
CompVertexBufferID = other.CompVertexBufferID;
glDeleteVertexArrays(1, &CompVertexArrayID);
CompVertexArrayID = other.CompVertexArrayID;
glDeleteTextures(1, &CompScreenInputTex);
CompScreenInputTex = other.CompScreenInputTex;
glDeleteTextures(CompScreenOutputTex.size(), &CompScreenOutputTex[0]);
CompScreenOutputTex = other.CompScreenOutputTex;
glDeleteFramebuffers(CompScreenOutputFB.size(), &CompScreenOutputFB[0]);
CompScreenOutputFB = other.CompScreenOutputFB;
other.CompScreenOutputFB = {};
other.CompScreenInputTex = {};
other.CompScreenOutputTex = {};
other.CompVertexArrayID = {};
other.CompVertexBufferID = {};
other.CompShader = {};
}
return *this;
} }
void GLCompositor::SetRenderSettings(const RenderSettings& settings) noexcept void GLCompositor::SetScaleFactor(int scale) noexcept
{ {
int scale = settings.GL_ScaleFactor; if (scale == Scale)
return;
Scale = scale; Scale = scale;
ScreenW = 256 * scale; ScreenW = 256 * scale;
@ -170,13 +231,12 @@ void GLCompositor::SetRenderSettings(const RenderSettings& settings) noexcept
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }
void GLCompositor::Stop() void GLCompositor::Stop(const GPU& gpu) noexcept
{ {
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ {
int frontbuf = GPU.FrontBuffer;
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB[frontbuf]); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB[gpu.FrontBuffer]);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
} }
@ -184,9 +244,9 @@ void GLCompositor::Stop()
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }
void GLCompositor::RenderFrame() void GLCompositor::RenderFrame(const GPU& gpu, GLRenderer& renderer) noexcept
{ {
int frontbuf = GPU.FrontBuffer; int frontbuf = gpu.FrontBuffer;
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB[frontbuf]); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB[frontbuf]);
@ -204,21 +264,21 @@ void GLCompositor::RenderFrame()
glUniform1ui(CompScaleLoc, Scale); glUniform1ui(CompScaleLoc, Scale);
// TODO: support setting this midframe, if ever needed // TODO: support setting this midframe, if ever needed
glUniform1i(Comp3DXPosLoc, ((int)GPU.GPU3D.GetRenderXPos() << 23) >> 23); glUniform1i(Comp3DXPosLoc, ((int)gpu.GPU3D.GetRenderXPos() << 23) >> 23);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, CompScreenInputTex); glBindTexture(GL_TEXTURE_2D, CompScreenInputTex);
if (GPU.Framebuffer[frontbuf][0] && GPU.Framebuffer[frontbuf][1]) if (gpu.Framebuffer[frontbuf][0] && gpu.Framebuffer[frontbuf][1])
{ {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256*3 + 1, 192, GL_RGBA_INTEGER, glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256*3 + 1, 192, GL_RGBA_INTEGER,
GL_UNSIGNED_BYTE, GPU.Framebuffer[frontbuf][0]); GL_UNSIGNED_BYTE, gpu.Framebuffer[frontbuf][0].get());
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256*3 + 1, 192, GL_RGBA_INTEGER, glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256*3 + 1, 192, GL_RGBA_INTEGER,
GL_UNSIGNED_BYTE, GPU.Framebuffer[frontbuf][1]); GL_UNSIGNED_BYTE, gpu.Framebuffer[frontbuf][1].get());
} }
glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1);
reinterpret_cast<GLRenderer*>(GPU.GPU3D.GetCurrentRenderer())->SetupAccelFrame(); renderer.SetupAccelFrame();
glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID); glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID);
glBindVertexArray(CompVertexArrayID); glBindVertexArray(CompVertexArrayID);

View File

@ -21,51 +21,51 @@
#include "OpenGLSupport.h" #include "OpenGLSupport.h"
#include <array> #include <array>
#include <memory> #include <optional>
namespace melonDS namespace melonDS
{ {
class GPU; class GPU;
struct RenderSettings; struct RenderSettings;
class GLRenderer;
class GLCompositor class GLCompositor
{ {
public: public:
static std::unique_ptr<GLCompositor> New(melonDS::GPU& gpu) noexcept; static std::optional<GLCompositor> New() noexcept;
GLCompositor(const GLCompositor&) = delete; GLCompositor(const GLCompositor&) = delete;
GLCompositor& operator=(const GLCompositor&) = delete; GLCompositor& operator=(const GLCompositor&) = delete;
GLCompositor(GLCompositor&&) noexcept;
GLCompositor& operator=(GLCompositor&&) noexcept;
~GLCompositor(); ~GLCompositor();
void Reset(); void SetScaleFactor(int scale) noexcept;
[[nodiscard]] int GetScaleFactor() const noexcept { return Scale; }
void SetRenderSettings(const RenderSettings& settings) noexcept; void Stop(const GPU& gpu) noexcept;
void RenderFrame(const GPU& gpu, GLRenderer& renderer) noexcept;
void Stop();
void RenderFrame();
void BindOutputTexture(int buf); void BindOutputTexture(int buf);
private: private:
GLCompositor(std::array<GLuint, 3> CompShader, melonDS::GPU& gpu) noexcept; GLCompositor(std::array<GLuint, 3> CompShader) noexcept;
melonDS::GPU& GPU; int Scale = 0;
int Scale; int ScreenH = 0, ScreenW = 0;
int ScreenH, ScreenW;
std::array<GLuint, 3> CompShader; std::array<GLuint, 3> CompShader {};
GLuint CompScaleLoc; GLuint CompScaleLoc = 0;
GLuint Comp3DXPosLoc; GLuint Comp3DXPosLoc = 0;
GLuint CompVertexBufferID; GLuint CompVertexBufferID = 0;
GLuint CompVertexArrayID; GLuint CompVertexArrayID = 0;
struct CompVertex struct CompVertex
{ {
float Position[2]; std::array<float, 2> Position {};
float Texcoord[2]; std::array<float, 2> Texcoord {};
}; };
CompVertex CompVertices[2 * 3*2]; std::array<CompVertex, 2*3*2> CompVertices {};
GLuint CompScreenInputTex; GLuint CompScreenInputTex = 0;
GLuint CompScreenOutputTex[2]; std::array<GLuint, 2> CompScreenOutputTex {};
GLuint CompScreenOutputFB[2]; std::array<GLuint, 2> CompScreenOutputFB {};
}; };
} }

View File

@ -93,6 +93,8 @@
#include "RTC.h" #include "RTC.h"
#include "DSi.h" #include "DSi.h"
#include "DSi_I2C.h" #include "DSi_I2C.h"
#include "GPU3D_Soft.h"
#include "GPU3D_OpenGL.h"
#include "Savestate.h" #include "Savestate.h"
@ -163,7 +165,6 @@ EmuThread* emuThread;
int autoScreenSizing = 0; int autoScreenSizing = 0;
int videoRenderer; int videoRenderer;
RenderSettings videoSettings;
bool videoSettingsDirty; bool videoSettingsDirty;
CameraManager* camManager[2]; CameraManager* camManager[2];
@ -350,9 +351,6 @@ void EmuThread::run()
autoScreenSizing = 0; autoScreenSizing = 0;
videoSettingsDirty = false; videoSettingsDirty = false;
videoSettings.Soft_Threaded = Config::Threaded3D != 0;
videoSettings.GL_ScaleFactor = Config::GL_ScaleFactor;
videoSettings.GL_BetterPolygons = Config::GL_BetterPolygons;
if (mainWindow->hasOGL) if (mainWindow->hasOGL)
{ {
@ -364,8 +362,16 @@ void EmuThread::run()
videoRenderer = 0; videoRenderer = 0;
} }
NDS->GPU.InitRenderer(videoRenderer); if (videoRenderer == 0)
NDS->GPU.SetRenderSettings(videoRenderer, videoSettings); { // If we're using the software renderer...
NDS->GPU.SetRenderer3D(std::make_unique<SoftRenderer>(NDS->GPU, Config::Threaded3D != 0));
}
else
{
auto glrenderer = melonDS::GLRenderer::New(NDS->GPU);
glrenderer->SetRenderSettings(Config::GL_BetterPolygons, Config::GL_ScaleFactor);
NDS->GPU.SetRenderer3D(std::move(glrenderer));
}
NDS->SPU.SetInterpolation(Config::AudioInterp); NDS->SPU.SetInterpolation(Config::AudioInterp);
@ -491,11 +497,16 @@ void EmuThread::run()
videoSettingsDirty = false; videoSettingsDirty = false;
videoSettings.Soft_Threaded = Config::Threaded3D != 0; if (videoRenderer == 0)
videoSettings.GL_ScaleFactor = Config::GL_ScaleFactor; { // If we're using the software renderer...
videoSettings.GL_BetterPolygons = Config::GL_BetterPolygons; NDS->GPU.SetRenderer3D(std::make_unique<SoftRenderer>(NDS->GPU, Config::Threaded3D != 0));
}
NDS->GPU.SetRenderSettings(videoRenderer, videoSettings); else
{
auto glrenderer = melonDS::GLRenderer::New(NDS->GPU);
glrenderer->SetRenderSettings(Config::GL_BetterPolygons, Config::GL_ScaleFactor);
NDS->GPU.SetRenderer3D(std::move(glrenderer));
}
} }
// process input and hotkeys // process input and hotkeys
@ -804,10 +815,10 @@ void EmuThread::drawScreenGL()
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
#ifdef OGLRENDERER_ENABLED #ifdef OGLRENDERER_ENABLED
if (NDS->GPU.Renderer != 0) if (NDS->GPU.GetRenderer3D().Accelerated)
{ {
// hardware-accelerated render // hardware-accelerated render
NDS->GPU.CurGLCompositor->BindOutputTexture(frontbuf); static_cast<GLRenderer&>(NDS->GPU.GetRenderer3D()).GetCompositor().BindOutputTexture(frontbuf);
} }
else else
#endif #endif
@ -818,9 +829,9 @@ void EmuThread::drawScreenGL()
if (NDS->GPU.Framebuffer[frontbuf][0] && NDS->GPU.Framebuffer[frontbuf][1]) if (NDS->GPU.Framebuffer[frontbuf][0] && NDS->GPU.Framebuffer[frontbuf][1])
{ {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA, glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA,
GL_UNSIGNED_BYTE, NDS->GPU.Framebuffer[frontbuf][0]); GL_UNSIGNED_BYTE, NDS->GPU.Framebuffer[frontbuf][0].get());
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192+2, 256, 192, GL_RGBA, glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192+2, 256, 192, GL_RGBA,
GL_UNSIGNED_BYTE, NDS->GPU.Framebuffer[frontbuf][1]); GL_UNSIGNED_BYTE, NDS->GPU.Framebuffer[frontbuf][1].get());
} }
} }
@ -1122,8 +1133,8 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event)
return; return;
} }
memcpy(screen[0].scanLine(0), emuThread->NDS->GPU.Framebuffer[frontbuf][0], 256 * 192 * 4); memcpy(screen[0].scanLine(0), emuThread->NDS->GPU.Framebuffer[frontbuf][0].get(), 256 * 192 * 4);
memcpy(screen[1].scanLine(0), emuThread->NDS->GPU.Framebuffer[frontbuf][1], 256 * 192 * 4); memcpy(screen[1].scanLine(0), emuThread->NDS->GPU.Framebuffer[frontbuf][1].get(), 256 * 192 * 4);
emuThread->FrontBufferLock.unlock(); emuThread->FrontBufferLock.unlock();
QRect screenrc(0, 0, 256, 192); QRect screenrc(0, 0, 256, 192);

View File

@ -20,6 +20,7 @@
#define TYPES_H #define TYPES_H
#include <stdint.h> #include <stdint.h>
#include <array>
namespace melonDS namespace melonDS
{ {
@ -32,5 +33,7 @@ typedef int16_t s16;
typedef int32_t s32; typedef int32_t s32;
typedef int64_t s64; typedef int64_t s64;
template<class T, std::size_t A, std::size_t B>
using array2d = std::array<std::array<T, B>, A>;
} }
#endif // TYPES_H #endif // TYPES_H