Merge pull request #325 from MihailRis/optimize-ChunksRenderer
Optimize ChunksRenderer
This commit is contained in:
commit
9157012cdb
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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),
|
||||||
|
|||||||
@ -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) {};
|
||||||
|
|||||||
31
src/graphics/core/MeshData.hpp
Normal file
31
src/graphics/core/MeshData.hpp
Normal 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)) {}
|
||||||
|
};
|
||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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(),
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user