diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index 8b3b6a88..e49c7963 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -12,89 +12,47 @@ static debug::Logger logger("chunks-render"); -ChunksRenderer::ChunksRenderer(Level* level, const ContentGfxCache* cache, const EngineSettings& settings) -: level(level), cache(cache), settings(settings) { - const int MAX_FULL_CUBES = 3000; - renderer = std::make_unique( - 9 * 6 * 6 * MAX_FULL_CUBES, level->content, cache, settings - ); +const uint RENDERER_CAPACITY = 9 * 6 * 6 * 3000; - const uint num_threads = std::thread::hardware_concurrency(); - for (uint i = 0; i < num_threads; i++) { - threads.emplace_back(&ChunksRenderer::threadLoop, this, i); - workersBlocked.emplace_back(); +class RendererWorker : public util::Worker, RendererResult> { + Level* level; + std::shared_ptr renderer; +public: + RendererWorker( + Level* level, + const ContentGfxCache* cache, + const EngineSettings& settings + ) : level(level) + { + renderer = std::make_shared( + RENDERER_CAPACITY, level->content, cache, settings + ); } - logger.info() << "created " << num_threads << " rendering threads"; + + RendererResult operator()(const std::shared_ptr& chunk) override { + renderer->build(chunk.get(), level->chunksStorage.get()); + return RendererResult {glm::ivec2(chunk->x, chunk->z), renderer}; + } +}; + +ChunksRenderer::ChunksRenderer( + Level* level, + const ContentGfxCache* cache, + const EngineSettings& settings +) : level(level), + threadPool( + [=](){return std::make_shared(level, cache, settings);}, + [=](RendererResult& mesh){ + meshes[mesh.key] = std::shared_ptr(mesh.renderer->createMesh()); + inwork.erase(mesh.key); + }) +{ + renderer = std::make_unique( + RENDERER_CAPACITY, level->content, cache, settings + ); } ChunksRenderer::~ChunksRenderer() { - { - std::unique_lock lock(jobsMutex); - working = false; - } - - resultsMutex.lock(); - while (!results.empty()) { - Result entry = results.front(); - results.pop(); - entry.locked = false; - entry.variable.notify_all(); - } - resultsMutex.unlock(); - - jobsMutexCondition.notify_all(); - for (auto& thread : threads) { - thread.join(); - } -} - -void ChunksRenderer::threadLoop(int index) { - const int MAX_FULL_CUBES = 3000; - BlocksRenderer renderer( - 9 * 6 * 6 * MAX_FULL_CUBES, level->content, cache, settings - ); - - std::condition_variable variable; - std::mutex mutex; - bool locked = false; - while (working) { - std::shared_ptr chunk; - { - std::unique_lock lock(jobsMutex); - jobsMutexCondition.wait(lock, [this] { - return !jobs.empty() || !working; - }); - if (!working) { - break; - } - chunk = jobs.front(); - jobs.pop(); - } - process(chunk, renderer); - { - resultsMutex.lock(); - results.push(Result {variable, index, locked, {renderer, glm::ivec2(chunk->x, chunk->z)}}); - locked = true; - resultsMutex.unlock(); - } - { - std::unique_lock lock(mutex); - variable.wait(lock, [&] { - return !working || !locked; - }); - } - } -} - -void ChunksRenderer::process(std::shared_ptr chunk, BlocksRenderer& renderer) { - renderer.build(chunk.get(), level->chunksStorage.get()); -} - -void ChunksRenderer::enqueueJob(std::shared_ptr job) { - jobsMutex.lock(); - jobs.push(job); - jobsMutex.unlock(); - jobsMutexCondition.notify_one(); } std::shared_ptr ChunksRenderer::render(std::shared_ptr chunk, bool important) { @@ -113,7 +71,7 @@ std::shared_ptr ChunksRenderer::render(std::shared_ptr chunk, bool } inwork[key] = true; - enqueueJob(chunk); + threadPool.enqueueJob(chunk); return nullptr; } @@ -144,15 +102,5 @@ std::shared_ptr ChunksRenderer::get(Chunk* chunk) { } void ChunksRenderer::update() { - resultsMutex.lock(); - while (!results.empty()) { - Result entry = results.front(); - mesh_entry mesh = entry.entry; - results.pop(); - meshes[mesh.key] = std::shared_ptr(mesh.renderer.createMesh()); - inwork.erase(mesh.key); - entry.locked = false; - entry.variable.notify_all(); - } - resultsMutex.unlock(); + threadPool.update(); } diff --git a/src/graphics/render/ChunksRenderer.h b/src/graphics/render/ChunksRenderer.h index 37247ff4..de5c5416 100644 --- a/src/graphics/render/ChunksRenderer.h +++ b/src/graphics/render/ChunksRenderer.h @@ -2,16 +2,14 @@ #define GRAPHICS_RENDER_CHUNKSRENDERER_H_ #include -#include -#include #include #include #include #include -#include #include "../../voxels/Block.h" #include "../../voxels/ChunksStorage.h" +#include "../../util/ThreadPool.h" #include "../../settings.h" class Mesh; @@ -20,44 +18,24 @@ class Level; class BlocksRenderer; class ContentGfxCache; -struct mesh_entry { - BlocksRenderer& renderer; +struct RendererResult { glm::ivec2 key; -}; - -struct Result { - std::condition_variable& variable; - int workerIndex; - bool& locked; - mesh_entry entry; + std::shared_ptr renderer; }; class ChunksRenderer { - std::unique_ptr renderer; Level* level; + std::unique_ptr renderer; std::unordered_map> meshes; std::unordered_map inwork; - std::vector threads; - std::queue results; - std::mutex resultsMutex; - - std::queue> jobs; - std::condition_variable jobsMutexCondition; - std::mutex jobsMutex; - - bool working = true; - const ContentGfxCache* cache; - const EngineSettings& settings; - std::vector> workersBlocked; - - void enqueueJob(std::shared_ptr chunk); - void threadLoop(int index); - void process(std::shared_ptr chunk, BlocksRenderer& renderer); + util::ThreadPool, RendererResult> threadPool; public: - ChunksRenderer(Level* level, - const ContentGfxCache* cache, - const EngineSettings& settings); + ChunksRenderer( + Level* level, + const ContentGfxCache* cache, + const EngineSettings& settings + ); virtual ~ChunksRenderer(); std::shared_ptr render(std::shared_ptr chunk, bool important); diff --git a/src/util/ThreadPool.h b/src/util/ThreadPool.h new file mode 100644 index 00000000..e6b20e39 --- /dev/null +++ b/src/util/ThreadPool.h @@ -0,0 +1,129 @@ +#ifndef UTIL_THREAD_POOL_H_ +#define UTIL_THREAD_POOL_H_ + +#include +#include +#include +#include + +#include "../delegates.h" + +namespace util { + +template +struct ThreadPoolResult { + std::condition_variable& variable; + int workerIndex; + bool& locked; + T entry; +}; + +template +class Worker { +public: + Worker() {} + virtual ~Worker() {} + virtual R operator()(const T&) = 0; +}; + +template +class ThreadPool { + std::queue jobs; + std::queue> results; + std::vector threads; + std::condition_variable jobsMutexCondition; + std::mutex jobsMutex; + bool working = true; + std::vector> workersBlocked; + consumer resultConsumer; + std::mutex resultsMutex; + + void threadLoop(int index, std::shared_ptr> worker) { + std::condition_variable variable; + std::mutex mutex; + bool locked = false; + while (working) { + T job; + { + std::unique_lock lock(jobsMutex); + jobsMutexCondition.wait(lock, [this] { + return !jobs.empty() || !working; + }); + if (!working) { + break; + } + job = jobs.front(); + jobs.pop(); + } + R result = (*worker)(job); + { + resultsMutex.lock(); + results.push(ThreadPoolResult {variable, index, locked, result}); + locked = true; + resultsMutex.unlock(); + } + { + std::unique_lock lock(mutex); + variable.wait(lock, [&] { + return !working || !locked; + }); + } + } + } +public: + ThreadPool( + supplier>> workersSupplier, + consumer resultConsumer + ) : resultConsumer(resultConsumer) { + const uint num_threads = std::thread::hardware_concurrency(); + for (uint i = 0; i < num_threads; i++) { + threads.emplace_back(&ThreadPool::threadLoop, this, i, workersSupplier()); + workersBlocked.emplace_back(); + } + } + ~ThreadPool(){ + { + std::lock_guard lock(jobsMutex); + working = false; + } + { + std::lock_guard lock(resultsMutex); + while (!results.empty()) { + ThreadPoolResult entry = results.front(); + results.pop(); + entry.locked = false; + entry.variable.notify_all(); + } + } + + jobsMutexCondition.notify_all(); + for (auto& thread : threads) { + thread.join(); + } + } + + void update() { + std::lock_guard lock(resultsMutex); + while (!results.empty()) { + ThreadPoolResult entry = results.front(); + results.pop(); + + resultConsumer(entry.entry); + + entry.locked = false; + entry.variable.notify_all(); + } + } + + void enqueueJob(T job) { + { + std::lock_guard lock(jobsMutex); + jobs.push(job); + } + jobsMutexCondition.notify_one(); + } +}; + +} // namespace util + +#endif // UTIL_THREAD_POOL_H_