diff --git a/src/swrenderer/r_renderthread.h b/src/swrenderer/r_renderthread.h index 8a4e8c54b..a0945cfee 100644 --- a/src/swrenderer/r_renderthread.h +++ b/src/swrenderer/r_renderthread.h @@ -23,6 +23,7 @@ #pragma once #include +#include class DrawerCommandQueue; typedef std::shared_ptr DrawerCommandQueuePtr; @@ -67,6 +68,8 @@ namespace swrenderer std::unique_ptr ClipSegments; DrawerCommandQueuePtr DrawQueue; + std::thread thread; + // VisibleSprite working buffers short clipbot[MAXWIDTH]; short cliptop[MAXWIDTH]; diff --git a/src/swrenderer/scene/r_scene.cpp b/src/swrenderer/scene/r_scene.cpp index 16a73ec91..bc4b6c5ae 100644 --- a/src/swrenderer/scene/r_scene.cpp +++ b/src/swrenderer/scene/r_scene.cpp @@ -63,6 +63,11 @@ namespace swrenderer Threads.push_back(std::make_unique(this)); } + RenderScene::~RenderScene() + { + StopThreads(); + } + void RenderScene::SetClearColor(int color) { clearcolor = color; @@ -161,26 +166,38 @@ namespace swrenderer void RenderScene::RenderThreadSlices() { int numThreads = r_scene_multithreaded ? 8 : 1; - - while (Threads.size() > (size_t)numThreads) + if (numThreads != Threads.size()) { - Threads.pop_back(); - } - - while (Threads.size() < (size_t)numThreads) - { - Threads.push_back(std::make_unique(this)); + StopThreads(); + StartThreads(numThreads); } + // Setup threads: + std::unique_lock start_lock(start_mutex); for (int i = 0; i < numThreads; i++) { Threads[i]->X1 = viewwidth * i / numThreads; Threads[i]->X2 = viewwidth * (i + 1) / numThreads; } + run_id++; + start_lock.unlock(); - for (int i = 0; i < numThreads; i++) + // Notify threads to run + if (Threads.size() > 1) { - RenderThreadSlice(Threads[i].get()); + start_condition.notify_all(); + } + + // Do the main thread ourselves: + RenderThreadSlice(MainThread()); + + // Wait for everyone to finish: + if (Threads.size() > 1) + { + std::unique_lock end_lock(end_mutex); + finished_threads++; + end_condition.wait(end_lock, [&]() { return finished_threads == Threads.size(); }); + finished_threads = 0; } } @@ -246,6 +263,54 @@ namespace swrenderer } } + void RenderScene::StartThreads(size_t numThreads) + { + while (Threads.size() < (size_t)numThreads) + { + auto thread = std::make_unique(this); + auto renderthread = thread.get(); + int start_run_id = run_id; + thread->thread = std::thread([=]() + { + int last_run_id = start_run_id; + while (true) + { + // Wait until we are signalled to run: + std::unique_lock start_lock(start_mutex); + start_condition.wait(start_lock, [&]() { return run_id != last_run_id || shutdown_flag; }); + if (shutdown_flag) + break; + last_run_id = run_id; + start_lock.unlock(); + + RenderThreadSlice(renderthread); + + // Notify main thread that we finished: + std::unique_lock end_lock(end_mutex); + finished_threads++; + end_lock.unlock(); + end_condition.notify_all(); + } + }); + Threads.push_back(std::move(thread)); + } + } + + void RenderScene::StopThreads() + { + std::unique_lock lock(start_mutex); + shutdown_flag = true; + lock.unlock(); + start_condition.notify_all(); + while (Threads.size() > 1) + { + Threads.back()->thread.join(); + Threads.pop_back(); + } + lock.lock(); + shutdown_flag = false; + } + void RenderScene::RenderViewToCanvas(AActor *actor, DCanvas *canvas, int x, int y, int width, int height, bool dontmaplines) { auto viewport = RenderViewport::Instance(); diff --git a/src/swrenderer/scene/r_scene.h b/src/swrenderer/scene/r_scene.h index d3053884c..d3680d422 100644 --- a/src/swrenderer/scene/r_scene.h +++ b/src/swrenderer/scene/r_scene.h @@ -14,7 +14,11 @@ #pragma once #include +#include #include +#include +#include +#include #include "r_defs.h" #include "d_player.h" @@ -30,6 +34,7 @@ namespace swrenderer { public: RenderScene(); + ~RenderScene(); void Init(); void ScreenResized(); @@ -49,10 +54,20 @@ namespace swrenderer void RenderDrawQueues(); void RenderThreadSlices(); void RenderThreadSlice(RenderThread *thread); + + void StartThreads(size_t numThreads); + void StopThreads(); bool dontmaplines = false; int clearcolor = 0; std::vector> Threads; + std::mutex start_mutex; + std::condition_variable start_condition; + bool shutdown_flag = false; + int run_id = 0; + std::mutex end_mutex; + std::condition_variable end_condition; + size_t finished_threads = 0; }; }