Skip to content

Commit

Permalink
Clean up the 3D renderer for enhanced flexibility (#1895)
Browse files Browse the repository at this point in the history
* 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`
  • Loading branch information
JesseTG authored Nov 29, 2023
1 parent e973236 commit 7caddf9
Show file tree
Hide file tree
Showing 15 changed files with 367 additions and 335 deletions.
150 changes: 28 additions & 122 deletions src/GPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@

#include "GPU2D_Soft.h"
#include "GPU3D_Soft.h"
#include "GPU3D_OpenGL.h"

namespace melonDS
{
Expand Down Expand Up @@ -64,33 +63,24 @@ enum
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_StartScanline, MemberEventFunc(GPU, StartScanline));
NDS.RegisterEventFunc(Event_LCD, LCD_FinishFrame, MemberEventFunc(GPU, FinishFrame));
NDS.RegisterEventFunc(Event_DisplayFIFO, 0, MemberEventFunc(GPU, DisplayFIFO));

GPU2D_Renderer = std::make_unique<GPU2D::SoftRenderer>(*this);

FrontBuffer = 0;
Framebuffer[0][0] = NULL; Framebuffer[0][1] = NULL;
Framebuffer[1][0] = NULL; Framebuffer[1][1] = NULL;
Renderer = 0;
}

GPU::~GPU() noexcept
{
// 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_StartScanline);
Expand Down Expand Up @@ -198,9 +188,7 @@ void GPU::Reset() noexcept
GPU3D.Reset();

int backbuf = FrontBuffer ? 0 : 1;
GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1], Framebuffer[backbuf][0]);

ResetRenderer();
GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1].get(), Framebuffer[backbuf][0].get());

ResetVRAMCache();

Expand All @@ -216,17 +204,12 @@ void GPU::Stop() noexcept
else
fbsize = 256 * 192;

memset(Framebuffer[0][0], 0, fbsize*4);
memset(Framebuffer[0][1], 0, fbsize*4);
memset(Framebuffer[1][0], 0, fbsize*4);
memset(Framebuffer[1][1], 0, fbsize*4);
memset(Framebuffer[0][0].get(), 0, fbsize*4);
memset(Framebuffer[0][1].get(), 0, fbsize*4);
memset(Framebuffer[1][0].get(), 0, fbsize*4);
memset(Framebuffer[1][1].get(), 0, fbsize*4);

#ifdef OGLRENDERER_ENABLED
// This needs a better way to know that we're
// using the OpenGL renderer specifically
if (GPU3D.IsRendererAccelerated())
CurGLCompositor->Stop();
#endif
GPU3D.Stop();
}

void GPU::DoSavestate(Savestate* file) noexcept
Expand Down Expand Up @@ -300,113 +283,38 @@ void GPU::AssignFramebuffers() noexcept
int backbuf = FrontBuffer ? 0 : 1;
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
{
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 == 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(GLRenderer::New(*this));
if (!GPU3D.GetCurrentRenderer())
{
// Fallback on software renderer
CurGLCompositor.reset();
renderer = 0;
GPU3D.SetCurrentRenderer(std::make_unique<SoftRenderer>(*this));
}
}
else
#endif
{
if (renderer == nullptr)
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);
}
GPU3D.SetCurrentRenderer(std::move(renderer));

int fbsize;
if (GPU3D.IsRendererAccelerated())
fbsize = (256*3 + 1) * 192;
else
fbsize = 256 * 192;

if (Framebuffer[0][0]) { delete[] Framebuffer[0][0]; Framebuffer[0][0] = nullptr; }
if (Framebuffer[1][0]) { delete[] Framebuffer[1][0]; Framebuffer[1][0] = nullptr; }
if (Framebuffer[0][1]) { delete[] Framebuffer[0][1]; Framebuffer[0][1] = nullptr; }
if (Framebuffer[1][1]) { delete[] Framebuffer[1][1]; Framebuffer[1][1] = nullptr; }

Framebuffer[0][0] = new u32[fbsize];
Framebuffer[1][0] = new u32[fbsize];
Framebuffer[0][1] = new u32[fbsize];
Framebuffer[1][1] = new u32[fbsize];
Framebuffer[0][0] = std::make_unique<u32[]>(fbsize);
Framebuffer[1][0] = std::make_unique<u32[]>(fbsize);
Framebuffer[0][1] = std::make_unique<u32[]>(fbsize);
Framebuffer[1][1] = std::make_unique<u32[]>(fbsize);

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);
memset(Framebuffer[0][0].get(), 0, fbsize*4);
memset(Framebuffer[1][0].get(), 0, fbsize*4);
memset(Framebuffer[0][1].get(), 0, fbsize*4);
memset(Framebuffer[1][1].get(), 0, fbsize*4);

AssignFramebuffers();

if (Renderer == 0)
{
GPU3D.GetCurrentRenderer()->SetRenderSettings(settings);
}
#ifdef OGLRENDERER_ENABLED
else
{
CurGLCompositor->SetRenderSettings(settings);
GPU3D.GetCurrentRenderer()->SetRenderSettings(settings);
}
#endif
}


Expand Down Expand Up @@ -1026,8 +934,8 @@ void GPU::BlankFrame() noexcept
else
fbsize = 256 * 192;

memset(Framebuffer[backbuf][0], 0, fbsize*4);
memset(Framebuffer[backbuf][1], 0, fbsize*4);
memset(Framebuffer[backbuf][0].get(), 0, fbsize*4);
memset(Framebuffer[backbuf][1].get(), 0, fbsize*4);

FrontBuffer = backbuf;
AssignFramebuffers();
Expand Down Expand Up @@ -1123,11 +1031,9 @@ void GPU::StartScanline(u32 line) noexcept
GPU2D_B.VBlank();
GPU3D.VBlank();

#ifdef OGLRENDERER_ENABLED
// Need a better way to identify the openGL renderer in particular
if (GPU3D.IsRendererAccelerated())
CurGLCompositor->RenderFrame();
#endif
GPU3D.Blit();
}
}

Expand Down
36 changes: 12 additions & 24 deletions src/GPU.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@
#include "GPU3D.h"
#include "NonStupidBitfield.h"

#ifdef OGLRENDERER_ENABLED
#include "GPU_OpenGL.h"
#endif

namespace melonDS
{
class GPU3D;
Expand Down Expand Up @@ -56,33 +52,30 @@ struct VRAMTrackingSet
NonStupidBitField<Size/VRAMDirtyGranularity> DeriveState(u32* currentMappings, GPU& gpu);
};

struct RenderSettings
{
bool Soft_Threaded;

int GL_ScaleFactor;
bool GL_BetterPolygons;
};

class GPU
{
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;
void Reset() noexcept;
void Stop() noexcept;

void DoSavestate(Savestate* file) noexcept;

[[deprecated("Set the renderer directly instead of using an integer code")]] void InitRenderer(int renderer) noexcept;
void DeInitRenderer() noexcept;
void ResetRenderer() noexcept;

void SetRenderSettings(int renderer, RenderSettings& settings) noexcept;
/// Sets the active renderer to the renderer given in the provided pointer.
/// The pointer is moved-from, so it will be \c nullptr after this method is called.
/// If the pointer is \c nullptr, the renderer is reset to the default renderer.
void SetRenderer3D(std::unique_ptr<Renderer3D>&& renderer) noexcept;
[[nodiscard]] const Renderer3D& GetRenderer3D() const noexcept { return GPU3D.GetCurrentRenderer(); }
[[nodiscard]] Renderer3D& GetRenderer3D() noexcept { return GPU3D.GetCurrentRenderer(); }

u8* GetUniqueBankPtr(u32 mask, u32 offset) 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_CD(u32 bank, u8 cnt) noexcept;
void MapVRAM_E(u32 bank, u8 cnt) noexcept;
Expand Down Expand Up @@ -578,7 +571,7 @@ class GPU
u8* VRAMPtr_BOBJ[0x8] {};

int FrontBuffer = 0;
u32* Framebuffer[2][2] {};
std::unique_ptr<u32[]> Framebuffer[2][2] {};

GPU2D::Unit GPU2D_A;
GPU2D::Unit GPU2D_B;
Expand Down Expand Up @@ -611,11 +604,6 @@ class GPU

u8 VRAMFlat_Texture[512*1024] {};
u8 VRAMFlat_TexPal[128*1024] {};

int Renderer = 0;
#ifdef OGLRENDERER_ENABLED
std::unique_ptr<GLCompositor> CurGLCompositor = nullptr;
#endif
private:
void ResetVRAMCache() noexcept;
void AssignFramebuffers() noexcept;
Expand Down
2 changes: 1 addition & 1 deletion src/GPU2D.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class Unit
// take a reference to the GPU so we can access its state
// and ensure that it's not null
Unit(u32 num, melonDS::GPU& gpu);

virtual ~Unit() = default;
Unit(const Unit&) = delete;
Unit& operator=(const Unit&) = delete;

Expand Down
Loading

0 comments on commit 7caddf9

Please sign in to comment.