From 47bc1c3f81d8f697ddf6a9b84f5cb0b762be9096 Mon Sep 17 00:00:00 2001 From: LaG1924 <12997935+LaG1924@users.noreply.github.com> Date: Sat, 17 Mar 2018 00:04:44 +0500 Subject: New lock-free RendererSectionData parsing --- src/RendererWorld.cpp | 259 +++++++++++++++++++++++++------------------------- src/RendererWorld.hpp | 19 ++-- 2 files changed, 144 insertions(+), 134 deletions(-) diff --git a/src/RendererWorld.cpp b/src/RendererWorld.cpp index 50d1fce..6349573 100644 --- a/src/RendererWorld.cpp +++ b/src/RendererWorld.cpp @@ -16,60 +16,16 @@ void RendererWorld::WorkerFunction(size_t workerId) { EventListener tasksListener; - tasksListener.RegisterHandler("RendererWorkerTask", [&](const Event& eventData) { - auto data = eventData.get>(); - if (std::get<0>(data) != workerId) - return; - Vector vec = std::get<1>(data); - auto forced = std::get<2>(data); - - sectionsMutex.lock(); - auto result = sections.find(vec); - if (result != sections.end()) { - if (result->second.GetHash() != gs->world.GetSection(result->first).GetHash() || forced) { - sectionsMutex.unlock(); - SectionsData sections; - sections.section = gs->world.GetSection(vec); - sections.west = gs->world.GetSection(vec + Vector(1, 0, 0)); - sections.east = gs->world.GetSection(vec + Vector(-1, 0, 0)); - sections.top = gs->world.GetSection(vec + Vector(0, 1, 0)); - sections.bottom = gs->world.GetSection(vec + Vector(0, -1, 0)); - sections.north = gs->world.GetSection(vec + Vector(0, 0, 1)); - sections.south = gs->world.GetSection(vec + Vector(0, 0, -1)); - auto data = std::make_unique(ParseSection(sections)); - data->forced = true; - renderDataMutex.lock(); - renderData.push(std::move(data)); - renderDataMutex.unlock(); - PUSH_EVENT("NewRenderDataAvailable", 0); - sectionsMutex.lock(); - } - else { - isParsingMutex.lock(); - isParsing[vec] = false; - isParsingMutex.unlock(); - } - } - else { - sectionsMutex.unlock(); - SectionsData sections; - sections.section = gs->world.GetSection(vec); - sections.west = gs->world.GetSection(vec + Vector(1, 0, 0)); - sections.east = gs->world.GetSection(vec + Vector(-1, 0, 0)); - sections.top = gs->world.GetSection(vec + Vector(0, 1, 0)); - sections.bottom = gs->world.GetSection(vec + Vector(0, -1, 0)); - sections.north = gs->world.GetSection(vec + Vector(0, 0, 1)); - sections.south = gs->world.GetSection(vec + Vector(0, 0, -1)); - auto data = std::make_unique(ParseSection(sections)); - data->forced = true; - renderDataMutex.lock(); - renderData.push(std::move(data)); - renderDataMutex.unlock(); - PUSH_EVENT("NewRenderDataAvailable", 0); - sectionsMutex.lock(); - } - sectionsMutex.unlock(); - }); + tasksListener.RegisterHandler("ParseSection", [&](const Event &eventData) { + auto data = eventData.get>(); + if (std::get<0>(data) != workerId) + return; + size_t id = std::get<1>(data); + bool forced = std::get<2>(data); + parsing[id].renderer = ParseSection(parsing[id].data); + parsing[id].renderer.forced = forced; + PUSH_EVENT("SectionParsed", id); + }); LoopExecutionTimeController timer(std::chrono::milliseconds(50)); while (isRunning) { @@ -79,6 +35,82 @@ void RendererWorld::WorkerFunction(size_t workerId) { } } +void RendererWorld::ParseQueueUpdate() { + while (!parseQueue.empty()) { + size_t id = 0; + for (; id < RendererWorld::parsingBufferSize && parsing[id].parsing; ++id) {} + if (id >= RendererWorld::parsingBufferSize) + break; + + Vector vec = parseQueue.front(); + parseQueue.pop(); + + bool forced = false; + + if (vec.y > 4000) { + forced = true; + vec.y -= 4500; + } + + parsing[id].data.section = gs->world.GetSection(vec); + parsing[id].data.north = gs->world.GetSection(vec + Vector(0, 0, 1)); + parsing[id].data.south = gs->world.GetSection(vec + Vector(0, 0, -1)); + parsing[id].data.west = gs->world.GetSection(vec + Vector(1, 0, 0)); + parsing[id].data.east = gs->world.GetSection(vec + Vector(-1, 0, 0)); + parsing[id].data.bottom = gs->world.GetSection(vec + Vector(0, -1, 0)); + parsing[id].data.top = gs->world.GetSection(vec + Vector(0, 1, 0)); + + parsing[id].parsing = true; + + PUSH_EVENT("ParseSection", std::make_tuple(currentWorker++, id, forced)); + if (currentWorker >= numOfWorkers) + currentWorker = 0; + } +} + +void RendererWorld::ParseQeueueRemoveUnnecessary() { + size_t size = parseQueue.size(); + static std::vector elements; + elements.clear(); + elements.reserve(size); + + for (size_t i = 0; i < size; i++) { + Vector vec = parseQueue.front(); + parseQueue.pop(); + + if (vec.y > 4000) { + parseQueue.push(vec); + continue; + } + + if (std::find(elements.begin(), elements.end(), vec) != elements.end()) + continue; + + const Section& section = gs->world.GetSection(vec); + + bool skip = false; + + for (int i = 0; i < RendererWorld::parsingBufferSize; i++) { + if (parsing[i].data.section.GetHash() == section.GetHash()) { + skip = true; + break; + } + } + if (skip) + continue; + + auto it = sections.find(vec); + if (it != sections.end() && section.GetHash() == it->second.GetHash()) { + continue; + } + + parseQueue.push(vec); + elements.push_back(vec); + } + + parseQueueNeedRemoveUnnecessary = false; +} + void RendererWorld::UpdateAllSections(VectorF playerPos) { Vector playerChunk(std::floor(gs->player->pos.x / 16), 0, std::floor(gs->player->pos.z / 16)); @@ -93,12 +125,10 @@ void RendererWorld::UpdateAllSections(VectorF playerPos) { std::vector toRemove; - sectionsMutex.lock(); for (auto& it : sections) { if (std::find(suitableChunks.begin(), suitableChunks.end(), it.first) == suitableChunks.end()) toRemove.push_back(it.first); } - sectionsMutex.unlock(); for (auto& it : toRemove) { PUSH_EVENT("DeleteSectionRender", it); @@ -113,7 +143,7 @@ void RendererWorld::UpdateAllSections(VectorF playerPos) { for (auto& it : suitableChunks) { PUSH_EVENT("ChunkChanged", it); - } + } } RendererWorld::RendererWorld(GameState* ptr) { @@ -128,42 +158,31 @@ RendererWorld::RendererWorld(GameState* ptr) { listener->RegisterHandler("DeleteSectionRender", [this](const Event& eventData) { auto vec = eventData.get(); - sectionsMutex.lock(); auto it = sections.find(vec); - if (it == sections.end()) { - sectionsMutex.unlock(); + if (it == sections.end()) return; - } sections.erase(it); - sectionsMutex.unlock(); }); - listener->RegisterHandler("NewRenderDataAvailable",[this](const Event&) { - renderDataMutex.lock(); - int i = 0; - while (!renderData.empty() && i++ < 20) { - auto data = std::move(renderData.front()); - renderData.pop(); - isParsingMutex.lock(); - if (isParsing[data->sectionPos] != true) - LOG(WARNING) << "Generated not parsed data"; - isParsing[data->sectionPos] = false; - isParsingMutex.unlock(); - - sectionsMutex.lock(); - if (sections.find(data->sectionPos) != sections.end()) { - if (sections.find(data->sectionPos)->second.GetHash() == data->hash && !data->forced) { - LOG(INFO) << "Generated not necesarry RendererData"; - sectionsMutex.unlock(); - continue; - } - sections.erase(sections.find(data->sectionPos)); - } - RendererSection renderer(*data); - sections.insert(std::make_pair(data->sectionPos, std::move(renderer))); - sectionsMutex.unlock(); - } - renderDataMutex.unlock(); + listener->RegisterHandler("SectionParsed",[this](const Event &eventData) { + auto id = eventData.get(); + parsing[id].parsing = false; + + auto it = sections.find(parsing[id].renderer.sectionPos); + + if (it != sections.end() && parsing[id].renderer.hash == it->second.GetHash() && !parsing[id].renderer.forced) { + LOG(WARNING) << "Generated not necessary RendererSectionData: " << parsing[id].renderer.sectionPos; + return; + } + + if (it != sections.end()) + sections.erase(it); + + const RendererSectionData &data = parsing[id].renderer; + + sections.emplace(std::make_pair(parsing[id].renderer.sectionPos, RendererSection(data))); + + parsing[id] = RendererWorld::SectionParsing(); }); listener->RegisterHandler("EntityChanged", [this](const Event& eventData) { @@ -177,6 +196,9 @@ RendererWorld::RendererWorld(GameState* ptr) { listener->RegisterHandler("ChunkChanged", [this](const Event& eventData) { auto vec = eventData.get(); + if (vec == Vector()) + return; + Vector playerChunk(std::floor(gs->player->pos.x / 16), 0, std::floor(gs->player->pos.z / 16)); double distanceToChunk = (Vector(vec.x, 0, vec.z) - playerChunk).GetLength(); @@ -184,23 +206,16 @@ RendererWorld::RendererWorld(GameState* ptr) { return; } - isParsingMutex.lock(); - if (isParsing.find(vec) == isParsing.end()) - isParsing[vec] = false; - if (isParsing[vec] == true) { - isParsingMutex.unlock(); - return; - } - isParsing[vec] = true; - isParsingMutex.unlock(); + parseQueue.push(vec); - PUSH_EVENT("RendererWorkerTask", std::make_tuple(currentWorker++, vec, false)); - if (currentWorker >= numOfWorkers) - currentWorker = 0; + parseQueueNeedRemoveUnnecessary = true; }); listener->RegisterHandler("ChunkChangedForce", [this](const Event& eventData) { auto vec = eventData.get(); + if (vec == Vector()) + return; + Vector playerChunk(std::floor(gs->player->pos.x / 16), 0, std::floor(gs->player->pos.z / 16)); double distanceToChunk = (Vector(vec.x, 0, vec.z) - playerChunk).GetLength(); @@ -208,19 +223,11 @@ RendererWorld::RendererWorld(GameState* ptr) { return; } - isParsingMutex.lock(); - if (isParsing.find(vec) == isParsing.end()) - isParsing[vec] = false; - if (isParsing[vec] == true) { - isParsingMutex.unlock(); - return; - } - isParsing[vec] = true; - isParsingMutex.unlock(); + vec.y += 4500; - PUSH_EVENT("RendererWorkerTask", std::make_tuple(currentWorker++, vec, true)); - if (currentWorker >= numOfWorkers) - currentWorker = 0; + parseQueue.push(vec); + + parseQueueNeedRemoveUnnecessary = true; }); listener->RegisterHandler("UpdateSectionsRender", [this](const Event&) { @@ -234,11 +241,9 @@ RendererWorld::RendererWorld(GameState* ptr) { listener->RegisterHandler("ChunkDeleted", [this](const Event& eventData) { auto pos = eventData.get(); - sectionsMutex.lock(); auto it = sections.find(pos); if (it != sections.end()) sections.erase(it); - sectionsMutex.unlock(); }); for (int i = 0; i < numOfWorkers; i++) @@ -249,11 +254,9 @@ RendererWorld::RendererWorld(GameState* ptr) { RendererWorld::~RendererWorld() { size_t faces = 0; - sectionsMutex.lock(); for (auto& it : sections) { faces += it.second.numOfFaces; } - sectionsMutex.unlock(); LOG(INFO) << "Total faces to render: " << faces; isRunning = false; for (int i = 0; i < numOfWorkers; i++) @@ -397,11 +400,9 @@ void RendererWorld::Render(RenderState & renderState) { frustum->UpdateFrustum(projView); - sectionsMutex.lock(); size_t culledSections = sections.size(); - for (auto& section : sections) { - sectionsMutex.unlock(); - std::vector sectionCorners = { + for (auto& section : sections) { + const static Vector sectionCorners[] = { Vector(0, 0, 0), Vector(0, 0, 16), Vector(0, 16, 0), @@ -429,15 +430,12 @@ void RendererWorld::Render(RenderState & renderState) { ).GetLength(); if (!isVisible && lengthToSection > 30.0f) { - sectionsMutex.lock(); culledSections--; continue; } section.second.Render(renderState); - sectionsMutex.lock(); } this->culledSections = culledSections; - sectionsMutex.unlock(); glCheckError(); } @@ -463,16 +461,21 @@ void RendererWorld::PrepareRender() { void RendererWorld::Update(double timeToUpdate) { static auto timeSincePreviousUpdate = std::chrono::steady_clock::now(); - int i = 0; - while (listener->NotEmpty() && i++ < 50) - listener->HandleEvent(); + + if (parseQueueNeedRemoveUnnecessary) + ParseQeueueRemoveUnnecessary(); + + ParseQueueUpdate(); + + listener->HandleAllEvents(); + if (std::chrono::steady_clock::now() - timeSincePreviousUpdate > std::chrono::seconds(5)) { PUSH_EVENT("UpdateSectionsRender", 0); timeSincePreviousUpdate = std::chrono::steady_clock::now(); } - DebugInfo::readyRenderer = this->renderData.size(); - DebugInfo::renderSections = this->sections.size(); + DebugInfo::readyRenderer = parseQueue.size(); + DebugInfo::renderSections = sections.size(); } GameState* RendererWorld::GameStatePtr() { diff --git a/src/RendererWorld.hpp b/src/RendererWorld.hpp index b9d1113..77a02fd 100644 --- a/src/RendererWorld.hpp +++ b/src/RendererWorld.hpp @@ -10,6 +10,7 @@ #include "RendererSection.hpp" #include "RendererEntity.hpp" #include "RendererSky.hpp" +#include "RendererSectionData.hpp" class Frustum; class GameState; @@ -17,9 +18,14 @@ class Texture; class Shader; class EventListener; class RenderState; -class RendererSectionData; class RendererWorld { + struct SectionParsing { + SectionsData data; + RendererSectionData renderer; + bool parsing = false; + }; + //General GameState *gs; std::unique_ptr listener; @@ -28,13 +34,14 @@ class RendererWorld { std::vector workers; void WorkerFunction(size_t WorkerId); bool isRunning = true; - std::mutex isParsingMutex; - std::map isParsing; + const static size_t parsingBufferSize = 64; + SectionParsing parsing[parsingBufferSize]; + std::queue parseQueue; + bool parseQueueNeedRemoveUnnecessary = false; + void ParseQueueUpdate(); + void ParseQeueueRemoveUnnecessary(); //Blocks - std::mutex renderDataMutex; - std::queue> renderData; std::vector renderList; - std::mutex sectionsMutex; std::map sections; Shader *blockShader; void UpdateAllSections(VectorF playerPos); -- cgit v1.2.3