Skip to content

Commit

Permalink
Keep separate active clusters list to allow for models to be removed …
Browse files Browse the repository at this point in the history
…when entities are removed/model components are removed

+ fix wrong instance cluster ID output in mesh shader (cloned models would look wrong)
  • Loading branch information
jglrxavpok committed Feb 27, 2024
1 parent 0b0fa46 commit 6407118
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 48 deletions.
63 changes: 17 additions & 46 deletions engine/engine/render/ClusterManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
//

#include "ClusterManager.h"

#include <core/containers/Vector.hpp>
#include <engine/utils/Profiling.h>
#include <engine/utils/Macros.h>
#include <engine/render/resources/ResourceAllocator.h>
Expand Down Expand Up @@ -310,20 +312,32 @@ namespace Carrot::Render {
requireInstanceUpdate = false;
}

BufferView activeModelsBufferView;
activeInstancesAllocator.clear();
Vector<std::uint32_t> activeInstances { activeInstancesAllocator };
if(instanceDataGPUVisibleArray) {
ClusterBasedModelData* pModelData = instanceDataGPUVisibleArray->view.map<ClusterBasedModelData>();

for(auto& [slot, pModel] : models) {
if(auto pLockedModel = pModel.lock()) {
pModelData[slot].visible = pLockedModel->enabled;
pModelData[slot].instanceData = pLockedModel->instanceData;

activeInstances.ensureReserve(activeInstances.size() + pLockedModel->instanceCount);
const std::size_t endInstance = pLockedModel->firstInstance + pLockedModel->instanceCount;
for(std::size_t instanceIndex = pLockedModel->firstInstance; instanceIndex < endInstance; instanceIndex++) {
activeInstances.pushBack(instanceIndex);
}
}
}

activeModelsBufferView = renderer.getSingleFrameBuffer(activeInstances.size() * sizeof(std::uint32_t));
activeModelsBufferView.directUpload(std::span<const std::uint32_t>(activeInstances));
}

clusterDataPerFrame[renderContext.swapchainIndex] = clusterGPUVisibleArray; // keep ref to avoid allocation going back to heap while still in use
auto& instancesPerFrame = instancesPerFramePerViewport[renderContext.pViewport];
if(instancesPerFrame.empty()) {
if(instancesPerFrame.size() != GetEngine().getSwapchainImageCount()) {
instancesPerFrame.resize(GetEngine().getSwapchainImageCount());
}
instancesPerFrame[renderContext.swapchainIndex] = instanceGPUVisibleArray; // keep ref to avoid allocation going back to heap while still in use
Expand All @@ -337,52 +351,9 @@ namespace Carrot::Render {
renderer.bindBuffer(*packet.pipeline, renderContext, instanceRefs, 0, 1);
renderer.bindBuffer(*packet.pipeline, renderContext, instanceDataRefs, 0, 2);
renderer.bindBuffer(*packet.pipeline, renderContext, statsCPUBuffer.view, 0, 4);
renderer.bindBuffer(*packet.pipeline, renderContext, activeModelsBufferView, 0, 5);
}

#if 0
for(const auto& [index, pInstance] : models) {
if(const auto instance = pInstance.lock()) {
if(!instance->enabled) {
continue;
}
if(instance->pViewport != renderContext.pViewport) {
continue;
}

packet.clearPerDrawData();
packet.unindexedDrawCommands.clear();
std::uint32_t instanceIndex = 0;

for(const auto& pTemplate : instance->templates) {
std::size_t clusterOffset = 0;
for(const auto& cluster : pTemplate->clusters) {
if(testLOD(cluster, *instance)) {
auto& drawCommand = packet.unindexedDrawCommands.emplace_back();
drawCommand.instanceCount = 1;
drawCommand.firstInstance = 0;
drawCommand.firstVertex = 0;
drawCommand.vertexCount = std::uint32_t(cluster.triangleCount)*3;

triangleCount += cluster.triangleCount;

GBufferDrawData drawData;
drawData.materialIndex = 0;
drawData.uuid0 = instance->firstInstance + instanceIndex;
packet.addPerDrawData(std::span{ &drawData, 1 });

}

instanceIndex++;
clusterOffset++;
}
}
verify(instanceIndex == instance->instanceCount, "instanceIndex == instance->instanceCount");

if(packet.unindexedDrawCommands.size() > 0)
renderer.render(packet);
}
}
#endif

{
auto& pushConstant = packet.addPushConstant("push", vk::ShaderStageFlagBits::eMeshEXT);
Expand All @@ -403,7 +374,7 @@ namespace Carrot::Render {
}

Render::PacketCommand& drawCommand = packet.commands.emplace_back();
drawCommand.drawMeshTasks.groupCountX = gpuInstances.size();
drawCommand.drawMeshTasks.groupCountX = activeInstances.size();
drawCommand.drawMeshTasks.groupCountY = 1;
drawCommand.drawMeshTasks.groupCountZ = 1;
renderer.render(packet);
Expand Down
3 changes: 3 additions & 0 deletions engine/engine/render/ClusterManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#pragma once

#include <core/allocators/StackAllocator.h>
#include <core/utils/WeakPool.hpp>
#include <engine/render/InstanceData.h>
#include <core/render/Meshlet.h>
Expand Down Expand Up @@ -188,6 +189,8 @@ namespace Carrot::Render {
std::unordered_map<Viewport*, std::shared_ptr<Carrot::Pipeline>> pipelines;
std::unordered_map<Viewport*, Render::PerFrame<std::shared_ptr<Carrot::BufferAllocation>>> instancesPerFramePerViewport;
std::unordered_map<Carrot::Render::Viewport*, Render::PerFrame<BufferAllocation>> statsCPUBuffers;

Carrot::StackAllocator activeInstancesAllocator { Carrot::Allocator::getDefault() };
};

} // Carrot::Render
2 changes: 1 addition & 1 deletion engine/resources/shaders/visibility-buffer.mesh.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ void main() {
const vec4 ndcPosition = cbo.jitteredProjection * viewPosition;
gl_MeshVerticesEXT[vertexIndex].gl_Position = ndcPosition;
outNDCPosition[vertexIndex] = ndcPosition;
outClusterInstanceID[vertexIndex] = clusterID;
outClusterInstanceID[vertexIndex] = instanceID;
}

for(uint triangleIndex = 0; triangleIndex < cluster.triangleCount; triangleIndex++) {
Expand Down
8 changes: 7 additions & 1 deletion engine/resources/shaders/visibility-buffer.task.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ layout(set = 0, binding = 2, std430) buffer ModelDataRef {
ClusterBasedModelData modelData[];
};

// slot 3 used by output image
// slot 4 is stats buffer
layout(set = 0, binding = 5, scalar) buffer ActiveClusters {
uint activeClusters[];
};

// assume a fixed resolution and fov
const float testFOV = M_PI_OVER_2;
const float cotHalfFov = 1.0f / tan(testFOV / 2.0f);
Expand Down Expand Up @@ -92,7 +98,7 @@ bool cull(uint clusterInstanceID) {
}

void main() {
uint clusterID = gl_GlobalInvocationID.x;
uint clusterID = activeClusters[gl_GlobalInvocationID.x];
bool culled = clusterID >= push.maxCluster || cull(clusterID);

// TODO: do multiple emits per task shader? (see NVIDIA example)
Expand Down

0 comments on commit 6407118

Please sign in to comment.