Vulkan Schnee 0.0.1
High-performance rendering engine
Loading...
Searching...
No Matches
RenderProcess.cpp
Go to the documentation of this file.
3
10#include <plog/Log.h>
11#include <Engine/Core/Engine.h>
12
14#include <cmath>
15#include <cstring>
16
19
20namespace EngineCore {
27#if defined(HEADLESS)
28 #define GRAPHICS_GUARD() return;
29 #define COMPUTE_GUARD() return;
30#elif defined(COMPUTE_DEBUG)
31 #define GRAPHICS_GUARD() return; // Skip graphics in debug mode
32 #define COMPUTE_GUARD() // No-op: compute runs in debug mode
33#else
34 #define GRAPHICS_GUARD()
35 #define COMPUTE_GUARD()
36#endif
37
40 const auto& renderingDataManager = renderer->getRenderingDataManager();
41 std::vector<VkDescriptorImageInfo> imageInfos = renderingDataManager->generateTextureDescriptorInfos();
42 PLOGI << "Update renderer descriptor sets";
44 .addStorageBuffer(ShaderStage::Graphics::VERTEX_BUFFER, VulkanHelper::fullBufferInfo( renderingDataManager->getVertexBuffer().getBuffer() ) )
46 .addStorageBuffer(ShaderStage::Graphics::MESHLET_BUFFER, VulkanHelper::fullBufferInfo( renderingDataManager->getMeshletBuffer().getBuffer() ) )
47 .addStorageBuffer(ShaderStage::Graphics::MESHLET_TRIANGLES_BUFFER, VulkanHelper::fullBufferInfo( renderingDataManager->getTriangleBuffer().getBuffer() ) )
49 .addStorageBuffer(ShaderStage::Graphics::PER_OBJECT_SSBO, VulkanHelper::fullBufferInfo( renderingDataManager->getPerObjectDataBuffer().getBuffer() ) )
51 .addStorageBuffer(ShaderStage::Graphics::MESH_PRIMITIVE_BUFFER, VulkanHelper::fullBufferInfo( renderingDataManager->getPrimitiveRenderDataBuffer().getBuffer() ) )
52 // NOTE: MESHLET_TO_OBJECT_MAP_BUFFER (binding 8) is not used in shaders - provide placeholder
55 .addStorageBuffer(ShaderStage::Graphics::MATERIAL_NORMALS, VulkanHelper::fullBufferInfo( renderingDataManager->getMaterialBufferForPipeline( PipelineNames::NORMALS_SHADER ).getBuffer() ) )
56 .addStorageBuffer(ShaderStage::Graphics::MATERIAL_DIFFUSE_SHADER, VulkanHelper::fullBufferInfo( renderingDataManager->getMaterialBufferForPipeline( PipelineNames::DIFFUSE_SHADER ).getBuffer() ) )
58 .addStorageBuffer(ShaderStage::Graphics::MATERIAL_L0, VulkanHelper::fullBufferInfo( renderingDataManager->getMaterialBufferForPipeline( PipelineNames::L0_SHADER ).getBuffer() ) )
59 .addStorageBuffer(ShaderStage::Graphics::MATERIAL_L1, VulkanHelper::fullBufferInfo( renderingDataManager->getMaterialBufferForPipeline( PipelineNames::L1_SHADER ).getBuffer() ) )
60 .addStorageBuffer(ShaderStage::Graphics::MATERIAL_L2, VulkanHelper::fullBufferInfo( renderingDataManager->getMaterialBufferForPipeline( PipelineNames::L2_SHADER ).getBuffer() ) )
63 .addTextureArrayIf(!imageInfos.empty(), ShaderStage::Graphics::TEXTURE_ARRAY, &imageInfos)
64 // VS instanced drawing: instance ID buffer for vertex shader lookup
66 .update(context->getVkDevice());
67 }
68
69 void RenderProcess::updateComputeObjectCullingDescriptorSets(VkImageView previousFrameHiZView) const {
71 PLOGI << "Update object culling descriptor sets";
72 const auto& renderingDataManager = renderer->getRenderingDataManager();
73 const Headset* headset = renderer->getHeadset();
74
75 // Use provided Hi-Z view (from previous frame) or fallback to own (for initial setup)
76 VkImageView hiZView = (previousFrameHiZView != VK_NULL_HANDLE)
77 ? previousFrameHiZView
79
80 // Hi-Z image info for occlusion culling (previous frame's Hi-Z for Pass 1)
81 VkDescriptorImageInfo hiZImageInfo{
82 .sampler = headset->getHiZSampler(),
83 .imageView = hiZView,
84 .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
85 };
86
87 // Current frame's Hi-Z for Pass 2 occlusion culling
88 // This is this RenderProcess's own Hi-Z pyramid (generated after Pass 1 rendering)
89 VkDescriptorImageInfo currentHiZImageInfo{
90 .sampler = headset->getHiZSampler(),
91 .imageView = getHiZPyramidFullView(),
92 .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
93 };
94
95 renderer->getObjectCullingComputePass().updateDescriptorSet(getEyeIndex())
96 .addStorageBuffer(ShaderStage::PrimitiveCulling::PRIMITIVE_CULLING_DATA, VulkanHelper::fullBufferInfo( renderingDataManager->getPrimitiveCullingBuffer().getBuffer() ) )
97 .addStorageBuffer(ShaderStage::PrimitiveCulling::PRIMITIVE_MESHLET_DATA, VulkanHelper::fullBufferInfo( renderingDataManager->getPrimitiveMeshletBuffer().getBuffer() ) )
99 .addStorageBuffer(ShaderStage::PrimitiveCulling::PRIMITIVE_IDS, VulkanHelper::fullBufferInfo( renderer->getObjectIDsBuffer().getBuffer() ) )
100 .addStorageBuffer(ShaderStage::PrimitiveCulling::PRIMITIVE_CULLING_COUNTER, VulkanHelper::fullBufferInfo( renderer->getCounterBuffer().getBuffer() ) )
101 .addStorageBuffer(ShaderStage::PrimitiveCulling::PRIMITIVE_MESH_UNPACKING_DATA, VulkanHelper::fullBufferInfo( renderer->getMeshUnpackingDataBuffer().getBuffer() ) )
102 // Hi-Z Occlusion Culling bindings (two pyramids for two-pass culling)
103 .addCombinedImageSampler(ShaderStage::HiZCulling::HIZ_PYRAMID, &hiZImageInfo) // Previous frame's Hi-Z (Pass 1)
105 .addCombinedImageSampler(ShaderStage::HiZCulling::HIZ_PYRAMID_CURRENT, &currentHiZImageInfo) // Current frame's Hi-Z (Pass 2)
106 // GPU Transform Optimization: bind local bounds and per-object transforms for GPU-side world space computation
107 .addStorageBuffer(ShaderStage::PrimitiveCulling::PRIMITIVE_LOCAL_BOUNDS, VulkanHelper::fullBufferInfo( renderingDataManager->getLocalBoundsBuffer().getBuffer() ) )
108 .addStorageBuffer(ShaderStage::PrimitiveCulling::PRIMITIVE_PER_OBJECT_DATA, VulkanHelper::fullBufferInfo( renderingDataManager->getPerObjectDataBuffer().getBuffer() ) )
109 // Two-pass occlusion culling: failed objects buffer for Pass 2 re-testing
112 // Vertex shader path for single-meshlet geometry optimization (now outputs primitive IDs)
115 .addStorageBuffer(ShaderStage::PrimitiveCulling::PRIMITIVE_SINGLE_MESHLET_GEO, VulkanHelper::fullBufferInfo( renderingDataManager->getSingleMeshletGeometryBuffer().getBuffer() ) )
116 .addStorageBuffer(ShaderStage::PrimitiveCulling::PRIMITIVE_INSTANCE_CULLING_DATA, VulkanHelper::fullBufferInfo( renderingDataManager->getInstanceCullingDataBuffer().getBuffer() ) )
117 .addStorageBuffer(ShaderStage::PrimitiveCulling::PRIMITIVE_MESH_GEOMETRY_DATA, VulkanHelper::fullBufferInfo( renderingDataManager->getMeshGeometryDataBuffer().getBuffer() ) )
118 // LOD cluster-based selection buffers
119 .addStorageBuffer(ShaderStage::PrimitiveCulling::CLUSTER_LOD_DATA, VulkanHelper::fullBufferInfo( renderingDataManager->getClusterLodDataBuffer().getBuffer() ) )
120 .addStorageBuffer(ShaderStage::PrimitiveCulling::CLUSTER_GROUP_DATA, VulkanHelper::fullBufferInfo( renderingDataManager->getClusterGroupDataBuffer().getBuffer() ) )
124 .update(context->getVkDevice());
125 }
126
129 PLOGI << "Update binning allocator descriptor sets";
130 const auto& renderingDataManager = renderer->getRenderingDataManager();
131 renderer->getBinningAllocatorComputePass().updateDescriptorSet(getEyeIndex())
132 .addStorageBuffer(ShaderStage::PrimitiveBinning::BINNING_CULLING_SURVIVORS, VulkanHelper::fullBufferInfo( renderer->getMeshUnpackingDataBuffer().getBuffer() ) )
133 .addStorageBuffer(ShaderStage::PrimitiveBinning::BINNING_PRIMITIVE_MESHLET_DATA, VulkanHelper::fullBufferInfo( renderingDataManager->getPrimitiveMeshletBuffer().getBuffer() ) )
136 .addStorageBuffer(ShaderStage::PrimitiveBinning::BINNING_SURVIVOR_COUNT, VulkanHelper::fullBufferInfo( renderer->getCounterBuffer().getBuffer() ) )
137 // LOD cluster survivor inputs for Stage 1 binning
140 .addStorageBuffer(ShaderStage::PrimitiveBinning::BINNING_CLUSTER_LOD_DATA, VulkanHelper::fullBufferInfo( renderingDataManager->getClusterLodDataBuffer().getBuffer() ) )
141 .addStorageBuffer(ShaderStage::PrimitiveBinning::BINNING_PRIMITIVE_MESHLET_DATA_LOD, VulkanHelper::fullBufferInfo( renderingDataManager->getPrimitiveMeshletBuffer().getBuffer() ) )
142 .update(context->getVkDevice());
143 }
144
147 PLOGI << "Update meshlet unpacking dispatcher descriptor sets";
148 renderer->getMeshletUnpackingDispatchComputePass().updateDescriptorSet(getEyeIndex())
152 .update(context->getVkDevice());
153 }
154
157 PLOGI << "Update meshlet unpacking descriptor sets (V2 bindings)";
158 renderer->getMeshletUnpackingComputePass().updateDescriptorSet(getEyeIndex())
162 .addStorageBuffer(ShaderStage::MeshletUnpackingV2::UNPACKING_SURVIVOR_COUNT, VulkanHelper::fullBufferInfo( renderer->getCounterBuffer().getBuffer() ) )
163 // LOD cluster survivor count for combined allocation processing
165 .update(context->getVkDevice());
166 }
167
170 renderer->getMeshletCullingDispatchComputePass().updateDescriptorSet(getEyeIndex())
173 .update(context->getVkDevice());
174 }
175
178 const auto& renderingDataManager = renderer->getRenderingDataManager();
179 renderer->getMeshletCullingComputePass().updateDescriptorSet(getEyeIndex())
181 .addStorageBuffer( ShaderStage::MeshletCulling::MESHLET_CULLING_BOUNDS, VulkanHelper::fullBufferInfo( renderingDataManager->getMeshletBoundsBuffer().getBuffer() ) )
185 .update(context->getVkDevice());
186 }
187
190 renderer->getDrawPreparationComputePass().updateDescriptorSet(getEyeIndex())
191 // Use pipelineCountersBuffer (written by BinningAllocator) instead of meshletCounterBuffer
192 // BinningAllocator computes per-pipeline meshlet counts, which PrepareDraw reads to generate indirect draw commands
195 .update(context->getVkDevice());
196 }
197
200 PLOGI << "Update VS binning allocator descriptor sets";
201 const auto& renderingDataManager = renderer->getRenderingDataManager();
202 renderer->getVSBinningAllocatorComputePass().updateDescriptorSet(getEyeIndex())
205 .addStorageBuffer(ShaderStage::VSBinning::VS_BINNING_MESH_GEOMETRY_DATA, VulkanHelper::fullBufferInfo( renderingDataManager->getMeshGeometryDataBuffer().getBuffer() ) )
206 .addStorageBuffer(ShaderStage::VSBinning::VS_BINNING_INSTANCE_CULLING_DATA, VulkanHelper::fullBufferInfo( renderingDataManager->getInstanceCullingDataBuffer().getBuffer() ) )
209 .update(context->getVkDevice());
210 }
211
214 PLOGI << "Update VS instance unpacking descriptor sets";
215 renderer->getVSInstanceUnpackingComputePass().updateDescriptorSet(getEyeIndex())
219 .update(context->getVkDevice());
220 }
221
224 PLOGI << "Update VS prepare draw descriptor sets for eye " << eyeIndex;
225 const auto& renderingDataManager = renderer->getRenderingDataManager();
226 // Log buffer handles for debugging buffer mismatch
227 PLOGI << "[VS_BUFFER_DEBUG] Eye " << eyeIndex << " VSPrepareDraw descriptor binding:"
228 << " vsDrawCountBuffer=" << (void*)getVSDrawCountBuffer().getBuffer()
229 << " vsDrawCommandsBuffer=" << (void*)getVSDrawCommandsBuffer().getBuffer();
230 renderer->getVSPrepareDrawComputePass().updateDescriptorSet(getEyeIndex())
231 .addStorageBuffer(ShaderStage::VSPrepareDraw::VS_PREPARE_SINGLE_MESHLET_GEO, VulkanHelper::fullBufferInfo( renderingDataManager->getSingleMeshletGeometryBuffer().getBuffer() ) )
235 .update(context->getVkDevice());
236 }
237
239 PLOGI << "Refreshing all descriptor sets for eye " << eyeIndex;
248 // VS instanced drawing pipeline (optional - may not exist if disabled)
249 try {
253 } catch (const std::runtime_error& e) {
254 // VS instanced drawing is disabled, skip updates
255 }
256 }
257
259 if (rdm == nullptr) return;
260
261 uint64_t currentVersion = rdm->getDataVersion();
262 if (lastSeenDataVersion_ < currentVersion) {
263 PLOGI << "RenderProcess " << eyeIndex << " syncing: version "
264 << lastSeenDataVersion_ << " -> " << currentVersion;
266 lastSeenDataVersion_ = currentVersion;
267 }
268 }
269
271 // DEPRECATED: Mesh rendering data is now managed by RenderingDataManager.
272 // UnifiedMeshlet data is populated via RenderingDataManager::updatePrimitiveData()
273 // and stored in getMeshletBuffer(). This function is kept as a no-op for API compatibility.
274 }
275
277 // DEPRECATED: Mesh rendering data is now managed by RenderingDataManager.
278 // Data is uploaded via RenderingDataManager::updatePrimitiveData().
279 // Return empty sync objects as no upload is performed here.
281 }
282
284 // DEPRECATED: Per-object data is now managed by RenderingDataManager.
285 // The slimmed-down PerObjectData (mat4 only) is populated via
286 // RenderingDataManager::updatePrimitiveData() and bound directly to the mesh shader.
287 // This function is kept as a no-op for API compatibility during transition.
288 (void)renderer; // Suppress unused parameter warning
289 }
290
292 {
293 // DEPRECATED: Meshlet data is now managed by RenderingDataManager.
294 // PrimitiveMeshletData (meshletStartIndex, meshletCount, pipelineID) is populated via
295 // RenderingDataManager::updatePrimitiveData() and stored in getPrimitiveMeshletBuffer().
296 // This function is kept as a no-op for API compatibility during transition.
297 }
298
300 // DEPRECATED: Per-object data is now managed by RenderingDataManager.
301 // Data is uploaded via RenderingDataManager::updatePrimitiveData().
302 // Return empty sync objects as no upload is performed here.
304 }
305
308 for (uint32_t eyeIndex = 0; eyeIndex < headset->getEyeCount(); ++eyeIndex) {
309 // Use the already-computed view-projection matrices from staticVertexUniformData
310 // This ensures the frustum matches what's being rendered (respects active camera)
311 // Note: updateEyeViewProjectionMatrices() must be called before this
312 const glm::mat4 eyeViewProjection = staticVertexUniformData.viewProjectionMatrices.at(eyeIndex);
313 const glm::mat4 mat = glm::transpose(eyeViewProjection); // Transpose for easier plane extraction
314
315 frustumData.planes[eyeIndex][0] = mat[3] + mat[0]; // Left plane
316 frustumData.planes[eyeIndex][1] = mat[3] - mat[0]; // Right plane
317 frustumData.planes[eyeIndex][2] = mat[3] + mat[1]; // Bottom plane
318 frustumData.planes[eyeIndex][3] = mat[3] - mat[1]; // Top plane
319 frustumData.planes[eyeIndex][4] = mat[3] + mat[2]; // Near plane
320 frustumData.planes[eyeIndex][5] = mat[3] - mat[2]; // Far plane
321
322 for (int i = 0; i < 6; ++i) {
323 const float length = glm::length(glm::vec3(frustumData.planes[eyeIndex][i]));
324 frustumData.planes[eyeIndex][i] /= length;
325 }
326 }
327
328 // For desktop mode (1 eye), duplicate eye 0's frustum to eye 1
329 // The culling shader always tests both eyes, so we need valid data in both slots
330 if (headset->getEyeCount() == 1) {
331 for (int i = 0; i < 6; ++i) {
332 frustumData.planes[1][i] = frustumData.planes[0][i];
333 }
334 }
335 }
336
338 TRACY_ZONE_SCOPED_NAMED("Upload Frustum UBO");
339
340 constexpr VkDeviceSize size = sizeof(FrustumPlanes);
341 return frustumDataBuffer->upload(context, &frustumData, size);
342 }
343
346 VkDeviceSize uniformBufferOffsetAlignment,
347 VkCommandPool graphicsCommandPool,
348 VkCommandPool transferCommandPool,
349 VkDescriptorPool descriptorPool,
350 VkDescriptorSetLayout graphicsDescriptorSetLayout,
351 uint32_t eyeIndex,
353 const Engine* engine
354 ) : renderer(renderer),
355 sceneManager(engine->getSceneManager()),
358 bufferUpdateThreadPool(20, [](std::size_t) {}, "BufferUpdate")
359 {
360 // --- Initialize view-projection data (for binding 3: ViewProjection) ---
361 // (Our shader expects 2 matrices; initialize both to identity.)
362 staticVertexUniformData.viewProjectionMatrices[0] = glm::mat4(1.0f);
363 staticVertexUniformData.viewProjectionMatrices[1] = glm::mat4(1.0f);
364
365 // --- graphics Allocate command buffer, semaphores, and fence ---
366 {
367 VkCommandBufferAllocateInfo commandBufferAllocateInfo{
368 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
369 .commandPool = graphicsCommandPool,
370 .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
371 .commandBufferCount = 1u
372 };
373 VulkanHelper::allocateCommandBuffers(context->getVkDevice(), &commandBufferAllocateInfo, &graphicsCommandBuffer, "Graphics");
374 }
375
376 // --- transfer Allocate command buffer,
377 {
378 VkCommandBufferAllocateInfo transferBufferAllocInfo{
379 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
380 .commandPool = transferCommandPool,
381 .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
382 .commandBufferCount = 1u
383 };
384 VulkanHelper::allocateCommandBuffers(context->getVkDevice(), &transferBufferAllocInfo, &transferCommandBuffer, "Transfer");
385 }
386
388 {
389 VkSemaphoreCreateInfo semaphoreCreateInfo{
390 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
391 };
392 VulkanHelper::createSemaphore(context->getVkDevice(), &semaphoreCreateInfo, nullptr, &renderFinishedSemaphore, "RenderFinished");
393 VulkanHelper::createSemaphore(context->getVkDevice(), &semaphoreCreateInfo, nullptr, &transferFinishedSemaphore, "Transfer Finished");
394 VulkanHelper::createSemaphore( context->getVkDevice(), &semaphoreCreateInfo, nullptr, &mirrorViewImageReadySemaphore, "Mirror View Image Ready" );
395
396 VkFenceCreateInfo fenceCreateInfo{
397 .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
398 .flags = VK_FENCE_CREATE_SIGNALED_BIT
399 };
400 VulkanHelper::createFence(context->getVkDevice(), &fenceCreateInfo, nullptr, &transferCompleteFence, "Transfer Complete");
401 VulkanHelper::createFence(context->getVkDevice(), &fenceCreateInfo, nullptr, &graphicsCompleteFence, "Graphics Complete");
402
403 PLOGI << "Created fences for RenderProcess (eyeIndex=" << eyeIndex << ")";
404 }
405
407 {
408 // resources for dispatch pipelines
409 createCountDispatcherBuffers();
410
411 createDispatcherDispatchBuffer();
412 createBinnedVisibleMeshletIndexBuffer();
413 createMeshletCounterBuffer();
414 createMeshletToObjectMapBuffer();
415 createViewProjectionBuffer();
416 createPerObjectSSBOBuffer();
417 createObjectMeshletDataBuffer();
418 createMeshRenderingInfoBuffers();
419 createFrustumUBOBuffer();
420 createIndirectDrawBuffer();
421 createVisibleObjectRangesBuffer();
422 createPass1CounterBuffer();
423 createBinningAllocatorBuffers();
424 createCullingFailedBuffers();
425 createHiZViewProjectionBuffer();
426 createVSIndirectDrawBuffers();
427 createVSInstancedDrawingBuffers();
428 createLodBuffers();
429
430 // Create Hi-Z pyramid for this frame (per-frame to avoid cross-frame hazards)
431 const Headset* headset = renderer->getHeadset();
432 createHiZPyramid(context->getVkDevice(), headset->getEyeResolution(0), 2u); // 2 layers for stereo
433 }
434
435 // --- Allocate descriptor set for set 0 using variable descriptor count for the texture array ---
436 const auto& renderingDataManager = renderer->getRenderingDataManager();
437 // Always allocate MAX_TEXTURE_COUNT slots to handle dynamic texture loading.
438 // Textures may be loaded after descriptor set creation, and variable descriptor count
439 // allocation cannot be resized after creation.
440 const uint32_t variableDescriptorCount = MAX_TEXTURE_COUNT;
441 assert(variableDescriptorCount <= MAX_TEXTURE_COUNT);
442 VkDescriptorSetVariableDescriptorCountAllocateInfo variableDescriptorCountAllocInfo{
443 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO,
444 .descriptorSetCount = 1u,
445 .pDescriptorCounts = &variableDescriptorCount
446 };
447
448 // --- Allocate graphics descriptor set ---
449 VkDescriptorSetAllocateInfo descriptorSetAllocateInfo{
450 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
451 .pNext = &variableDescriptorCountAllocInfo,
452 .descriptorPool = descriptorPool,
453 .descriptorSetCount = 1u,
454 .pSetLayouts = &graphicsDescriptorSetLayout,
455 };
456 VulkanHelper::allocateDescriptorSets(context->getVkDevice(), &descriptorSetAllocateInfo, &graphicsDescriptorSet, "Graphics descriptor set");
457
458 renderer->getObjectCullingComputePass().createDescriptorSet(context->getVkDevice(), getEyeIndex(), descriptorPool);
459 renderer->getBinningAllocatorComputePass().createDescriptorSet(context->getVkDevice(), getEyeIndex(), descriptorPool);
460 renderer->getMeshletUnpackingDispatchComputePass().createDescriptorSet(context->getVkDevice(), getEyeIndex(), descriptorPool);
461 renderer->getMeshletUnpackingComputePass().createDescriptorSet(context->getVkDevice(), getEyeIndex(), descriptorPool);
462 renderer->getMeshletCullingDispatchComputePass().createDescriptorSet(context->getVkDevice(), getEyeIndex(), descriptorPool);
463 renderer->getMeshletCullingComputePass().createDescriptorSet(context->getVkDevice(), getEyeIndex(), descriptorPool);
464 renderer->getDrawPreparationComputePass().createDescriptorSet(context->getVkDevice(), getEyeIndex(), descriptorPool);
465
466 // VS Instanced Drawing Pipeline (optional - may not exist if disabled)
467 try {
468 renderer->getVSBinningAllocatorComputePass().createDescriptorSet(context->getVkDevice(), getEyeIndex(), descriptorPool);
469 renderer->getVSInstanceUnpackingComputePass().createDescriptorSet(context->getVkDevice(), getEyeIndex(), descriptorPool);
470 renderer->getVSPrepareDrawComputePass().createDescriptorSet(context->getVkDevice(), getEyeIndex(), descriptorPool);
471 PLOGI << "[VS_INSTANCED] Created descriptor sets for VS instanced drawing passes (eye " << eyeIndex << ")";
472 } catch (const std::runtime_error& e) {
473 PLOGI << "[VS_INSTANCED] VS instanced drawing disabled, skipping descriptor sets";
474 }
475
476 updateRendererDescriptorSets();
477
478 updateComputeObjectCullingDescriptorSets(); // 1
479 updateComputeBinningAllocatorDescriptorSets(); // 1.5 (Stage 1: Binning Allocator)
480 updateComputeMeshletUnpackingDispatcherDescriptorSets(); // 2
481 updateComputeMeshletUnpackingDescriptorSets(); // 3 (Stage 2: Meshlet Unpacking V2)
482 updateComputeMeshletCullingDispatcherDescriptorSets(); // 4
483 updateComputeMeshletCullingDescriptorSets(); // 5
484 updateComputePrepareDrawDescriptorSets(); // 6
485 // VS Instanced Drawing Pipeline descriptor updates (handled in refreshAllDescriptorSets)
486 }
487
501 if (frustumDataBuffer) frustumDataBuffer->destroy();
503 if (allocationsBuffer) allocationsBuffer->destroy();
511
512 // VS instanced drawing buffers
520
521 // Cleanup Hi-Z pyramid
522 hiZPyramid_.cleanup(context->getVkDevice());
523
525
526 for (const auto &textureWrite: textureWritesToUpload) {
527 delete textureWrite;
528 }
529 textureWritesToUpload.clear();
530
531 if (context->getVkDevice()) {
532 // destroy semaphores
534 vkDestroySemaphore(context->getVkDevice(), transferFinishedSemaphore, nullptr);
535
537 vkDestroySemaphore(context->getVkDevice(), renderFinishedSemaphore, nullptr);
538
540 vkDestroySemaphore(context->getVkDevice(), mirrorViewImageReadySemaphore, nullptr);
541
543 vkDestroyFence(context->getVkDevice(), transferCompleteFence, nullptr);
544
546 vkDestroyFence(context->getVkDevice(), graphicsCompleteFence, nullptr);
547 }
548 }
549
551 // Check if there's an active camera override
552 CameraComponent* activeCamera = getActiveCamera();
553
554 for (size_t eye = 0; eye < headset->getEyeCount(); eye++) {
555 if (activeCamera != nullptr) {
556 // Use camera's view matrix with headset's projection matrix
557 // This allows viewing the scene from a debug camera while keeping VR projection
558 const glm::mat4 cameraView = activeCamera->getViewMatrix();
559 const glm::mat4 headsetProj = headset->getEyeProjectionMatrix(eye);
560 staticVertexUniformData.viewProjectionMatrices.at(eye) = headsetProj * cameraView;
561 } else {
562 // Default: use headset's view-projection (normal VR rendering)
563 staticVertexUniformData.viewProjectionMatrices.at(eye) = headset->getViewProjectionMatrix(eye);
564 }
565 }
566 }
567
572
574 {
576 }
577
579 {
581 }
582
586
590
593 }
594
599
601 TRACY_ZONE_SCOPED_NAMED("Update uniform buffers");
602 constexpr VkDeviceSize size = sizeof(ViewMatrixUniformData);
604 }
605
606 uint32_t RenderProcess::getEyeIndex() const {
607 return eyeIndex;
608 }
609
610 void RenderProcess::uploadNewTextures(const std::vector<Texture *> &textures, uint32_t firstIndex) const {
611 std::vector<VkDescriptorImageInfo> imageInfos(textures.size());
612 for (uint32_t i = 0; i < imageInfos.size(); i++) {
613 imageInfos.at(i).imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
614 imageInfos.at(i).sampler = textures.at(i)->getVkImageSampler();
615 imageInfos.at(i).imageView = textures.at(i)->getVkImageView();
616 }
617
618 VkWriteDescriptorSet write {
619 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
620 .dstSet = graphicsDescriptorSet,
622 .dstArrayElement = firstIndex,
623 .descriptorCount = static_cast<uint32_t>(imageInfos.size()),
624 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
625 .pImageInfo = imageInfos.data(),
626 .pBufferInfo = nullptr,
627 .pTexelBufferView = nullptr
628 };
629
630 // Call vkUpdateDescriptorSets with a single write
631 vkUpdateDescriptorSets(context->getVkDevice(), 1, &write, 0, nullptr);
632 }
633
634 void RenderProcess::addTexturesToUpload(const std::vector<Texture *> &textures,
635 uint32_t firstIndex) {
636 TRACY_ZONE_SCOPED_NAMED("Add textures to upload queue");
637 TextureWriteContainer *writeContainer = new TextureWriteContainer();
638 writeContainer->imageInfos.resize(textures.size());
639 for (uint32_t i = 0; i < writeContainer->imageInfos.size(); i++) {
640 writeContainer->imageInfos.at(i).imageLayout =
641 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
642 writeContainer->imageInfos.at(i).sampler = textures.at(i)->getVkImageSampler();
643 writeContainer->imageInfos.at(i).imageView = textures.at(i)->getVkImageView();
644 }
645
646 writeContainer->write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
647 writeContainer->write.dstSet = graphicsDescriptorSet;
648 writeContainer->write.dstBinding = ShaderStage::Graphics::TEXTURE_ARRAY;
649 // the array binding index in the descriptor set
650 writeContainer->write.dstArrayElement =
651 firstIndex; // start writing at index 4 (the 5th element)
652 writeContainer->write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
653 writeContainer->write.descriptorCount = static_cast<uint32_t>(
654 writeContainer->imageInfos.size()); // how many new descriptors to write
655 writeContainer->write.pImageInfo = writeContainer->imageInfos.data();
656 writeContainer->write.pBufferInfo = nullptr;
657 writeContainer->write.pTexelBufferView = nullptr;
658
659 textureWritesToUpload.push_back(std::move(writeContainer));
660 }
661
663 if (textureWritesToUpload.empty()) return;
664
665 TRACY_ZONE_SCOPED_NAMED("Process texture uploads");
666 std::vector<VkWriteDescriptorSet> writes(textureWritesToUpload.size());
667 for (uint32_t copyIndex = 0u; copyIndex < textureWritesToUpload.size(); copyIndex++) {
668 writes.at(copyIndex) = textureWritesToUpload.at(copyIndex)->write;
669 }
670
671 PLOGI << "Called update descriptor sets for textures";
672 vkUpdateDescriptorSets(context->getVkDevice(), static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
673
674 for (const TextureWriteContainer *writeContainer: textureWritesToUpload) {
675 delete writeContainer;
676 }
677 textureWritesToUpload.clear();
678 }
679
681 stagingBuffersForCleanup.push_back(std::move(stagingBuffer));
682 }
683
685 TRACY_ZONE_SCOPED_NAMED("Cleanup staging buffers");
687 buffer.destroy();
688 }
690 }
691
693 {
695 constexpr VkDeviceSize bufferSize = sizeof(PrimitiveMeshletData) * MAX_MESHLETS_PER_BIN;
696 objectMeshletDataBuffer.emplace();
697 objectMeshletDataBuffer->create( context, bufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT );
698 objectMeshletDataBuffer->setDebugName( "Object meshlet data ", eyeIndex );
699 }
700
702 constexpr VkDeviceSize bufferSize = sizeof(VisibleMeshletInfo) * MAX_PIPELINES * MAX_MESHLETS_PER_BIN;
704 binnedVisibleMeshletIndexBuffer->create(bufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
705 binnedVisibleMeshletIndexBuffer->setDebugName( "binnedMeshletRenderingIndices" + std::to_string(eyeIndex) );
706 }
707
709 constexpr VkDeviceSize bufferSize = sizeof(uint32_t) * MAX_PIPELINES * MAX_MESHLETS_PER_BIN;
711 meshletCounterBuffer->create(bufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
712 meshletCounterBuffer->setDebugName( "meshlet counter buffer " + std::to_string(eyeIndex) );
713 }
714
716 // Initial capacity - will grow dynamically via ensureBufferSizes()
717 constexpr uint32_t INITIAL_BUFFER_CAPACITY = 1000;
718 perObjectData.resize(INITIAL_BUFFER_CAPACITY);
719 constexpr VkDeviceSize bufferSize = INITIAL_BUFFER_CAPACITY * sizeof(PerObjectData);
720 perObjectDataBuffer.emplace();
721 perObjectDataBuffer->create(context, bufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
722 perObjectDataBuffer->setDebugName("per Object", eyeIndex);
723 }
724
726 // Initial capacity - will grow dynamically via ensureBufferSizes()
727 constexpr uint32_t INITIAL_BUFFER_CAPACITY = 1000;
728 unifiedMeshes.resize(INITIAL_BUFFER_CAPACITY);
729 constexpr VkDeviceSize bufferSize = sizeof(GpuMeshDescription) * INITIAL_BUFFER_CAPACITY;
730 meshRenderingInfoBuffer.emplace();
731 meshRenderingInfoBuffer->create(context, bufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
732 meshRenderingInfoBuffer->setDebugName("mesh Rendering Info", eyeIndex);
733 }
734
736 constexpr VkDeviceSize frustumUBOSize = sizeof(FrustumPlanes);
737 frustumDataBuffer.emplace();
738 frustumDataBuffer->create(context, frustumUBOSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
739 frustumDataBuffer->setDebugName("frustumUBO" + std::to_string(eyeIndex));
740 }
741
743 constexpr VkDeviceSize bufferSize = MAX_PIPELINES * sizeof(VkDrawMeshTasksIndirectCommandEXT);
745 indirectDrawBuffer->create(bufferSize, VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
746 indirectDrawBuffer->setDebugName( "indirect draw buffer " + std::to_string(eyeIndex) );
747 std::vector<VkDrawMeshTasksIndirectCommandEXT> cmd(MAX_PIPELINES, {1,1,1});
748 VulkanHelper::uploadDataToExistingBuffer( context->getVkDevice(), context->getVkPhysicalDevice(), renderer->getGraphicsCommandPool(), context->getGraphicsQueue(), bufferSize, &cmd, indirectDrawBuffer->getBuffer());
749 }
750
754
759
764
769
774
778
782
786
790
794
796 constexpr VkDeviceSize bufferSize = sizeof(HiZViewProjectionData);
797 hiZViewProjectionBuffer_.emplace();
798 hiZViewProjectionBuffer_->create(context, bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
799 hiZViewProjectionBuffer_->setDebugName("hiZViewProjectionUBO", eyeIndex);
800 }
801
804
805 // Use this frame's view-projection matrices for Hi-Z occlusion culling.
806 // Two-pass culling uses current VP with previous Hi-Z, catching false negatives in Pass 2.
807 for (uint32_t eye = 0; eye < 2u; ++eye) {
808 hiZViewProjectionData_.viewProjection[eye] = staticVertexUniformData.viewProjectionMatrices.at(eye);
809 }
810
811 // Update screen size and mip level info
812 const VkExtent2D eyeResolution = headset->getEyeResolution(0);
813 hiZViewProjectionData_.screenSize = glm::vec2(
814 static_cast<float>(eyeResolution.width),
815 static_cast<float>(eyeResolution.height)
816 );
817 // Use this frame's Hi-Z pyramid mip levels
819 hiZViewProjectionData_._padding = 0;
820
821 // On first frame, disable Hi-Z culling by setting mipLevels to 0
822 // The Hi-Z pyramid hasn't been generated yet
823 if (hiZFirstFrame_) {
824 hiZViewProjectionData_.hiZMipLevels = 0;
825 hiZFirstFrame_ = false;
826 }
827 }
828
830 TRACY_ZONE_SCOPED_NAMED("Upload Hi-Z ViewProjection UBO");
831
832 if (!hiZViewProjectionBuffer_.has_value()) {
834 }
835
836 constexpr VkDeviceSize size = sizeof(HiZViewProjectionData);
838 }
839
843
845 {
846 return indirectDrawBuffer.value();
847 }
848
852
854 return pass1CounterBuffer.value();
855 }
856
861
870 void RenderProcess::setTransferFenceSubmitted(const bool isSubmitted)
871 {
872 transferFenceSubmitted = isSubmitted;
873 }
874 void RenderProcess::setGraphicsFenceSubmitted(const bool isSubmitted)
875 {
876 graphicsFenceSubmitted = isSubmitted;
877 }
886
888 constexpr VkDeviceSize bufferSize = sizeof(Dispatch);
890 dispatcherDispatchBuffer->create(bufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT );
891 dispatcherDispatchBuffer->setDebugName("dispatcher Dispatch Buffer");
892 }
893
895 constexpr VkDeviceSize counterBufferSize = sizeof(uint32_t);
896 constexpr VkDeviceSize dispatchBufferSize = sizeof(Dispatch);
899 countDispatcherCounter->create(counterBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
900 countDispatcherDispatchBuffer->create(dispatchBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
901 countDispatcherCounter->setDebugName("countDispatcherCounter");
902 countDispatcherDispatchBuffer->setDebugName("countDispatcherDispatchBuffer");
903 }
904
906 // Initial capacity - will grow dynamically via ensureBufferSizes()
907 constexpr uint32_t INITIAL_BUFFER_CAPACITY = 1000;
908 constexpr VkDeviceSize bufferSize = sizeof(VisibleMeshletRange) * INITIAL_BUFFER_CAPACITY;
910 visibleObjectRangesBuffer->create(bufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
911 visibleObjectRangesBuffer->setDebugName("visibleObjectRanges" + std::to_string(eyeIndex));
912 }
913
915 constexpr VkDeviceSize bufferSize = sizeof(uint32_t) * 2; // objectCount + totalMeshletCount
917 pass1CounterBuffer->create(bufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
918 pass1CounterBuffer->setDebugName("pass1Counters" + std::to_string(eyeIndex));
919 }
920
922 constexpr VkDeviceSize viewProjBufferSize = sizeof(ViewMatrixUniformData);
923 viewProjectionBuffer.emplace();
924 viewProjectionBuffer->create(context, viewProjBufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
925 viewProjectionBuffer->setDebugName("View projection buffer" + std::to_string(eyeIndex));
926 }
927
929 constexpr VkDeviceSize bufferSize = sizeof(uint32_t) * MAX_MESHLETS_PER_BIN;
930 meshletToObjectMapBuffer.emplace();
931 meshletToObjectMapBuffer->create(context, bufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
932 meshletToObjectMapBuffer->setDebugName("meshletToObjectMap Buffer" + std::to_string(eyeIndex));
933 }
934
936 // PrimitiveAllocation struct size: 32 bytes (8 * sizeof(uint32_t))
937 // Includes: primitiveID, pipelineID, binWriteOffset, meshletStartIndex, meshletCount, ditherFactor, _padding[2]
938 // Initial capacity - will grow dynamically via ensureBufferSizes()
939 constexpr uint32_t INITIAL_BUFFER_CAPACITY = 1000;
940 constexpr VkDeviceSize allocationStructSize = 32; // 8 * sizeof(uint32_t) = 32 bytes
941 constexpr VkDeviceSize allocationsBufferSize = allocationStructSize * INITIAL_BUFFER_CAPACITY;
942
943 allocationsBuffer.emplace(context);
944 allocationsBuffer->create(allocationsBufferSize,
945 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
946 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
947 allocationsBuffer->setDebugName("allocationsBuffer" + std::to_string(eyeIndex));
948
949 // Pipeline counters: one uint32_t per pipeline for atomic counting
950 constexpr VkDeviceSize pipelineCountersSize = sizeof(uint32_t) * MAX_PIPELINES;
952 pipelineCountersBuffer->create(pipelineCountersSize,
953 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
954 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
955 pipelineCountersBuffer->setDebugName("pipelineCountersBuffer" + std::to_string(eyeIndex));
956
957 // Pipeline bin offsets: prefix sum of pipeline counters (one uint32_t per pipeline)
958 constexpr VkDeviceSize binOffsetsSize = sizeof(uint32_t) * MAX_PIPELINES;
960 pipelineBinOffsetsBuffer->create(binOffsetsSize,
961 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
962 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
963 pipelineBinOffsetsBuffer->setDebugName("pipelineBinOffsetsBuffer" + std::to_string(eyeIndex));
964 }
965
967 return allocationsBuffer.value();
968 }
969
973
977
979 // Two-pass occlusion culling: buffer to store object IDs that failed Pass 1 Hi-Z test
980 // Initial capacity - will grow dynamically via ensureBufferSizes()
981 constexpr uint32_t INITIAL_BUFFER_CAPACITY = 1000;
982 constexpr VkDeviceSize failedBufferSize = sizeof(uint32_t) * INITIAL_BUFFER_CAPACITY;
983
985 cullingFailedBuffer->create(failedBufferSize,
986 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
987 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
988 cullingFailedBuffer->setDebugName("cullingFailedBuffer" + std::to_string(eyeIndex));
989
990 // Counter for failed objects (single uint32_t, used atomically)
991 constexpr VkDeviceSize counterSize = sizeof(uint32_t);
993 cullingFailedCounterBuffer->create(counterSize,
994 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT,
995 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
996 cullingFailedCounterBuffer->setDebugName("cullingFailedCounterBuffer" + std::to_string(eyeIndex));
997 }
998
1000 return cullingFailedBuffer.value();
1001 }
1002
1006
1008 // Vertex shader path: indirect draw buffer for single-meshlet geometry
1009 // VkDrawIndexedIndirectCommand is 20 bytes (5 x uint32_t)
1010 // Initial capacity for 10000 single-meshlet draws - will grow via ensureBufferSizes()
1011 constexpr uint32_t INITIAL_DRAW_CAPACITY = 10000;
1012 constexpr VkDeviceSize drawBufferSize = sizeof(VkDrawIndexedIndirectCommand) * INITIAL_DRAW_CAPACITY;
1013
1015 vsIndirectDrawBuffer_->create(drawBufferSize,
1016 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
1017 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1018 vsIndirectDrawBuffer_->setDebugName("vsIndirectDrawBuffer" + std::to_string(eyeIndex));
1019
1020 // Draw count buffer - single uint32_t for indirect draw count
1021 constexpr VkDeviceSize countBufferSize = sizeof(uint32_t);
1023 vsIndirectDrawCountBuffer_->create(countBufferSize,
1024 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
1025 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1026 vsIndirectDrawCountBuffer_->setDebugName("vsIndirectDrawCountBuffer" + std::to_string(eyeIndex));
1027
1028 PLOGI << "[VS_PATH] Created VS indirect draw buffers for frame " << eyeIndex;
1029 }
1030
1034
1038
1040 // VS Instanced Drawing Pipeline Buffers
1041 // This pipeline reduces ~5k draw calls to ~1 by batching instances by geometry type
1042
1043 constexpr uint32_t INITIAL_INSTANCE_CAPACITY = 10000;
1044 constexpr uint32_t MAX_GEOMETRY_TYPES = 256;
1045 constexpr uint32_t MAX_VS_INSTANCES_PER_GEO = 16384;
1046
1047 // Stage 0: Culling outputs primitive IDs (replaces draw commands)
1048 // vsVisibleInstancesBuffer_: uint array of primitive IDs
1049 constexpr VkDeviceSize visibleInstancesSize = sizeof(uint32_t) * INITIAL_INSTANCE_CAPACITY;
1051 vsVisibleInstancesBuffer_->create(visibleInstancesSize,
1052 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
1053 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1054 vsVisibleInstancesBuffer_->setDebugName("vsVisibleInstancesBuffer" + std::to_string(eyeIndex));
1055
1056 // vsVisibleCountBuffer_: single uint32_t for visible count
1057 constexpr VkDeviceSize countSize = sizeof(uint32_t);
1059 vsVisibleCountBuffer_->create(countSize,
1060 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
1061 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1062 vsVisibleCountBuffer_->setDebugName("vsVisibleCountBuffer" + std::to_string(eyeIndex));
1063
1064 // Stage 1: VSBinningAllocator outputs
1065 // vsInstanceAllocationsBuffer_: (primitiveID, geoIndex, writeOffset) per survivor
1066 constexpr VkDeviceSize allocationStructSize = 3 * sizeof(uint32_t); // VSInstanceAllocation
1067 constexpr VkDeviceSize allocationsBufferSize = allocationStructSize * INITIAL_INSTANCE_CAPACITY;
1069 vsInstanceAllocationsBuffer_->create(allocationsBufferSize,
1070 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
1071 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1072 vsInstanceAllocationsBuffer_->setDebugName("vsInstanceAllocationsBuffer" + std::to_string(eyeIndex));
1073
1074 // vsGeometryCountersBuffer_: atomic instance count per geometry type
1075 constexpr VkDeviceSize geometryCountersSize = sizeof(uint32_t) * MAX_GEOMETRY_TYPES;
1077 vsGeometryCountersBuffer_->create(geometryCountersSize,
1078 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
1079 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1080 vsGeometryCountersBuffer_->setDebugName("vsGeometryCountersBuffer" + std::to_string(eyeIndex));
1081
1082 // Stage 2: VSInstanceUnpacking outputs
1083 // vsInstanceIdsBuffer_: Instance ID lookup for vertex shader
1084 // Layout: [Geo0 slots][Geo1 slots]... with MAX_VS_INSTANCES_PER_GEO slots each
1085 constexpr VkDeviceSize instanceIdsSize = sizeof(uint32_t) * MAX_GEOMETRY_TYPES * MAX_VS_INSTANCES_PER_GEO;
1087 vsInstanceIdsBuffer_->create(instanceIdsSize,
1088 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
1089 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1090 vsInstanceIdsBuffer_->setDebugName("vsInstanceIdsBuffer" + std::to_string(eyeIndex));
1091
1092 // Stage 3: VSPrepareDraw outputs
1093 // vsDrawCommandsBuffer_: VkDrawIndexedIndirectCommand array (one per geometry with instances)
1094 constexpr VkDeviceSize drawCommandsSize = sizeof(VkDrawIndexedIndirectCommand) * MAX_GEOMETRY_TYPES;
1096 vsDrawCommandsBuffer_->create(drawCommandsSize,
1097 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
1098 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1099 vsDrawCommandsBuffer_->setDebugName("vsDrawCommandsBuffer" + std::to_string(eyeIndex));
1100
1101 // vsDrawCountBuffer_: number of draw commands generated
1102 vsDrawCountBuffer_.emplace(context);
1103 vsDrawCountBuffer_->create(countSize,
1104 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
1105 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1106 vsDrawCountBuffer_->setDebugName("vsDrawCountBuffer" + std::to_string(eyeIndex));
1107
1108 PLOGI << "[VS_PATH_INSTANCED] Created VS instanced drawing buffers for frame " << eyeIndex;
1109 }
1110
1114
1118
1122
1126
1130
1134
1136 return vsDrawCountBuffer_.value();
1137 }
1138
1140 // LOD cluster-based selection buffers
1141 // These support Nanite-style per-cluster LOD selection
1142
1143 constexpr uint32_t INITIAL_CLUSTER_SURVIVOR_CAPACITY = 100000;
1144
1145 // lodClusterSurvivorsBuffer_: ClusterSurvivor output from culling shader
1146 // ClusterSurvivor = {primitiveID, clusterIndex, ditherFactor, padding} = 16 bytes
1147 constexpr VkDeviceSize survivorsSize = sizeof(ClusterSurvivor) * INITIAL_CLUSTER_SURVIVOR_CAPACITY;
1149 lodClusterSurvivorsBuffer_->create(survivorsSize,
1150 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
1151 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1152 lodClusterSurvivorsBuffer_->setDebugName("lodClusterSurvivorsBuffer" + std::to_string(eyeIndex));
1153
1154 // lodClusterSurvivorCountBuffer_: atomic counter for LOD cluster survivors
1155 constexpr VkDeviceSize counterSize = sizeof(uint32_t);
1157 lodClusterSurvivorCountBuffer_->create(counterSize,
1158 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT,
1159 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1160 lodClusterSurvivorCountBuffer_->setDebugName("lodClusterSurvivorCountBuffer" + std::to_string(eyeIndex));
1161
1162 // lodConfigBuffer_: LodConfigUBO for camera/screen info (uniform buffer)
1163 lodConfigBuffer_.emplace();
1164 lodConfigBuffer_->create(context, sizeof(LodConfigUBO),
1165 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
1166 true /* persistentMapping */);
1167 lodConfigBuffer_->setDebugName("lodConfigBuffer" + std::to_string(eyeIndex));
1168
1169 PLOGI << "[LOD] Created LOD cluster selection buffers for frame " << eyeIndex;
1170 }
1171
1175
1179
1181 return lodConfigBuffer_.value();
1182 }
1183
1184 void RenderProcess::updateLodConfig(const glm::vec3& cameraPosition, const glm::mat4& projMatrix,
1185 float nearPlane, float screenWidth, float screenHeight,
1186 float errorThresholdPixels, bool ditherEnabled) {
1187 lodConfigData_.errorThresholdPixels = errorThresholdPixels;
1188 lodConfigData_.cameraProj11 = projMatrix[1][1]; // cot(fov/2)
1189 lodConfigData_.cameraNear = nearPlane;
1190 lodConfigData_.ditherEnabled = ditherEnabled ? 1u : 0u;
1191 lodConfigData_.cameraPosition = glm::vec4(cameraPosition, 1.0f);
1192 lodConfigData_.screenSize = glm::vec2(screenWidth, screenHeight);
1193 lodConfigData_._padding = glm::vec2(0.0f);
1194 }
1195
1199
1200 bool RenderProcess::ensureBufferSizes(const uint32_t primitiveCount)
1201 {
1202 bool anyRecreated = false;
1203 constexpr VkMemoryPropertyFlags deviceLocal = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
1204 constexpr VkBufferUsageFlags storageUsage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
1205
1206 // perObjectDataBuffer: stores PerObjectData per primitive
1207 const VkDeviceSize perObjectSize = primitiveCount * sizeof(PerObjectData);
1208 if (perObjectDataBuffer.has_value()) {
1209 anyRecreated |= perObjectDataBuffer->ensureSize(perObjectSize, storageUsage);
1210 }
1211
1212 // meshRenderingInfoBuffer: stores GpuMeshDescription per primitive
1213 const VkDeviceSize meshRenderingSize = primitiveCount * sizeof(GpuMeshDescription);
1214 if (meshRenderingInfoBuffer.has_value()) {
1215 anyRecreated |= meshRenderingInfoBuffer->ensureSize(meshRenderingSize, storageUsage);
1216 }
1217
1218 // visibleObjectRangesBuffer: stores VisibleMeshletRange per primitive
1219 const VkDeviceSize visibleRangesSize = primitiveCount * sizeof(VisibleMeshletRange);
1220 if (visibleObjectRangesBuffer.has_value()) {
1221 anyRecreated |= visibleObjectRangesBuffer->ensureSize(visibleRangesSize, storageUsage, deviceLocal);
1222 }
1223
1224 // allocationsBuffer: stores PrimitiveAllocation (32 bytes) per allocation
1225 // With LOD, allocations = primitiveCount (regular) + clusterCount (LOD clusters)
1226 // PrimitiveAllocation: primitiveID, pipelineID, binWriteOffset, meshletStartIndex, meshletCount, ditherFactor, _padding[2]
1227 constexpr VkDeviceSize allocationStructSize = 32; // 8 * uint32_t = 32 bytes
1228 const uint32_t clusterCount = renderer->getRenderingDataManager()->getClusterCount();
1229 const VkDeviceSize allocationsSize = (primitiveCount + clusterCount) * allocationStructSize;
1230 if (allocationsBuffer.has_value()) {
1231 anyRecreated |= allocationsBuffer->ensureSize(allocationsSize,
1232 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, deviceLocal);
1233 }
1234
1235 // cullingFailedBuffer: stores uint32_t object IDs that failed Pass 1 Hi-Z test
1236 const VkDeviceSize failedBufferSize = primitiveCount * sizeof(uint32_t);
1237 if (cullingFailedBuffer.has_value()) {
1238 anyRecreated |= cullingFailedBuffer->ensureSize(failedBufferSize,
1239 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, deviceLocal);
1240 }
1241
1242 // vsIndirectDrawBuffer: stores VkDrawIndexedIndirectCommand for VS path
1243 // Size based on primitiveCount since all primitives could potentially be single-meshlet
1244 const VkDeviceSize vsDrawBufferSize = primitiveCount * sizeof(VkDrawIndexedIndirectCommand);
1245 if (vsIndirectDrawBuffer_.has_value()) {
1246 anyRecreated |= vsIndirectDrawBuffer_->ensureSize(vsDrawBufferSize,
1247 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, deviceLocal);
1248 }
1249
1250 // Resize CPU-side vectors to match
1251 if (perObjectData.size() < primitiveCount) {
1252 perObjectData.resize(primitiveCount);
1253 }
1254 if (unifiedMeshes.size() < primitiveCount) {
1255 unifiedMeshes.resize(primitiveCount);
1256 }
1257
1258 if (anyRecreated) {
1259 PLOGI << "RenderProcess " << eyeIndex << ": Buffers resized for " << primitiveCount << " primitives";
1260 }
1261
1262 return anyRecreated;
1263 }
1264
1265 // ============================================================================
1266 // Hi-Z Pyramid Implementation
1267 // ============================================================================
1268
1270 {
1271 if (device == VK_NULL_HANDLE) return;
1272
1273 for (VkImageView mipView : mipViews) {
1274 if (mipView != VK_NULL_HANDLE) {
1275 vkDestroyImageView(device, mipView, nullptr);
1276 }
1277 }
1278 mipViews.clear();
1279
1280 if (fullView != VK_NULL_HANDLE) {
1281 vkDestroyImageView(device, fullView, nullptr);
1282 fullView = VK_NULL_HANDLE;
1283 }
1284
1285 if (image != VK_NULL_HANDLE) {
1286 vkDestroyImage(device, image, nullptr);
1287 image = VK_NULL_HANDLE;
1288 }
1289
1290 if (deviceMemory != VK_NULL_HANDLE) {
1291 vkFreeMemory(device, deviceMemory, nullptr);
1292 deviceMemory = VK_NULL_HANDLE;
1293 }
1294
1295 mipLevels = 0;
1296 extent = {};
1297 }
1298
1299 void RenderProcess::createHiZPyramid(VkDevice device, VkExtent2D resolution, uint32_t arrayLayers)
1300 {
1301 PLOGI << "Creating Hi-Z pyramid for frame " << eyeIndex << " with resolution "
1302 << resolution.width << "x" << resolution.height;
1303
1304 // Calculate number of mip levels needed
1305 hiZPyramid_.extent = resolution;
1306 hiZPyramid_.mipLevels = static_cast<uint32_t>(
1307 std::floor(std::log2(std::max(resolution.width, resolution.height)))) + 1;
1308
1309 PLOGI << "Hi-Z pyramid will have " << hiZPyramid_.mipLevels << " mip levels";
1310
1311 // Create Hi-Z image with all mip levels
1312 VkImageCreateInfo imageCreateInfo{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
1313 imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
1314 imageCreateInfo.extent.width = resolution.width;
1315 imageCreateInfo.extent.height = resolution.height;
1316 imageCreateInfo.extent.depth = 1u;
1317 imageCreateInfo.mipLevels = hiZPyramid_.mipLevels;
1318 imageCreateInfo.arrayLayers = arrayLayers; // 2 for stereo
1319 imageCreateInfo.format = VK_FORMAT_R32_SFLOAT; // Single float for depth
1320 imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1321 imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1322 imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT;
1323 imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1324 imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1325
1326 PLOG_FN_VK(vkCreateImage(device, &imageCreateInfo, nullptr, &hiZPyramid_.image));
1328 "Hi-Z Pyramid Image Frame " + std::to_string(eyeIndex));
1329
1330 // Allocate memory
1331 VkMemoryRequirements memRequirements;
1332 vkGetImageMemoryRequirements(device, hiZPyramid_.image, &memRequirements);
1333
1334 uint32_t memoryTypeIndex = 0u;
1335 VkMemoryRequirements2 memReqs2{VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2};
1336 memReqs2.memoryRequirements = memRequirements;
1337 if (!VulkanHelper::findSuitableMemoryType(context->getVkPhysicalDevice(), memReqs2,
1338 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, memoryTypeIndex))
1339 {
1340 THROW_ERROR("Could not find suitable memory type for Hi-Z pyramid");
1341 }
1342
1343 VkMemoryAllocateInfo allocInfo{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
1344 allocInfo.allocationSize = memRequirements.size;
1345 allocInfo.memoryTypeIndex = memoryTypeIndex;
1346
1347 PLOG_FN_VK(vkAllocateMemory(device, &allocInfo, nullptr, &hiZPyramid_.deviceMemory));
1348 VulkanHelper::setObjectName(device, hiZPyramid_.deviceMemory,
1349 "Hi-Z Pyramid Memory Frame " + std::to_string(eyeIndex));
1350
1351 PLOG_FN_VK(vkBindImageMemory(device, hiZPyramid_.image, hiZPyramid_.deviceMemory, 0));
1352
1353 // Create full image view (all mip levels) for sampling
1354 VkImageViewCreateInfo viewInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
1355 viewInfo.image = hiZPyramid_.image;
1356 viewInfo.viewType = (arrayLayers == 1u ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY);
1357 viewInfo.format = VK_FORMAT_R32_SFLOAT;
1358 viewInfo.components = {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
1359 VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
1360 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1361 viewInfo.subresourceRange.baseMipLevel = 0;
1362 viewInfo.subresourceRange.levelCount = hiZPyramid_.mipLevels;
1363 viewInfo.subresourceRange.baseArrayLayer = 0;
1364 viewInfo.subresourceRange.layerCount = arrayLayers;
1365
1366 PLOG_FN_VK(vkCreateImageView(device, &viewInfo, nullptr, &hiZPyramid_.fullView));
1368 "Hi-Z Pyramid Full View Frame " + std::to_string(eyeIndex));
1369
1370 // Create per-mip image views for compute shader writes
1371 hiZPyramid_.mipViews.resize(hiZPyramid_.mipLevels);
1372 for (uint32_t mip = 0; mip < hiZPyramid_.mipLevels; mip++)
1373 {
1374 VkImageViewCreateInfo mipViewInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
1375 mipViewInfo.image = hiZPyramid_.image;
1376 mipViewInfo.viewType = (arrayLayers == 1u ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY);
1377 mipViewInfo.format = VK_FORMAT_R32_SFLOAT;
1378 mipViewInfo.components = {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
1379 VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
1380 mipViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1381 mipViewInfo.subresourceRange.baseMipLevel = mip;
1382 mipViewInfo.subresourceRange.levelCount = 1;
1383 mipViewInfo.subresourceRange.baseArrayLayer = 0;
1384 mipViewInfo.subresourceRange.layerCount = arrayLayers;
1385
1386 PLOG_FN_VK(vkCreateImageView(device, &mipViewInfo, nullptr, &hiZPyramid_.mipViews[mip]));
1387 VulkanHelper::setObjectName(device, hiZPyramid_.mipViews[mip],
1388 "Hi-Z Pyramid Mip " + std::to_string(mip) + " View Frame " + std::to_string(eyeIndex));
1389 }
1390
1391 PLOGI << "Hi-Z pyramid created successfully for frame " << eyeIndex;
1392 }
1393
1394} // namespace EngineCore
#define GRAPHICS_GUARD()
Definition Renderer.cpp:64
#define COMPUTE_GUARD()
Definition Renderer.cpp:63
constexpr uint32_t MAX_PIPELINES
Definition Settings.h:39
constexpr uint32_t MAX_MESHLETS_PER_BIN
Definition Settings.h:37
constexpr uint32_t MAX_TEXTURE_COUNT
Definition Settings.h:35
#define TRACY_ZONE_SCOPED_FUNCTION
#define TRACY_ZONE_SCOPED_NAMED(name)
The application context is the core class which stores the basic openxr and vulkan objects.
A camera component that provides view and projection matrices based on its SceneNode transform....
glm::mat4 getViewMatrix() const
Gets the view matrix computed from the camera's world transform.
The Descriptor set updater is used to create a list of descriptor set writes which are executed with ...
void update(VkDevice device) const
Updates the bindings on a descriptor sets.
DescriptorSetUpdater & addUniformBuffer(uint32_t binding, const VkDescriptorBufferInfo *pBufferInfo)
Adds a uniform buffer (glsl std140 buffer) to the chain of updates to execute.
DescriptorSetUpdater & addStorageBuffer(uint32_t binding, const VkDescriptorBufferInfo *pBufferInfo)
Adds a storage buffer (glsl std430 buffer) to the chain of updates to execute.
DescriptorSetUpdater & addTextureArrayIf(const bool condition, uint32_t binding, std::vector< VkDescriptorImageInfo > *imageInfos)
Adds updates to texture array.
VkSampler getHiZSampler() const
Definition Headset.h:327
VkExtent2D getEyeResolution(size_t eyeIndex) const
Gets eye resolution in x and y for a specified eye index. 0 is left.
Definition Headset.cpp:953
glm::mat4 getViewProjectionMatrix(size_t eyeIndex) const
Gets you a matrix of the multiplied view and projection matrix.
Definition Headset.cpp:994
glm::mat4 getEyeProjectionMatrix(size_t eyeIndex) const
Gets the projection matrix for an eye.
Definition Headset.cpp:989
uint32_t getEyeCount() const
Gets eye count.
Definition Headset.cpp:948
VkCommandBuffer graphicsCommandBuffer
const VulkanStagedBuffer & getLodConfigBuffer() const
VkSemaphore mirrorViewImageReadySemaphore
BS_tracy::tracy_thread_pool< BS_tracy::tp::none > bufferUpdateThreadPool
std::optional< VulkanBuffer > vsInstanceIdsBuffer_
Stage 2: VSInstanceUnpacking outputs.
void updateComputeVSBinningAllocatorDescriptorSets() const
std::optional< VulkanBuffer > dispatcherDispatchBuffer
const VulkanBuffer & getMeshletCounterBuffer() const
const VulkanBuffer & getCullingFailedBuffer() const
Two-pass occlusion culling buffer getters.
std::optional< VulkanStagedBuffer > meshRenderingInfoBuffer
VkCommandBuffer & getGraphicsCommandBuffer()
void updateComputeVSPrepareDrawDescriptorSets() const
void updateComputeMeshletCullingDescriptorSets() const
std::optional< VulkanStagedBuffer > perObjectDataBuffer
const VulkanBuffer & getVisibleObjectRangesBuffer() const
const VulkanStagedBuffer & getFrustumUBOBuffer() const
bool ensureBufferSizes(uint32_t primitiveCount)
Ensures RenderProcess buffers have sufficient capacity for the given primitive count Dynamically resi...
const VulkanBuffer & getIndirectDrawBuffer() const
std::vector< TextureWriteContainer * > textureWritesToUpload
void updateComputeObjectCullingDescriptorSets(VkImageView previousFrameHiZView=VK_NULL_HANDLE) const
Updates object culling descriptor sets.
const std::unique_ptr< SceneManager > & sceneManager
std::optional< VulkanBuffer > vsGeometryCountersBuffer_
Atomic instance count per geometry type.
std::optional< VulkanBuffer > vsDrawCountBuffer_
Number of draw commands.
ApplicationContext * context
const VulkanBuffer & getPipelineCountersBuffer() const
std::optional< VulkanBuffer > allocationsBuffer
Binning allocator stage buffers (Stage 1)
struct EngineCore::RenderProcess::ViewMatrixUniformData staticVertexUniformData
VkSemaphore transferFinishedSemaphore
const VulkanBuffer & getVSInstanceIdsBuffer() const
void updateFrustumUBOData(const Headset *headset)
std::vector< PrimitiveMeshletData > objectMeshletData
void updateEyeViewProjectionMatrices(const Headset *headset)
writes a view projection matrix to an eye
std::vector< PerObjectData > perObjectData
Rendering resource.
const VulkanBuffer & getVSDrawCommandsBuffer() const
const VulkanBuffer & getLodClusterSurvivorsBuffer() const
LOD cluster-based selection buffer getters.
VkImageView getHiZPyramidFullView() const
bool getGraphicsFenceSubmitted() const
VkDescriptorSet graphicsDescriptorSet
uint32_t getHiZMipLevels() const
std::vector< GpuMeshDescription > unifiedMeshes
VulkanStagedBufferSyncObjects uploadMeshRenderingStagingData()
const VulkanBuffer & getCullingFailedCounterBuffer() const
VkSemaphore getRenderFinishedSemaphore() const
std::optional< VulkanBuffer > vsIndirectDrawCountBuffer_
Legacy: was draw count.
void createHiZPyramid(VkDevice device, VkExtent2D resolution, uint32_t arrayLayers)
const VulkanStagedBuffer & getHiZViewProjectionBuffer() const
VulkanStagedBufferSyncObjects uploadLodConfigBuffer()
Upload LOD config buffer to GPU.
std::optional< VulkanBuffer > cullingFailedBuffer
Two-pass occlusion culling buffers.
std::optional< VulkanBuffer > vsVisibleCountBuffer_
Number of visible VS path primitives.
void addTexturesToUpload(const std::vector< Texture * > &textures, uint32_t firstIndex)
void uploadNewTextures(const std::vector< Texture * > &textures, uint32_t firstIndex) const
const VulkanBuffer & getVSIndirectDrawCountBuffer() const
VulkanStagedBuffer & getMeshletToObjectMapBuffer()
const VulkanBuffer & getLodClusterSurvivorCountBuffer() const
FrustumPlanes frustumData
Rendering Resource.
RenderProcess(ApplicationContext *context, VkDeviceSize uniformBufferOffsetAlignment, VkCommandPool graphicsCommandPool, VkCommandPool transferCommandPool, VkDescriptorPool descriptorPool, VkDescriptorSetLayout graphicsDescriptorSetLayout, uint32_t eyeIndex, Renderer *renderer, const Engine *engine)
std::optional< VulkanStagedBuffer > objectMeshletDataBuffer
const VulkanBuffer & getAllocationsBuffer() const
Binning allocator stage buffer getters.
void updateLodConfig(const glm::vec3 &cameraPosition, const glm::mat4 &projMatrix, float nearPlane, float screenWidth, float screenHeight, float errorThresholdPixels=1.0f, bool ditherEnabled=true)
Update LOD configuration with camera and screen info Call this once per frame before dispatching prim...
void updateHiZViewProjectionData(const Headset *headset)
Updates Hi-Z view-projection data for occlusion culling Must be called after updateEyeViewProjectionM...
std::optional< VulkanBuffer > vsDrawCommandsBuffer_
Stage 3: VSPrepareDraw outputs.
bool getTransferFenceSubmitted() const
VulkanBuffer & getBinnedVisibleMeshletIndexBuffer()
const VulkanBuffer & getVSInstanceAllocationsBuffer() const
std::optional< VulkanBuffer > vsVisibleInstancesBuffer_
Primitive IDs from culling (uint array)
void setGraphicsFenceSubmitted(bool isSubmitted)
std::optional< VulkanBuffer > binnedVisibleMeshletIndexBuffer
const VulkanBuffer & getPass1CounterBuffer() const
std::optional< VulkanBuffer > pipelineCountersBuffer
Per-pipeline meshlet counters (atomic)
const VulkanBuffer & getVSGeometryCountersBuffer() const
std::optional< VulkanBuffer > pipelineBinOffsetsBuffer
Prefix sum of pipeline counters.
VkDescriptorSet getGraphicsDescriptorSet() const
std::optional< VulkanBuffer > countDispatcherDispatchBuffer
VulkanStagedBufferSyncObjects uploadSSBOStagingBuffer()
std::optional< VulkanStagedBuffer > lodConfigBuffer_
LodConfigUBO for camera/screen info.
void updateSSBO(const Renderer *renderer)
VkCommandBuffer transferCommandBuffer
VulkanStagedBufferSyncObjects uploadUniformBufferData()
const VulkanBuffer & getVSVisibleCountBuffer() const
StaticFragmentUniformData staticFragmentUniformData
const VulkanBuffer & getVSDrawCountBuffer() const
void updateComputeVSInstanceUnpackingDescriptorSets() const
std::optional< VulkanBuffer > vsInstanceAllocationsBuffer_
Stage 1: VSBinningAllocator outputs.
VkCommandBuffer & getTransferCommandBuffer()
HiZViewProjectionData hiZViewProjectionData_
Hi-Z view-projection data for occlusion culling (per-frame to avoid races)
void updateComputeMeshletCullingDispatcherDescriptorSets() const
const VulkanStagedBuffer & getObjectMeshletDataBuffer() const
std::optional< VulkanBuffer > indirectDrawBuffer
void addStagingBufferForCleanup(VulkanBuffer &&stagingBuffer)
std::vector< VulkanBuffer > stagingBuffersForCleanup
void updateComputePrepareDrawDescriptorSets() const
std::optional< VulkanStagedBuffer > hiZViewProjectionBuffer_
void refreshAllDescriptorSets()
Refreshes all descriptor sets after buffer recreation Call this when RenderingDataManager buffers hav...
std::optional< VulkanBuffer > meshletCounterBuffer
void updateFragmentTime(float time)
Sets the time value for time based fragment shader effects.
std::optional< VulkanBuffer > vsIndirectDrawBuffer_
Legacy: was VkDrawIndexedIndirectCommand array.
VulkanStagedBufferSyncObjects uploadFrustumUBO()
void cleanup()
Cleans up all artifacts of the class.
std::optional< VulkanBuffer > lodClusterSurvivorsBuffer_
LOD cluster-based selection buffers.
void setTransferFenceSubmitted(bool isSubmitted)
std::optional< VulkanBuffer > cullingFailedCounterBuffer
Atomic counter for failed objects.
const VulkanStagedBuffer & getPerObjectSSBOBuffer() const
LodConfigUBO lodConfigData_
CPU-side LOD config data.
VkSemaphore getMirrorViewImageSemaphore() const
std::optional< VulkanBuffer > countDispatcherCounter
void updateComputeBinningAllocatorDescriptorSets() const
VkSemaphore renderFinishedSemaphore
std::optional< VulkanStagedBuffer > meshletToObjectMapBuffer
const VulkanBuffer & getPipelineBinOffsetsBuffer() const
std::optional< VulkanStagedBuffer > viewProjectionBuffer
std::optional< VulkanBuffer > pass1CounterBuffer
const VulkanStagedBuffer & getMeshRenderingBuffer() const
void syncWithRenderingDataManager(RenderingDataManager *rdm)
Sync descriptor sets with RenderingDataManager if data has changed Compares version numbers and updat...
VulkanStagedBuffer & getViewProjectionBuffer()
void updateComputeMeshletUnpackingDescriptorSets() const
const VulkanBuffer & getVSVisibleInstancesBuffer() const
Vertex shader path buffer getters (instanced drawing pipeline)
std::optional< VulkanBuffer > visibleObjectRangesBuffer
void updateComputeMeshletUnpackingDispatcherDescriptorSets() const
std::optional< VulkanStagedBuffer > frustumDataBuffer
VkSemaphore getTransferFinishedSemaphore() const
const VulkanBuffer & getVSIndirectDrawBuffer() const
Legacy VS path buffer getters (backwards compatibility)
std::optional< VulkanBuffer > lodClusterSurvivorCountBuffer_
Atomic counter for LOD cluster survivors.
VulkanStagedBufferSyncObjects uploadHiZViewProjectionUBO()
Uploads Hi-Z view-projection UBO to GPU.
The rendering data manager is supposed to hold all methods to update the contents of the buffers whic...
uint64_t getDataVersion() const
Get the current data version Incremented each time buffers are updated. RenderProcesses use this to k...
RAII wrapper for Vulkan buffer and device memory.
VkBuffer getBuffer() const
static bool findSuitableMemoryType(VkPhysicalDevice physicalDevice, VkMemoryRequirements2 requirements, VkMemoryPropertyFlags properties, uint32_t &out_typeIndex)
static void createFence(VkDevice device, const VkFenceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFence *pFence, const std::string &name)
static VkResult allocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo *pAllocateInfo, VkCommandBuffer *pCommandBuffers, const std::string &name)
static VkResult createSemaphore(VkDevice device, const VkSemaphoreCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSemaphore *pSemaphore, const std::string &name)
static void uploadDataToExistingBuffer(VkDevice device, VkPhysicalDevice physicalDevice, VkCommandPool commandPool, VkQueue queue, VkDeviceSize size, const void *dataPtr, VkBuffer targetBuffer)
static void setObjectName(VkDevice device, VulkanObjectType objectHandle, const std::string &name)
static VkResult allocateDescriptorSets(VkDevice device, const VkDescriptorSetAllocateInfo *pAllocateInfo, VkDescriptorSet *pDescriptorSets, const std::string &name)
static VkDescriptorBufferInfo fullBufferInfo(VkBuffer buffer)
Log category system implementation.
CameraComponent * getActiveCamera()
Gets the currently active camera.
@ MOVABLE_DIFFUSE_SHADER
STL namespace.
Cluster survivor data passed from culling to binning shaders. Contains the LOD selection result inclu...
Definition RenderData.h:519
Alignment at 4 bytes.
Definition RenderData.h:63
Frustum planes for frustum culling in the first compute shader stage.
Definition RenderData.h:195
Describes a mesh on the gpu. Where it is in the memory and how many meshlets it consists of.
Definition RenderData.h:51
View-projection data for Hi-Z occlusion culling Contains matrices and screen info needed to project b...
Definition RenderData.h:209
LOD configuration for GPU shader selection. Updated per-frame with camera and screen information.
Definition RenderData.h:498
Stores all data which is unique to each object.
Definition RenderData.h:124
Data for the primitive culling shader to assemble and pass on to the unpacking shader.
Definition RenderData.h:172
VkImageView fullView
View of all mip levels for sampling.
std::vector< VkImageView > mipViews
Per-mip views for compute writes.
std::vector< VkDescriptorImageInfo > imageInfos
This struct holds the data for the view projection matrix which will be passed to every rendered obje...
Struct for the result buffer of the culling operation. Stores which objects have passed the culling t...
Definition RenderData.h:224