diff options
Diffstat (limited to 'src/GPU3D_Soft.cpp')
-rw-r--r-- | src/GPU3D_Soft.cpp | 53 |
1 files changed, 47 insertions, 6 deletions
diff --git a/src/GPU3D_Soft.cpp b/src/GPU3D_Soft.cpp index c96464a..74027d5 100644 --- a/src/GPU3D_Soft.cpp +++ b/src/GPU3D_Soft.cpp @@ -34,8 +34,11 @@ void SoftRenderer::StopRenderThread() { if (RenderThreadRunning.load(std::memory_order_relaxed)) { + // Tell the render thread to stop drawing new frames, and finish up the current one. RenderThreadRunning = false; + Platform::Semaphore_Post(Sema_RenderStart); + Platform::Thread_Wait(RenderThread); Platform::Thread_Free(RenderThread); RenderThread = nullptr; @@ -47,24 +50,36 @@ void SoftRenderer::SetupRenderThread(GPU& gpu) if (Threaded) { if (!RenderThreadRunning.load(std::memory_order_relaxed)) - { - RenderThreadRunning = true; + { // If the render thread isn't already running... + RenderThreadRunning = true; // "Time for work, render thread!" RenderThread = Platform::Thread_Create([this, &gpu]() { RenderThreadFunc(gpu); }); } - // otherwise more than one frame can be queued up at once + // "Be on standby, but don't start rendering until I tell you to!" Platform::Semaphore_Reset(Sema_RenderStart); + // "Oh, sorry, were you already in the middle of a frame from the last iteration?" if (RenderThreadRendering) + // "Tell me when you're done, I'll wait here." Platform::Semaphore_Wait(Sema_RenderDone); + // "All good? Okay, let me give you your training." + // "(Maybe you're still the same thread, but I have to tell you this stuff anyway.)" + + // "This is the signal you'll send when you're done with a frame." + // "I'll listen for it when I need to show something to the frontend." Platform::Semaphore_Reset(Sema_RenderDone); + + // "This is the signal I'll send when I want you to start rendering." + // "Don't do anything until you get the message." Platform::Semaphore_Reset(Sema_RenderStart); - Platform::Semaphore_Reset(Sema_ScanlineCount); - Platform::Semaphore_Post(Sema_RenderStart); + // "This is the signal you'll send every time you finish drawing a line." + // "I might need some of your scanlines before you finish the whole buffer," + // "so let me know as soon as you're done with each one." + Platform::Semaphore_Reset(Sema_ScanlineCount); } else { @@ -72,6 +87,13 @@ void SoftRenderer::SetupRenderThread(GPU& gpu) } } +void SoftRenderer::EnableRenderThread() +{ + if (Threaded && Sema_RenderStart) + { + Platform::Semaphore_Post(Sema_RenderStart); + } +} SoftRenderer::SoftRenderer(bool threaded) noexcept : Renderer3D(false), Threaded(threaded) @@ -103,6 +125,7 @@ void SoftRenderer::Reset(GPU& gpu) PrevIsShadowMask = false; SetupRenderThread(gpu); + EnableRenderThread(); } void SoftRenderer::SetThreaded(bool threaded, GPU& gpu) noexcept @@ -111,6 +134,7 @@ void SoftRenderer::SetThreaded(bool threaded, GPU& gpu) noexcept { Threaded = threaded; SetupRenderThread(gpu); + EnableRenderThread(); } } @@ -1692,12 +1716,14 @@ void SoftRenderer::RenderPolygons(const GPU& gpu, bool threaded, Polygon** polyg ScanlineFinalPass(gpu.GPU3D, y-1); if (threaded) + // Notify the main thread that we're done with a scanline. Platform::Semaphore_Post(Sema_ScanlineCount); } ScanlineFinalPass(gpu.GPU3D, 191); if (threaded) + // If this renderer is threaded, notify the main thread that we're done with the frame. Platform::Semaphore_Post(Sema_ScanlineCount); } @@ -1719,6 +1745,7 @@ void SoftRenderer::RenderFrame(GPU& gpu) if (RenderThreadRunning.load(std::memory_order_relaxed)) { + // "Render thread, you're up! Get moving." Platform::Semaphore_Post(Sema_RenderStart); } else if (!FrameIdentical) @@ -1731,18 +1758,26 @@ void SoftRenderer::RenderFrame(GPU& gpu) void SoftRenderer::RestartFrame(GPU& gpu) { SetupRenderThread(gpu); + EnableRenderThread(); } void SoftRenderer::RenderThreadFunc(GPU& gpu) { for (;;) { + // Wait for a notice from the main thread to start rendering (or to stop entirely). Platform::Semaphore_Wait(Sema_RenderStart); if (!RenderThreadRunning) return; + // Protect the GPU state from the main thread. + // Some melonDS frontends (though not ours) + // will repeatedly save or load states; + // if they do so while the render thread is busy here, + // the ensuing race conditions may cause a crash + // (since some of the GPU state includes pointers). RenderThreadRendering = true; if (FrameIdentical) - { + { // If no rendering is needed, just say we're done. Platform::Semaphore_Post(Sema_ScanlineCount, 192); } else @@ -1751,7 +1786,10 @@ void SoftRenderer::RenderThreadFunc(GPU& gpu) RenderPolygons(gpu, true, &gpu.GPU3D.RenderPolygonRAM[0], gpu.GPU3D.RenderNumPolygons); } + // Tell the main thread that we're done rendering + // and that it's safe to access the GPU state again. Platform::Semaphore_Post(Sema_RenderDone); + RenderThreadRendering = false; } } @@ -1761,6 +1799,9 @@ u32* SoftRenderer::GetLine(int line) if (RenderThreadRunning.load(std::memory_order_relaxed)) { if (line < 192) + // We need a scanline, so let's wait for the render thread to finish it. + // (both threads process scanlines from top-to-bottom, + // so we don't need to wait for a specific row) Platform::Semaphore_Wait(Sema_ScanlineCount); } |