feat: distance-based dense render optimization

This commit is contained in:
MihailRis 2025-07-25 00:54:54 +03:00
parent 799d712447
commit fbde46afa7
6 changed files with 87 additions and 39 deletions

View File

@ -126,11 +126,9 @@ void Mesh<VertexStructure>::draw(unsigned int primitive, int iboIndex) const {
if (!ibos.empty()) { if (!ibos.empty()) {
if (iboIndex < ibos.size()) { if (iboIndex < ibos.size()) {
glBindVertexArray(vao); glBindVertexArray(vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibos[ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibos[iboIndex].ibo);
std::min(static_cast<size_t>(iboIndex), ibos.size())
].ibo);
glDrawElements( glDrawElements(
primitive, ibos.at(0).indexCount, GL_UNSIGNED_INT, nullptr primitive, ibos.at(iboIndex).indexCount, GL_UNSIGNED_INT, nullptr
); );
glBindVertexArray(0); glBindVertexArray(0);
} }

View File

@ -20,6 +20,7 @@ BlocksRenderer::BlocksRenderer(
) : content(content), ) : content(content),
vertexBuffer(std::make_unique<ChunkVertex[]>(capacity)), vertexBuffer(std::make_unique<ChunkVertex[]>(capacity)),
indexBuffer(std::make_unique<uint32_t[]>(capacity)), indexBuffer(std::make_unique<uint32_t[]>(capacity)),
denseIndexBuffer(std::make_unique<uint32_t[]>(capacity)),
vertexCount(0), vertexCount(0),
vertexOffset(0), vertexOffset(0),
indexCount(0), indexCount(0),
@ -475,9 +476,10 @@ glm::vec4 BlocksRenderer::pickSoftLight(
} }
void BlocksRenderer::render( void BlocksRenderer::render(
const voxel* voxels, int beginEnds[256][2] const voxel* voxels, const int beginEnds[256][2]
) { ) {
bool denseRender = this->denseRender; bool denseRender = this->denseRender;
bool densePass = this->densePass;
for (const auto drawGroup : *content.drawGroups) { for (const auto drawGroup : *content.drawGroups) {
int begin = beginEnds[drawGroup][0]; int begin = beginEnds[drawGroup][0];
if (begin == 0) { if (begin == 0) {
@ -494,16 +496,19 @@ void BlocksRenderer::render(
if (id == 0 || variant.drawGroup != drawGroup || state.segment) { if (id == 0 || variant.drawGroup != drawGroup || state.segment) {
continue; continue;
} }
if (denseRender != (variant.culling == CullingMode::OPTIONAL)) {
continue;
}
if (def.translucent) { if (def.translucent) {
continue; continue;
} }
const UVRegion texfaces[6] { const UVRegion texfaces[6] {
cache.getRegion(id, variantId, 0, denseRender), cache.getRegion(id, variantId, 0, densePass),
cache.getRegion(id, variantId, 1, denseRender), cache.getRegion(id, variantId, 1, densePass),
cache.getRegion(id, variantId, 2, denseRender), cache.getRegion(id, variantId, 2, densePass),
cache.getRegion(id, variantId, 3, denseRender), cache.getRegion(id, variantId, 3, densePass),
cache.getRegion(id, variantId, 4, denseRender), cache.getRegion(id, variantId, 4, densePass),
cache.getRegion(id, variantId, 5, denseRender) cache.getRegion(id, variantId, 5, densePass)
}; };
int x = i % CHUNK_W; int x = i % CHUNK_W;
int y = i / (CHUNK_D * CHUNK_W); int y = i / (CHUNK_D * CHUNK_W);
@ -514,16 +519,19 @@ void BlocksRenderer::render(
def.ambientOcclusion); def.ambientOcclusion);
break; break;
case BlockModelType::XSPRITE: { case BlockModelType::XSPRITE: {
if (!denseRender)
blockXSprite(x, y, z, glm::vec3(1.0f), blockXSprite(x, y, z, glm::vec3(1.0f),
texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f); texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f);
break; break;
} }
case BlockModelType::AABB: { case BlockModelType::AABB: {
if (!denseRender)
blockAABB({x, y, z}, texfaces, &def, vox.state.rotation, blockAABB({x, y, z}, texfaces, &def, vox.state.rotation,
!def.shadeless, def.ambientOcclusion); !def.shadeless, def.ambientOcclusion);
break; break;
} }
case BlockModelType::CUSTOM: { case BlockModelType::CUSTOM: {
if (!denseRender)
blockCustomModel( blockCustomModel(
{x, y, z}, {x, y, z},
def, def,
@ -552,7 +560,7 @@ SortingMeshData BlocksRenderer::renderTranslucent(
bool aabbInit = false; bool aabbInit = false;
size_t totalSize = 0; size_t totalSize = 0;
bool denseRender = this->denseRender; bool densePass = this->densePass;
for (const auto drawGroup : *content.drawGroups) { for (const auto drawGroup : *content.drawGroups) {
int begin = beginEnds[drawGroup][0]; int begin = beginEnds[drawGroup][0];
if (begin == 0) { if (begin == 0) {
@ -573,12 +581,12 @@ SortingMeshData BlocksRenderer::renderTranslucent(
continue; continue;
} }
const UVRegion texfaces[6] { const UVRegion texfaces[6] {
cache.getRegion(id, variantId, 0, denseRender), cache.getRegion(id, variantId, 0, densePass),
cache.getRegion(id, variantId, 1, denseRender), cache.getRegion(id, variantId, 1, densePass),
cache.getRegion(id, variantId, 2, denseRender), cache.getRegion(id, variantId, 2, densePass),
cache.getRegion(id, variantId, 3, denseRender), cache.getRegion(id, variantId, 3, densePass),
cache.getRegion(id, variantId, 4, denseRender), cache.getRegion(id, variantId, 4, densePass),
cache.getRegion(id, variantId, 5, denseRender) cache.getRegion(id, variantId, 5, densePass)
}; };
int x = i % CHUNK_W; int x = i % CHUNK_W;
int y = i / (CHUNK_D * CHUNK_W); int y = i / (CHUNK_D * CHUNK_W);
@ -703,29 +711,45 @@ void BlocksRenderer::build(const Chunk* chunk, const Chunks* chunks) {
vertexCount = 0; vertexCount = 0;
vertexOffset = indexCount = 0; vertexOffset = indexCount = 0;
denseRender = false;
densePass = false;
sortingMesh = renderTranslucent(voxels, beginEnds); sortingMesh = renderTranslucent(voxels, beginEnds);
overflow = false; overflow = false;
vertexCount = 0; vertexCount = 0;
vertexOffset = 0; vertexOffset = 0;
indexCount = 0; indexCount = 0;
denseIndexCount = 0;
denseRender = settings.graphics.denseRender.get(); denseRender = false; //settings.graphics.denseRender.get();
// denseRender = false; densePass = false;
render(voxels, beginEnds); render(voxels, beginEnds);
// denseRender = settings.graphics.denseRender.get(); size_t endIndex = indexCount;
// if (denseRender) {
// } denseRender = true;
densePass = true;
render(voxels, beginEnds);
denseIndexCount = indexCount;
for (size_t i = 0; i < denseIndexCount; i++) {
denseIndexBuffer[i] = indexBuffer[i];
}
indexCount = endIndex;
densePass = false;
render(voxels, beginEnds);
} }
ChunkMeshData BlocksRenderer::createMesh() { ChunkMeshData BlocksRenderer::createMesh() {
return ChunkMeshData { return ChunkMeshData {
MeshData( MeshData(
util::Buffer(vertexBuffer.get(), vertexCount), util::Buffer(vertexBuffer.get(), vertexCount),
std::vector<util::Buffer<uint32_t>> {util::Buffer(indexBuffer.get(), indexCount)}, std::vector<util::Buffer<uint32_t>> {
util::Buffer(indexBuffer.get(), indexCount),
util::Buffer(denseIndexBuffer.get(), denseIndexCount),
},
util::Buffer( util::Buffer(
ChunkVertex::ATTRIBUTES, sizeof(ChunkVertex::ATTRIBUTES) / sizeof(VertexAttribute) ChunkVertex::ATTRIBUTES, sizeof(ChunkVertex::ATTRIBUTES) / sizeof(VertexAttribute)
) )
@ -739,10 +763,18 @@ ChunkMesh BlocksRenderer::render(const Chunk *chunk, const Chunks *chunks) {
return ChunkMesh{std::make_unique<Mesh<ChunkVertex>>( return ChunkMesh{std::make_unique<Mesh<ChunkVertex>>(
vertexBuffer.get(), vertexCount, vertexBuffer.get(), vertexCount,
std::vector<IndexBufferData> {IndexBufferData {indexBuffer.get(), indexCount}} std::vector<IndexBufferData> {
IndexBufferData {indexBuffer.get(), indexCount},
IndexBufferData {denseIndexBuffer.get(), denseIndexCount},
}
), std::move(sortingMesh)}; ), std::move(sortingMesh)};
} }
VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const { VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const {
return voxelsBuffer.get(); return voxelsBuffer.get();
} }
size_t BlocksRenderer::getMemoryConsumption() const {
size_t volume = voxelsBuffer->getW() * voxelsBuffer->getH() * voxelsBuffer->getD();
return capacity * (sizeof(ChunkVertex) + sizeof(uint32_t) * 2) + volume * (sizeof(voxel) + sizeof(light_t));
}

View File

@ -26,13 +26,16 @@ class BlocksRenderer {
const Content& content; const Content& content;
std::unique_ptr<ChunkVertex[]> vertexBuffer; std::unique_ptr<ChunkVertex[]> vertexBuffer;
std::unique_ptr<uint32_t[]> indexBuffer; std::unique_ptr<uint32_t[]> indexBuffer;
std::unique_ptr<uint32_t[]> denseIndexBuffer;
size_t vertexCount; size_t vertexCount;
size_t vertexOffset; size_t vertexOffset;
size_t indexCount; size_t indexCount;
size_t denseIndexCount;
size_t capacity; size_t capacity;
int voxelBufferPadding = 2; int voxelBufferPadding = 2;
bool overflow = false; bool overflow = false;
bool cancelled = false; bool cancelled = false;
bool densePass = false;
bool denseRender = false; bool denseRender = false;
const Chunk* chunk = nullptr; const Chunk* chunk = nullptr;
std::unique_ptr<VoxelsVolume> voxelsBuffer; std::unique_ptr<VoxelsVolume> voxelsBuffer;
@ -136,10 +139,12 @@ class BlocksRenderer {
if ((otherDrawGroup && (otherDrawGroup != variant.drawGroup)) || !blockVariant.rt.solid) { if ((otherDrawGroup && (otherDrawGroup != variant.drawGroup)) || !blockVariant.rt.solid) {
return true; return true;
} }
if ((variant.culling == CullingMode::DISABLED || if (densePass) {
(variant.culling == CullingMode::OPTIONAL && return variant.culling == CullingMode::OPTIONAL;
denseRender)) && } else if (variant.culling == CullingMode::OPTIONAL) {
vox.id == def.rt.id) { return false;
}
if (variant.culling == CullingMode::DISABLED && vox.id == def.rt.id) {
return true; return true;
} }
return !vox.id; return !vox.id;
@ -150,7 +155,7 @@ class BlocksRenderer {
glm::vec4 pickSoftLight(const glm::ivec3& coord, const glm::ivec3& right, const glm::ivec3& up) const; glm::vec4 pickSoftLight(const glm::ivec3& coord, const glm::ivec3& right, const glm::ivec3& up) const;
glm::vec4 pickSoftLight(float x, float y, float z, const glm::ivec3& right, const glm::ivec3& up) const; glm::vec4 pickSoftLight(float x, float y, float z, const glm::ivec3& right, const glm::ivec3& up) const;
void render(const voxel* voxels, int beginEnds[256][2]); void render(const voxel* voxels, const int beginEnds[256][2]);
SortingMeshData renderTranslucent(const voxel* voxels, int beginEnds[256][2]); SortingMeshData renderTranslucent(const voxel* voxels, int beginEnds[256][2]);
public: public:
BlocksRenderer( BlocksRenderer(
@ -166,6 +171,8 @@ public:
ChunkMeshData createMesh(); ChunkMeshData createMesh();
VoxelsVolume* getVoxelsBuffer() const; VoxelsVolume* getVoxelsBuffer() const;
size_t getMemoryConsumption() const;
bool isCancelled() const { bool isCancelled() const {
return cancelled; return cancelled;
} }

View File

@ -18,6 +18,9 @@ static debug::Logger logger("chunks-render");
size_t ChunksRenderer::visibleChunks = 0; size_t ChunksRenderer::visibleChunks = 0;
inline constexpr int DENSE_DISTANCE = 64;
inline constexpr int DENSE_DISTANCE2 = DENSE_DISTANCE * DENSE_DISTANCE;
class RendererWorker : public util::Worker<std::shared_ptr<Chunk>, RendererResult> { class RendererWorker : public util::Worker<std::shared_ptr<Chunk>, RendererResult> {
const Chunks& chunks; const Chunks& chunks;
BlocksRenderer renderer; BlocksRenderer renderer;
@ -87,6 +90,9 @@ ChunksRenderer::ChunksRenderer(
level->content, cache, settings level->content, cache, settings
); );
logger.info() << "created " << threadPool.getWorkersCount() << " workers"; logger.info() << "created " << threadPool.getWorkersCount() << " workers";
logger.info() << "memory consumption is "
<< renderer->getMemoryConsumption() * threadPool.getWorkersCount()
<< " B";
} }
ChunksRenderer::~ChunksRenderer() = default; ChunksRenderer::~ChunksRenderer() = default;
@ -182,7 +188,7 @@ const Mesh<ChunkVertex>* ChunksRenderer::retrieveChunk(
} }
void ChunksRenderer::drawChunksShadowsPass( void ChunksRenderer::drawChunksShadowsPass(
const Camera& camera, Shader& shader const Camera& camera, Shader& shader, const Camera& playerCamera
) { ) {
Frustum frustum; Frustum frustum;
frustum.update(camera.getProjView()); frustum.update(camera.getProjView());
@ -205,7 +211,7 @@ void ChunksRenderer::drawChunksShadowsPass(
pos.x * CHUNK_W + 0.5f, 0.5f, pos.y * CHUNK_D + 0.5f pos.x * CHUNK_W + 0.5f, 0.5f, pos.y * CHUNK_D + 0.5f
); );
glm::vec3 min(pos.x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D); glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D);
glm::vec3 max( glm::vec3 max(
chunk->x * CHUNK_W + CHUNK_W, chunk->x * CHUNK_W + CHUNK_W,
chunk->top, chunk->top,
@ -217,7 +223,9 @@ void ChunksRenderer::drawChunksShadowsPass(
} }
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord); glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
shader.uniformMatrix("u_model", model); shader.uniformMatrix("u_model", model);
found->second.mesh->draw(); found->second.mesh->draw(GL_TRIANGLES,
glm::distance2(playerCamera.position * glm::vec3(1, 0, 1),
(min + max) * 0.5f * glm::vec3(1, 0, 1)) < DENSE_DISTANCE2);
} }
} }
@ -265,7 +273,8 @@ void ChunksRenderer::drawChunks(
); );
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord); glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
shader.uniformMatrix("u_model", model); shader.uniformMatrix("u_model", model);
mesh->draw(); mesh->draw(GL_TRIANGLES, glm::distance2(camera.position * glm::vec3(1, 0, 1),
(coord + glm::vec3(CHUNK_W * 0.5f, 0.0f, CHUNK_D * 0.5f))) < DENSE_DISTANCE2);
visibleChunks++; visibleChunks++;
} }
} }

View File

@ -73,7 +73,9 @@ public:
const std::shared_ptr<Chunk>& chunk, bool important const std::shared_ptr<Chunk>& chunk, bool important
); );
void drawChunksShadowsPass(const Camera& camera, Shader& shader); void drawChunksShadowsPass(
const Camera& camera, Shader& shader, const Camera& playerCamera
);
void drawChunks(const Camera& camera, Shader& shader); void drawChunks(const Camera& camera, Shader& shader);

View File

@ -406,7 +406,7 @@ void WorldRenderer::generateShadowsMap(
sctx.setViewport({resolution, resolution}); sctx.setViewport({resolution, resolution});
shadowMap.bind(); shadowMap.bind();
setupWorldShader(shadowsShader, shadowCamera, settings, 0.0f); setupWorldShader(shadowsShader, shadowCamera, settings, 0.0f);
chunks->drawChunksShadowsPass(shadowCamera, shadowsShader); chunks->drawChunksShadowsPass(shadowCamera, shadowsShader, camera);
shadowMap.unbind(); shadowMap.unbind();
} }
} }