Merge pull request #325 from MihailRis/optimize-ChunksRenderer

Optimize ChunksRenderer
This commit is contained in:
MihailRis 2024-10-24 11:58:58 +03:00 committed by GitHub
commit 9157012cdb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 122 additions and 45 deletions

View File

@ -272,15 +272,15 @@ public:
LoaderWorker(AssetsLoader* loader) : loader(loader) { LoaderWorker(AssetsLoader* loader) : loader(loader) {
} }
assetload::postfunc operator()(const std::shared_ptr<aloader_entry>& entry assetload::postfunc operator()(const aloader_entry& entry
) override { ) override {
aloader_func loadfunc = loader->getLoader(entry->tag); aloader_func loadfunc = loader->getLoader(entry.tag);
return loadfunc( return loadfunc(
loader, loader,
loader->getPaths(), loader->getPaths(),
entry->filename, entry.filename,
entry->alias, entry.alias,
entry->config entry.config
); );
} }
}; };
@ -290,14 +290,13 @@ std::shared_ptr<Task> AssetsLoader::startTask(runnable onDone) {
std::make_shared<util::ThreadPool<aloader_entry, assetload::postfunc>>( std::make_shared<util::ThreadPool<aloader_entry, assetload::postfunc>>(
"assets-loader-pool", "assets-loader-pool",
[=]() { return std::make_shared<LoaderWorker>(this); }, [=]() { return std::make_shared<LoaderWorker>(this); },
[=](assetload::postfunc& func) { func(assets); } [=](const assetload::postfunc& func) { func(assets); }
); );
pool->setOnComplete(std::move(onDone)); pool->setOnComplete(std::move(onDone));
while (!entries.empty()) { while (!entries.empty()) {
const aloader_entry& entry = entries.front(); aloader_entry entry = std::move(entries.front());
auto ptr = std::make_shared<aloader_entry>(entry);
pool->enqueueJob(ptr);
entries.pop(); entries.pop();
pool->enqueueJob(std::move(entry));
} }
return pool; return pool;
} }

View File

@ -43,8 +43,8 @@ using aloader_func = std::function<
struct aloader_entry { struct aloader_entry {
AssetType tag; AssetType tag;
const std::string filename; std::string filename;
const std::string alias; std::string alias;
std::shared_ptr<AssetCfg> config; std::shared_ptr<AssetCfg> config;
}; };

View File

