Execute scene slices on worker threads

This commit is contained in:
Magnus Norddahl 2017-02-04 16:45:36 +01:00
parent d9e545a519
commit 8ad132b64f
3 changed files with 93 additions and 10 deletions

View file

@ -23,6 +23,7 @@
#pragma once
#include <memory>
#include <thread>
class DrawerCommandQueue;
typedef std::shared_ptr<DrawerCommandQueue> DrawerCommandQueuePtr;
@ -67,6 +68,8 @@ namespace swrenderer
std::unique_ptr<RenderClipSegment> ClipSegments;
DrawerCommandQueuePtr DrawQueue;
std::thread thread;
// VisibleSprite working buffers
short clipbot[MAXWIDTH];
short cliptop[MAXWIDTH];

View file

@ -63,6 +63,11 @@ namespace swrenderer
Threads.push_back(std::make_unique<RenderThread>(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<RenderThread>(this));
StopThreads();
StartThreads(numThreads);
}
// Setup threads:
std::unique_lock<std::mutex> 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<std::mutex> 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<RenderThread>(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<std::mutex> 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<std::mutex> 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<std::mutex> 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();

View file

@ -14,7 +14,11 @@
#pragma once
#include <stddef.h>
#include <vector>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#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<std::unique_ptr<RenderThread>> 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;
};
}