@ -27,8 +27,8 @@ public:
: converter(std::move(converter)) { : converter(std::move(converter)) {
} }
int operator()(const std::shared_ptr<ConvertTask>& task) override { int operator()(const ConvertTask& task) override {
converter->convert(*task); converter->convert(task);
return 0; return 0;
} }
}; };
@ -169,10 +169,10 @@ std::shared_ptr<Task> WorldConverter::startTask(
); );
auto& converterTasks = converter->tasks; auto& converterTasks = converter->tasks;
while (!converterTasks.empty()) { while (!converterTasks.empty()) {
const ConvertTask& task = converterTasks.front(); ConvertTask task = std::move(converterTasks.front());
auto ptr = std::make_shared<ConvertTask>(task);
pool->enqueueJob(ptr);
converterTasks.pop(); converterTasks.pop();
pool->enqueueJob(std::move(task));
} }
pool->setOnComplete([=]() { pool->setOnComplete([=]() {
converter->write(); converter->write();

View File

@ -4,6 +4,21 @@
int Mesh::meshesCount = 0; int Mesh::meshesCount = 0;
int Mesh::drawCalls = 0; int Mesh::drawCalls = 0;
inline size_t calc_vertex_size(const vattr* attrs) {
size_t vertexSize = 0;
for (int i = 0; attrs[i].size; i++) {
vertexSize += attrs[i].size;
}
return vertexSize;
}
Mesh::Mesh(const MeshData& data)
: Mesh(data.vertices.data(),
data.vertices.size() / calc_vertex_size(data.attrs.data()),
data.indices.data(),
data.indices.size(),
data.attrs.data()) {}
Mesh::Mesh(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices, const vattr* attrs) : Mesh::Mesh(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices, const vattr* attrs) :
ibo(0), ibo(0),
vertices(vertices), vertices(vertices),

View File

@ -1,11 +1,9 @@
#pragma once #pragma once
#include <stdlib.h> #include <stdlib.h>
#include "typedefs.hpp"
struct vattr { #include "typedefs.hpp"
ubyte size; #include "MeshData.hpp"
};
class Mesh { class Mesh {
unsigned int vao; unsigned int vao;
@ -15,6 +13,7 @@ class Mesh {
size_t indices; size_t indices;
size_t vertexSize; size_t vertexSize;
public: public:
Mesh(const MeshData& data);
Mesh(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices, const vattr* attrs); Mesh(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices, const vattr* attrs);
Mesh(const float* vertexBuffer, size_t vertices, const vattr* attrs) : Mesh(const float* vertexBuffer, size_t vertices, const vattr* attrs) :
Mesh(vertexBuffer, vertices, nullptr, 0, attrs) {}; Mesh(vertexBuffer, vertices, nullptr, 0, attrs) {};

View File

@ -0,0 +1,31 @@
#pragma once
#include <vector>
#include "typedefs.hpp"
#include "util/Buffer.hpp"
/// @brief Vertex attribute info
struct vattr {
ubyte size;
};
/// @brief Raw mesh data structure
struct MeshData {
util::Buffer<float> vertices;
util::Buffer<int> indices;
util::Buffer<vattr> attrs;
MeshData() = default;
/// @param vertices vertex data buffer
/// @param indices nullable indices buffer
/// @param attrs vertex attribute sizes (must be null-terminated)
MeshData(
util::Buffer<float> vertices,
util::Buffer<int> indices,
util::Buffer<vattr> attrs
) : vertices(std::move(vertices)),
indices(std::move(indices)),
attrs(std::move(attrs)) {}
};

View File

@ -531,7 +531,18 @@ void BlocksRenderer::build(const Chunk* chunk, const ChunksStorage* chunks) {
render(voxels); render(voxels);
} }
std::shared_ptr<Mesh> BlocksRenderer::createMesh() { MeshData BlocksRenderer::createMesh() {
const vattr attrs[]{ {3}, {2}, {1}, {0} };
return MeshData(
util::Buffer<float>(vertexBuffer.get(), vertexOffset),
util::Buffer<int>(indexBuffer.get(), indexSize),
util::Buffer<vattr>({{3}, {2}, {1}, {0}})
);
}
std::shared_ptr<Mesh> BlocksRenderer::render(const Chunk* chunk, const ChunksStorage* chunks) {
build(chunk, chunks);
const vattr attrs[]{ {3}, {2}, {1}, {0} }; const vattr attrs[]{ {3}, {2}, {1}, {0} };
size_t vcount = vertexOffset / BlocksRenderer::VERTEX_SIZE; size_t vcount = vertexOffset / BlocksRenderer::VERTEX_SIZE;
return std::make_shared<Mesh>( return std::make_shared<Mesh>(
@ -539,11 +550,6 @@ std::shared_ptr<Mesh> BlocksRenderer::createMesh() {
); );
} }
std::shared_ptr<Mesh> BlocksRenderer::render(const Chunk* chunk, const ChunksStorage* chunks) {
build(chunk, chunks);
return createMesh();
}
VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const { VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const {
return voxelsBuffer.get(); return voxelsBuffer.get();
} }

View File

@ -10,6 +10,7 @@
#include "voxels/Block.hpp" #include "voxels/Block.hpp"
#include "voxels/Chunk.hpp" #include "voxels/Chunk.hpp"
#include "voxels/VoxelsVolume.hpp" #include "voxels/VoxelsVolume.hpp"
#include "graphics/core/MeshData.hpp"
class Content; class Content;
class Mesh; class Mesh;
@ -148,7 +149,7 @@ public:
void build(const Chunk* chunk, const ChunksStorage* chunks); void build(const Chunk* chunk, const ChunksStorage* chunks);
std::shared_ptr<Mesh> render(const Chunk* chunk, const ChunksStorage* chunks); std::shared_ptr<Mesh> render(const Chunk* chunk, const ChunksStorage* chunks);
std::shared_ptr<Mesh> createMesh(); MeshData createMesh();
VoxelsVolume* getVoxelsBuffer() const; VoxelsVolume* getVoxelsBuffer() const;
bool isCancelled() const { bool isCancelled() const {

View File

@ -12,7 +12,7 @@
static debug::Logger logger("chunks-render"); static debug::Logger logger("chunks-render");
class RendererWorker : public util::Worker<Chunk, RendererResult> { class RendererWorker : public util::Worker<std::shared_ptr<Chunk>, RendererResult> {
Level* level; Level* level;
BlocksRenderer renderer; BlocksRenderer renderer;
public: public:
@ -27,7 +27,13 @@ public:
RendererResult operator()(const std::shared_ptr<Chunk>& chunk) override { RendererResult operator()(const std::shared_ptr<Chunk>& chunk) override {
renderer.build(chunk.get(), level->chunksStorage.get()); renderer.build(chunk.get(), level->chunksStorage.get());
return RendererResult {glm::ivec2(chunk->x, chunk->z), &renderer}; if (renderer.isCancelled()) {
return RendererResult {
glm::ivec2(chunk->x, chunk->z), true, MeshData()};
}
auto meshData = renderer.createMesh();
return RendererResult {
glm::ivec2(chunk->x, chunk->z), false, std::move(meshData)};
} }
}; };
@ -39,14 +45,13 @@ ChunksRenderer::ChunksRenderer(
threadPool( threadPool(
"chunks-render-pool", "chunks-render-pool",
[=](){return std::make_shared<RendererWorker>(level, cache, settings);}, [=](){return std::make_shared<RendererWorker>(level, cache, settings);},
[=](RendererResult& mesh){ [=](RendererResult& result){
if (!mesh.renderer->isCancelled()) { if (!result.cancelled) {
meshes[mesh.key] = mesh.renderer->createMesh(); meshes[result.key] = std::make_shared<Mesh>(result.meshData);
} }
inwork.erase(mesh.key); inwork.erase(result.key);
}, settings->graphics.chunkMaxRenderers.get()) }, settings->graphics.chunkMaxRenderers.get())
{ {
threadPool.setStandaloneResults(false);
threadPool.setStopOnFail(false); threadPool.setStopOnFail(false);
renderer = std::make_unique<BlocksRenderer>( renderer = std::make_unique<BlocksRenderer>(
settings->graphics.chunkMaxVertices.get(), settings->graphics.chunkMaxVertices.get(),

View File

@ -9,6 +9,7 @@
#include "voxels/Block.hpp" #include "voxels/Block.hpp"
#include "voxels/ChunksStorage.hpp" #include "voxels/ChunksStorage.hpp"
#include "util/ThreadPool.hpp" #include "util/ThreadPool.hpp"
#include "graphics/core/MeshData.hpp"
class Mesh; class Mesh;
class Chunk; class Chunk;
@ -19,7 +20,8 @@ struct EngineSettings;
struct RendererResult { struct RendererResult {
glm::ivec2 key; glm::ivec2 key;
BlocksRenderer* renderer; bool cancelled;
MeshData meshData;
}; };
class ChunksRenderer { class ChunksRenderer {
@ -28,7 +30,7 @@ class ChunksRenderer {
std::unordered_map<glm::ivec2, std::shared_ptr<Mesh>> meshes; std::unordered_map<glm::ivec2, std::shared_ptr<Mesh>> meshes;
std::unordered_map<glm::ivec2, bool> inwork; std::unordered_map<glm::ivec2, bool> inwork;
util::ThreadPool<Chunk, RendererResult> threadPool; util::ThreadPool<std::shared_ptr<Chunk>, RendererResult> threadPool;
public: public:
ChunksRenderer( ChunksRenderer(
Level* level, Level* level,

View File

@ -2,6 +2,7 @@
#include <memory> #include <memory>
#include <cstring> #include <cstring>
#include <initializer_list>
namespace util { namespace util {
/// @brief Template similar to std::unique_ptr stores a buffer with its size /// @brief Template similar to std::unique_ptr stores a buffer with its size
@ -11,6 +12,8 @@ namespace util {
std::unique_ptr<T[]> ptr; std::unique_ptr<T[]> ptr;
size_t length; size_t length;
public: public:
Buffer() = default;
Buffer(size_t length) Buffer(size_t length)
: ptr(std::make_unique<T[]>(length)), length(length) { : ptr(std::make_unique<T[]>(length)), length(length) {
} }
@ -23,7 +26,23 @@ namespace util {
Buffer(const T* src, size_t length) Buffer(const T* src, size_t length)
: ptr(std::make_unique<T[]>(length)), length(length) { : ptr(std::make_unique<T[]>(length)), length(length) {
std::memcpy(ptr.get(), src, length); std::memcpy(ptr.get(), src, length * sizeof(T));
}
Buffer(std::initializer_list<T> values)
: ptr(std::make_unique<T[]>(values.size())),
length(values.size()) {
std::copy(values.begin(), values.end(), ptr.get());
}
Buffer(std::nullptr_t) noexcept : ptr(nullptr), length(0) {}
inline bool operator==(std::nullptr_t) const noexcept {
return ptr == nullptr;
}
inline bool operator!=(std::nullptr_t) const noexcept {
return ptr != nullptr;
} }
T& operator[](long long index) { T& operator[](long long index) {

View File

@ -17,7 +17,7 @@ namespace util {
template <class J, class T> template <class J, class T>
struct ThreadPoolResult { struct ThreadPoolResult {
std::shared_ptr<J> job; J job;
std::condition_variable& variable; std::condition_variable& variable;
int workerIndex; int workerIndex;
bool& locked; bool& locked;
@ -29,13 +29,13 @@ namespace util {
public: public:
Worker() = default; Worker() = default;
virtual ~Worker() = default; virtual ~Worker() = default;
virtual R operator()(const std::shared_ptr<T>&) = 0; virtual R operator()(const T&) = 0;
}; };
template <class T, class R> template <class T, class R>
class ThreadPool : public Task { class ThreadPool : public Task {
debug::Logger logger; debug::Logger logger;
std::queue<std::shared_ptr<T>> jobs; std::queue<T> jobs;
std::queue<ThreadPoolResult<T, R>> results; std::queue<ThreadPoolResult<T, R>> results;
std::mutex resultsMutex; std::mutex resultsMutex;
std::vector<std::thread> threads; std::vector<std::thread> threads;
@ -43,7 +43,7 @@ namespace util {
std::mutex jobsMutex; std::mutex jobsMutex;
std::vector<std::unique_lock<std::mutex>> workersBlocked; std::vector<std::unique_lock<std::mutex>> workersBlocked;
consumer<R&> resultConsumer; consumer<R&> resultConsumer;
consumer<std::shared_ptr<T>&> onJobFailed = nullptr; consumer<T&> onJobFailed = nullptr;
runnable onComplete = nullptr; runnable onComplete = nullptr;
std::atomic<int> busyWorkers = 0; std::atomic<int> busyWorkers = 0;
std::atomic<uint> jobsDone = 0; std::atomic<uint> jobsDone = 0;
@ -57,7 +57,7 @@ namespace util {
std::mutex mutex; std::mutex mutex;
bool locked = false; bool locked = false;
while (working) { while (working) {
std::shared_ptr<T> job; T job;
{ {
std::unique_lock<std::mutex> lock(jobsMutex); std::unique_lock<std::mutex> lock(jobsMutex);
jobsMutexCondition.wait(lock, [this] { jobsMutexCondition.wait(lock, [this] {
@ -66,7 +66,7 @@ namespace util {
if (!working || failed) { if (!working || failed) {
break; break;
} }
job = jobs.front(); job = std::move(jobs.front());
jobs.pop(); jobs.pop();
busyWorkers++; busyWorkers++;
@ -229,10 +229,10 @@ namespace util {
} }
} }
void enqueueJob(const std::shared_ptr<T>& job) { void enqueueJob(T job) {
{ {
std::lock_guard<std::mutex> lock(jobsMutex); std::lock_guard<std::mutex> lock(jobsMutex);
jobs.push(job); jobs.push(std::move(job));
} }
jobsMutexCondition.notify_one(); jobsMutexCondition.notify_one();
} }