32#include "zeroerr/assert.h"
34#include <glm/gtc/matrix_transform.hpp>
36#include "graphics/shaders/gpu_emit_comp_spv.inc"
37#include "graphics/shaders/vg_main_cull_comp_spv.inc"
38#include "graphics/shaders/hzb_build_comp_spv.inc"
39#include "graphics/shaders/gpu_cull_comp_spv.inc"
40#include "graphics/shaders/mesh3d_gpudriven_vert_spv.inc"
41#include "graphics/shaders/mesh3d_gpudriven_frag_spv.inc"
42#include "graphics/shaders/mesh3d_gbuffer_vis_vert_spv.inc"
43#include "graphics/shaders/mesh3d_gbuffer_vis_frag_spv.inc"
44#include "graphics/shaders/mesh3d_gbuffer_vgvis_vert_spv.inc"
45#include "graphics/shaders/mesh3d_gbuffer_vgvis_frag_spv.inc"
46#include "graphics/shaders/resolve_vis_vert_spv.inc"
47#include "graphics/shaders/resolve_vis_frag_spv.inc"
54 sceneColorSamples == vk::SampleCountFlagBits::e1;
57vk::DescriptorSet Graphics::bindlessSetForFrame()
const {
58 if (bindlessSets_.empty())
return nullptr;
59 return bindlessSets_[currentFrameSlot() % bindlessSets_.size()];
62void Graphics::initGpuDrivenResources() {
64 frameArenas_.resize(frameSlotCount());
65 for (
auto &arena : frameArenas_) {
66 arena.ensure(device, 8u << 20,
67 vk::BufferUsageFlagBits::eStorageBuffer |
68 vk::BufferUsageFlagBits::eIndirectBuffer |
69 vk::BufferUsageFlagBits::eTransferDst);
72 createMesh3DGpuDrivenPipeline();
75void Graphics::createGpuDrivenVisResources(
int width,
int height) {
77 if (gbufferVisRenderPass || gbufferSlots.empty())
return;
78 const uint32_t
w = uint32_t(
width);
79 const uint32_t
h = uint32_t(
height);
80 const vk::Format colorFmt = pickGBufferColorFormat(device);
81 const vk::Format depthFmt = vk::Format::eD32Sfloat;
82 const vk::Format visIDFmt = vk::Format::eR32G32Uint;
83 const vk::Format visBaryFmt = vk::Format::eR16G16Sfloat;
85 gbufferVisRenderPass =
86 device.createRenderPass()
87 .addSampledColorAttachment(colorFmt)
88 .addSampledColorAttachment(colorFmt)
89 .addSampledColorAttachment(colorFmt)
90 .addSampledColorAttachment(visIDFmt)
91 .addSampledColorAttachment(visBaryFmt)
92 .addSampledDepthAttachment(depthFmt)
93 .addSubpass(vkb::SubpassBuilder()
94 .addAttachmentRef(0, vk::ImageLayout::eColorAttachmentOptimal)
95 .addAttachmentRef(1, vk::ImageLayout::eColorAttachmentOptimal)
96 .addAttachmentRef(2, vk::ImageLayout::eColorAttachmentOptimal)
97 .addAttachmentRef(3, vk::ImageLayout::eColorAttachmentOptimal)
98 .addAttachmentRef(4, vk::ImageLayout::eColorAttachmentOptimal)
99 .setDepthStencilAttachment(
100 5, vk::ImageLayout::eDepthStencilAttachmentOptimal))
101 .addExternalShaderReadDependencies()
103 for (
auto &slot : gbufferSlots) {
104 slot.visFramebuffer = gbufferVisRenderPass.createFramebuffer(
106 {slot.normal.asAttachment(), slot.depthColor.asAttachment(), slot.albedo.asAttachment(),
107 slot.visID.asAttachment(), slot.visBary.asAttachment(), slot.depth.asAttachment()});
110 if (!mesh3dGpuDrivenPipelineLayout)
return;
114 std::vector<uint32_t> visVert(mesh3d_gbuffer_vis_vert_spv,
115 mesh3d_gbuffer_vis_vert_spv +
116 mesh3d_gbuffer_vis_vert_spv_count);
117 std::vector<uint32_t> visFrag(mesh3d_gbuffer_vis_frag_spv,
118 mesh3d_gbuffer_vis_frag_spv +
119 mesh3d_gbuffer_vis_frag_spv_count);
120 vk::ShaderModule visVertModule =
121 vkb::PipelineBuilder::createShaderModule(device.instance, visVert);
122 vk::ShaderModule visFragModule =
123 vkb::PipelineBuilder::createShaderModule(device.instance, visFrag);
125 device.createPipeline()
126 .useClassicPipeline(visVertModule, visFragModule)
127 .setPipelineLayout(mesh3dGpuDrivenPipelineLayout)
128 .setDynamicStatesViewportScissor()
129 .setRasterizer(vk::PolygonMode::eFill,
false,
false, 1.0f,
130 vk::CullModeFlagBits::eNone, vk::FrontFace::eClockwise)
131 .setDepthStencil(
true,
true, vk::CompareOp::eLess)
132 .setColorAttachmentCount(5)
133 .build(gbufferVisRenderPass);
134 device->destroyShaderModule(visVertModule);
135 device->destroyShaderModule(visFragModule);
139 std::vector<uint32_t> vgVisVert(mesh3d_gbuffer_vgvis_vert_spv,
140 mesh3d_gbuffer_vgvis_vert_spv +
141 mesh3d_gbuffer_vgvis_vert_spv_count);
142 std::vector<uint32_t> vgVisFrag(mesh3d_gbuffer_vgvis_frag_spv,
143 mesh3d_gbuffer_vgvis_frag_spv +
144 mesh3d_gbuffer_vgvis_frag_spv_count);
145 vk::ShaderModule vgVisVertModule =
146 vkb::PipelineBuilder::createShaderModule(device.instance, vgVisVert);
147 vk::ShaderModule vgVisFragModule =
148 vkb::PipelineBuilder::createShaderModule(device.instance, vgVisFrag);
149 gbufferVgVisPipeline =
150 device.createPipeline()
151 .useClassicPipeline(vgVisVertModule, vgVisFragModule)
152 .setPipelineLayout(mesh3dGpuDrivenPipelineLayout)
153 .setDynamicStatesViewportScissor()
154 .setRasterizer(vk::PolygonMode::eFill,
false,
false, 1.0f,
155 vk::CullModeFlagBits::eNone, vk::FrontFace::eClockwise)
156 .setDepthStencil(
true,
true, vk::CompareOp::eLess)
157 .setColorAttachmentCount(5)
158 .build(gbufferVisRenderPass);
159 device->destroyShaderModule(vgVisVertModule);
160 device->destroyShaderModule(vgVisFragModule);
167 if (frameArenas_.empty()) {
171 return frameArenas_[currentFrameSlot() % frameArenas_.size()];
174void Graphics::createBindlessSet() {
176 if (!whiteTexture || !whiteTexture->
gpuHandle)
return;
177 if (!defaultBindlessCube || !defaultBindlessCube->
gpuHandle)
return;
181 auto samplerBinding = [](uint32_t binding, uint32_t count, vk::ShaderStageFlags stages) {
182 vk::DescriptorSetLayoutBinding
b{};
184 b.descriptorType = vk::DescriptorType::eCombinedImageSampler;
185 b.descriptorCount = count;
186 b.stageFlags = stages;
189 auto storageBinding = [](uint32_t binding, vk::ShaderStageFlags stages) {
190 vk::DescriptorSetLayoutBinding
b{};
192 b.descriptorType = vk::DescriptorType::eStorageBuffer;
193 b.descriptorCount = 1;
194 b.stageFlags = stages;
197 auto uniformBinding = [](uint32_t binding, vk::ShaderStageFlags stages) {
198 vk::DescriptorSetLayoutBinding
b{};
200 b.descriptorType = vk::DescriptorType::eUniformBuffer;
201 b.descriptorCount = 1;
202 b.stageFlags = stages;
205 const vk::ShaderStageFlags allStages = vk::ShaderStageFlagBits::eVertex |
206 vk::ShaderStageFlagBits::eFragment |
207 vk::ShaderStageFlagBits::eCompute;
208 const vk::ShaderStageFlags computeOnly = vk::ShaderStageFlagBits::eCompute;
209 const vk::ShaderStageFlags vertFrag =
210 vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment;
211 std::vector<vk::DescriptorSetLayoutBinding> bindings{
214 storageBinding(2, allStages),
215 storageBinding(3, allStages),
216 storageBinding(4, allStages),
217 storageBinding(5, allStages),
218 uniformBinding(6, computeOnly),
219 storageBinding(7, computeOnly),
220 storageBinding(8, computeOnly),
221 storageBinding(9, computeOnly),
222 storageBinding(10, computeOnly),
223 samplerBinding(11, 1, computeOnly),
224 storageBinding(12, computeOnly),
225 storageBinding(13, computeOnly),
226 storageBinding(14, computeOnly),
227 samplerBinding(15, 1, vk::ShaderStageFlagBits::eFragment),
228 samplerBinding(16, 1, vk::ShaderStageFlagBits::eFragment),
229 storageBinding(17, computeOnly),
230 storageBinding(18, vertFrag),
231 storageBinding(19, vertFrag),
232 storageBinding(20, vertFrag),
233 storageBinding(21, vertFrag),
234 storageBinding(22, allStages),
235 storageBinding(23, allStages),
236 storageBinding(24, allStages),
237 storageBinding(25, allStages),
238 storageBinding(26, allStages),
239 storageBinding(27, allStages),
240 storageBinding(28, vk::ShaderStageFlagBits::eFragment),
241 storageBinding(29, vk::ShaderStageFlagBits::eVertex |
242 vk::ShaderStageFlagBits::eFragment |
243 vk::ShaderStageFlagBits::eCompute),
244 storageBinding(30, computeOnly),
247 vk::DescriptorSetLayoutCreateInfo layoutInfo{};
248 layoutInfo.bindingCount = uint32_t(bindings.size());
249 layoutInfo.pBindings = bindings.data();
250 bindlessSetLayoutUnique_ = device->createDescriptorSetLayoutUnique(layoutInfo);
251 bindlessSetLayout_ = *bindlessSetLayoutUnique_;
256 const uint32_t setCount = kAsyncResourceCopies;
257 std::array<vk::DescriptorPoolSize, 3> poolSizes{
258 vk::DescriptorPoolSize{vk::DescriptorType::eCombinedImageSampler,
260 vk::DescriptorPoolSize{vk::DescriptorType::eStorageBuffer, 25 * setCount},
261 vk::DescriptorPoolSize{vk::DescriptorType::eUniformBuffer, 1 * setCount},
263 vk::DescriptorPoolCreateInfo poolInfo{};
264 poolInfo.maxSets = setCount;
265 poolInfo.poolSizeCount = uint32_t(poolSizes.size());
266 poolInfo.pPoolSizes = poolSizes.data();
267 bindlessPool_ = device->createDescriptorPool(poolInfo);
270 std::vector<vk::DescriptorSetLayout> layouts(setCount, bindlessSetLayout_);
271 vk::DescriptorSetAllocateInfo alloc{};
272 alloc.descriptorPool = bindlessPool_;
273 alloc.descriptorSetCount = setCount;
274 alloc.pSetLayouts = layouts.data();
275 bindlessSets_ = device->allocateDescriptorSets(alloc);
280 bindlessFree2D_.clear();
281 bindlessFreeCube_.clear();
287 vk::DescriptorImageInfo white2D{white->sampler, white->imageView(),
288 vk::ImageLayout::eShaderReadOnlyOptimal};
289 vk::DescriptorImageInfo whiteCubeInfo{whiteCube->sampler, whiteCube->cubeImage.imageView(),
290 vk::ImageLayout::eShaderReadOnlyOptimal};
293 constexpr uint32_t kDescriptorChunk = 1;
294 auto chunkFill = [&](vk::DescriptorSet set, uint32_t binding, uint32_t total,
295 const vk::DescriptorImageInfo &info) {
296 for (uint32_t base = 0; base < total; base += kDescriptorChunk) {
297 vk::WriteDescriptorSet
w{};
299 w.dstBinding = binding;
300 w.dstArrayElement = base;
301 w.descriptorCount = std::min(kDescriptorChunk, total - base);
302 w.descriptorType = vk::DescriptorType::eCombinedImageSampler;
303 w.pImageInfo = &info;
304 device->updateDescriptorSets(1, &
w, 0,
nullptr);
307 for (vk::DescriptorSet set : bindlessSets_) {
313 constexpr uint32_t kMaxMeshRecords = 4096;
314 constexpr uint32_t kMaxMaterialRecords = 1024;
315 meshTableBuffer_ = vkb::GenericBuffer(device, vk::BufferUsageFlagBits::eStorageBuffer,
317 kHostVisibleCoherent);
318 meshTableCapacity_ = kMaxMeshRecords;
319 meshTableRecords_.reserve(kMaxMeshRecords);
320 materialTableBuffer_ = vkb::GenericBuffer(device, vk::BufferUsageFlagBits::eStorageBuffer,
322 kHostVisibleCoherent);
323 materialTableCapacity_ = kMaxMaterialRecords;
324 materialTableRecords_.reserve(kMaxMaterialRecords);
329 auto tableWrite = [&](vk::DescriptorSet set, uint32_t binding, vk::Buffer buffer) {
330 vk::DescriptorBufferInfo info{buffer, 0, VK_WHOLE_SIZE};
331 vk::WriteDescriptorSet
w{};
333 w.dstBinding = binding;
334 w.descriptorCount = 1;
335 w.descriptorType = vk::DescriptorType::eStorageBuffer;
336 w.pBufferInfo = &info;
337 device->updateDescriptorSets(1, &
w, 0,
nullptr);
339 for (vk::DescriptorSet set : bindlessSets_) {
340 tableWrite(set, 2, meshTableBuffer_.buffer);
341 tableWrite(set, 3, materialTableBuffer_.buffer);
342 tableWrite(set, 4, meshTableBuffer_.buffer);
343 tableWrite(set, 5, meshTableBuffer_.buffer);
346 gpuDrivenCullParamsPlaceholder_ =
347 vkb::GenericBuffer(device, vk::BufferUsageFlagBits::eUniformBuffer, 256,
348 kHostVisibleCoherent);
350 vk::DescriptorBufferInfo ubo{gpuDrivenCullParamsPlaceholder_.buffer, 0, 256};
351 for (vk::DescriptorSet set : bindlessSets_) {
352 vk::WriteDescriptorSet
w{};
355 w.descriptorCount = 1;
356 w.descriptorType = vk::DescriptorType::eUniformBuffer;
357 w.pBufferInfo = &ubo;
358 device->updateDescriptorSets(1, &
w, 0,
nullptr);
361 for (uint32_t
b : {7u, 8u, 9u, 10u, 12u, 13u, 14u, 17u})
362 for (vk::DescriptorSet set : bindlessSets_) tableWrite(set,
b, meshTableBuffer_.buffer);
364 vk::DescriptorImageInfo depthInfo{white->sampler, white->imageView(),
365 vk::ImageLayout::eShaderReadOnlyOptimal};
366 for (vk::DescriptorSet set : bindlessSets_) {
367 vk::WriteDescriptorSet
w{};
370 w.descriptorCount = 1;
371 w.descriptorType = vk::DescriptorType::eCombinedImageSampler;
372 w.pImageInfo = &depthInfo;
373 device->updateDescriptorSets(1, &
w, 0,
nullptr);
378 for (vk::DescriptorSet set : bindlessSets_) {
379 vk::DescriptorImageInfo visInfo{white->sampler, white->imageView(),
380 vk::ImageLayout::eShaderReadOnlyOptimal};
381 vk::WriteDescriptorSet
w{};
384 w.descriptorCount = 1;
385 w.descriptorType = vk::DescriptorType::eCombinedImageSampler;
386 w.pImageInfo = &visInfo;
387 device->updateDescriptorSets(1, &
w, 0,
nullptr);
389 device->updateDescriptorSets(1, &
w, 0,
nullptr);
392 ensureGpuVertexPool();
393 bindGpuVertexPoolBindless();
396 bindVgPoolBindless();
399void Graphics::ensureGpuVertexPool() {
400 if (gpuVertexPool_.positions.buffer)
return;
401 constexpr uint32_t kInitialVertices = 256u << 10;
402 constexpr uint32_t kInitialIndices = 768u << 10;
403 const auto hostMem = kHostVisibleCoherent;
404 gpuVertexPool_.positions = vkb::GenericBuffer(
405 device, vk::BufferUsageFlagBits::eStorageBuffer, kInitialVertices *
sizeof(glm::vec4),
407 gpuVertexPool_.normals = vkb::GenericBuffer(
408 device, vk::BufferUsageFlagBits::eStorageBuffer, kInitialVertices *
sizeof(glm::vec4),
410 gpuVertexPool_.uvs = vkb::GenericBuffer(
411 device, vk::BufferUsageFlagBits::eStorageBuffer, kInitialVertices *
sizeof(glm::vec2),
413 gpuVertexPool_.indices = vkb::GenericBuffer(
414 device, vk::BufferUsageFlagBits::eStorageBuffer, kInitialIndices *
sizeof(uint32_t),
416 gpuVertexPool_.vertexCount = 0;
417 gpuVertexPool_.indexCount = 0;
420void Graphics::growGpuVertexPool(uint32_t needVertices, uint32_t needIndices) {
424 const uint32_t oldVerts = gpuVertexPool_.vertexCount;
425 const uint32_t oldInds = gpuVertexPool_.indexCount;
426 const auto hostMem = kHostVisibleCoherent;
427 auto copyInto = [&](vkb::GenericBuffer &dst, vkb::GenericBuffer &src, vk::DeviceSize oldBytes,
428 vk::DeviceSize newBytes) {
429 vkb::GenericBuffer grown(device, vk::BufferUsageFlagBits::eStorageBuffer, newBytes,
432 void *srcMap = src.map();
433 void *dstMap = grown.map();
434 std::memcpy(dstMap, srcMap, oldBytes);
439 dst = std::move(grown);
441 const uint32_t newVerts = std::max(needVertices, oldVerts * 2u);
442 const uint32_t newInds = std::max(needIndices, oldInds * 2u);
443 copyInto(gpuVertexPool_.positions, gpuVertexPool_.positions,
444 vk::DeviceSize(oldVerts) *
sizeof(glm::vec4),
445 vk::DeviceSize(newVerts) *
sizeof(glm::vec4));
446 copyInto(gpuVertexPool_.normals, gpuVertexPool_.normals,
447 vk::DeviceSize(oldVerts) *
sizeof(glm::vec4),
448 vk::DeviceSize(newVerts) *
sizeof(glm::vec4));
449 copyInto(gpuVertexPool_.uvs, gpuVertexPool_.uvs,
450 vk::DeviceSize(oldVerts) *
sizeof(glm::vec2),
451 vk::DeviceSize(newVerts) *
sizeof(glm::vec2));
452 copyInto(gpuVertexPool_.indices, gpuVertexPool_.indices,
453 vk::DeviceSize(oldInds) *
sizeof(uint32_t),
454 vk::DeviceSize(newInds) *
sizeof(uint32_t));
455 bindGpuVertexPoolBindless();
458void Graphics::bindGpuVertexPoolBindless() {
459 if (bindlessSets_.empty() || !gpuVertexPool_.positions.buffer)
return;
460 auto bufWrite = [&](vk::DescriptorSet set, uint32_t binding, vk::Buffer buffer,
461 vk::DeviceSize size) {
462 vk::DescriptorBufferInfo info{buffer, 0, size};
463 vk::WriteDescriptorSet
w{};
465 w.dstBinding = binding;
466 w.descriptorCount = 1;
467 w.descriptorType = vk::DescriptorType::eStorageBuffer;
468 w.pBufferInfo = &info;
469 device->updateDescriptorSets(1, &
w, 0,
nullptr);
471 const uint32_t cap = [&]() {
472 const vk::DeviceSize bytes = gpuVertexPool_.positions.size;
473 return uint32_t(bytes /
sizeof(glm::vec4));
475 const uint32_t indCap = uint32_t(gpuVertexPool_.indices.size /
sizeof(uint32_t));
476 for (vk::DescriptorSet set : bindlessSets_) {
477 bufWrite(set, 18, gpuVertexPool_.positions.buffer,
478 vk::DeviceSize(cap) *
sizeof(glm::vec4));
479 bufWrite(set, 19, gpuVertexPool_.normals.buffer, vk::DeviceSize(cap) *
sizeof(glm::vec4));
480 bufWrite(set, 20, gpuVertexPool_.uvs.buffer, vk::DeviceSize(cap) *
sizeof(glm::vec2));
481 bufWrite(set, 21, gpuVertexPool_.indices.buffer,
482 vk::DeviceSize(indCap) *
sizeof(uint32_t));
486void Graphics::appendGpuMeshToPool(GpuMesh &gpu) {
487 if (!gpu.vertices.buffer || !gpu.indices.buffer)
return;
488 ensureGpuVertexPool();
489 const uint32_t nVerts = gpu.record.vertexCount;
490 const uint32_t nInds = gpu.record.indexCount;
491 if (nVerts == 0 || nInds == 0)
return;
492 const uint32_t newVerts = gpuVertexPool_.vertexCount + nVerts;
493 const uint32_t newInds = gpuVertexPool_.indexCount + nInds;
494 const uint32_t capVerts =
495 uint32_t(gpuVertexPool_.positions.size /
sizeof(glm::vec4));
496 const uint32_t capInds = uint32_t(gpuVertexPool_.indices.size /
sizeof(uint32_t));
497 if (newVerts > capVerts || newInds > capInds) {
498 growGpuVertexPool(newVerts, newInds);
501 void *vMap = gpu.vertices.map();
502 void *iMap = gpu.indices.map();
503 if (!vMap || !iMap)
return;
504 auto *
verts =
static_cast<const MeshVertex *
>(vMap);
505 auto *posDst =
static_cast<glm::vec4 *
>(gpuVertexPool_.positions.map());
506 auto *nrmDst =
static_cast<glm::vec4 *
>(gpuVertexPool_.normals.map());
507 auto *uvDst =
static_cast<glm::vec2 *
>(gpuVertexPool_.uvs.map());
508 auto *idxDst =
static_cast<uint32_t *
>(gpuVertexPool_.indices.map());
509 if (!posDst || !nrmDst || !uvDst || !idxDst) {
510 gpu.vertices.unmap();
514 posDst += gpuVertexPool_.vertexCount;
515 nrmDst += gpuVertexPool_.vertexCount;
516 uvDst += gpuVertexPool_.vertexCount;
517 idxDst += gpuVertexPool_.indexCount;
518 for (uint32_t i = 0; i < nVerts; ++i) {
519 posDst[i] = glm::vec4(
verts[i].
pos, 0.f);
521 uvDst[i] =
verts[i].uv;
523 if (gpu.indexType == vk::IndexType::eUint16) {
524 const auto *src16 =
static_cast<const uint16_t *
>(iMap);
525 for (uint32_t i = 0; i < nInds; ++i) idxDst[i] = uint32_t(src16[i]);
527 const auto *src32 =
static_cast<const uint32_t *
>(iMap);
528 for (uint32_t i = 0; i < nInds; ++i) idxDst[i] = src32[i];
530 const uint32_t firstIdx =
static_cast<const uint32_t *
>(iMap)[0];
531 const glm::vec3 firstPos =
verts[0].pos;
532 gpuVertexPool_.positions.unmap();
533 gpuVertexPool_.normals.unmap();
534 gpuVertexPool_.uvs.unmap();
535 gpuVertexPool_.indices.unmap();
536 gpu.vertices.unmap();
540 gpu.record.vertexOffset = gpuVertexPool_.vertexCount;
541 gpu.record.indexOffset = gpuVertexPool_.indexCount;
542 gpu.record.firstIndex = 0;
543 gpu.record.vertexBase = 0;
544 gpuVertexPool_.vertexCount = newVerts;
545 gpuVertexPool_.indexCount = newInds;
548uint32_t Graphics::registerBindlessTexture2D(GpuTexture *tex) {
552 const uint32_t slot = bindlessFree2D_.front();
553 bindlessFree2D_.erase(bindlessFree2D_.begin());
554 bindlessTextures2D_[slot] = tex;
555 tex->bindlessIndex2D = slot;
556 vk::DescriptorImageInfo img{tex->sampler, tex->imageView(),
557 vk::ImageLayout::eShaderReadOnlyOptimal};
558 for (vk::DescriptorSet set : bindlessSets_) {
559 vk::WriteDescriptorSet write{};
561 write.dstBinding = 0;
562 write.dstArrayElement = slot;
563 write.descriptorCount = 1;
564 write.descriptorType = vk::DescriptorType::eCombinedImageSampler;
565 write.pImageInfo = &img;
566 device->updateDescriptorSets(1, &write, 0,
nullptr);
571uint32_t Graphics::registerBindlessTextureCube(GpuTexture *tex) {
575 const uint32_t slot = bindlessFreeCube_.front();
576 bindlessFreeCube_.erase(bindlessFreeCube_.begin());
577 bindlessCubemaps_[slot] = tex;
578 tex->bindlessIndexCube = slot;
579 vk::DescriptorImageInfo img{tex->sampler, tex->cubeImage.imageView(),
580 vk::ImageLayout::eShaderReadOnlyOptimal};
581 for (vk::DescriptorSet set : bindlessSets_) {
582 vk::WriteDescriptorSet write{};
584 write.dstBinding = 1;
585 write.dstArrayElement = slot;
586 write.descriptorCount = 1;
587 write.descriptorType = vk::DescriptorType::eCombinedImageSampler;
588 write.pImageInfo = &img;
589 device->updateDescriptorSets(1, &write, 0,
nullptr);
594void Graphics::unregisterBindlessTexture(GpuTexture *tex) {
595 if (!tex || bindlessSets_.empty())
return;
596 auto *white =
static_cast<GpuTexture *
>(whiteTexture->
gpuHandle);
597 GpuTexture *whiteCube = white;
598 if (defaultBindlessCube && defaultBindlessCube->
gpuHandle)
599 whiteCube =
static_cast<GpuTexture *
>(defaultBindlessCube->
gpuHandle);
601 auto restore = [&](uint32_t binding, uint32_t slot, GpuTexture *placeholder,
603 vk::DescriptorImageInfo img{
604 placeholder->sampler,
605 cube ? placeholder->cubeImage.imageView() : placeholder->imageView(),
606 vk::ImageLayout::eShaderReadOnlyOptimal};
607 for (vk::DescriptorSet set : bindlessSets_) {
608 vk::WriteDescriptorSet write{};
610 write.dstBinding = binding;
611 write.dstArrayElement = slot;
612 write.descriptorCount = 1;
613 write.descriptorType = vk::DescriptorType::eCombinedImageSampler;
614 write.pImageInfo = &img;
615 device->updateDescriptorSets(1, &write, 0,
nullptr);
620 const uint32_t slot = tex->bindlessIndex2D;
621 bindlessTextures2D_[slot] = white;
622 bindlessFree2D_.push_back(slot);
623 restore(0, slot, white,
false);
627 const uint32_t slot = tex->bindlessIndexCube;
628 bindlessCubemaps_[slot] = whiteCube;
629 bindlessFreeCube_.push_back(slot);
630 restore(1, slot, whiteCube,
true);
635void Graphics::ensureVgBuffers() {
636 if (vgGpu_.positions.buffer)
return;
637 const auto hostMem = kHostVisibleCoherent;
638 constexpr uint32_t kInitClusters = 16384;
639 constexpr uint32_t kInitVertFloats = 1u << 20;
640 constexpr uint32_t kInitTriangles = 2u << 20;
641 vgGpu_.positions = vkb::GenericBuffer(device, vk::BufferUsageFlagBits::eStorageBuffer,
642 kInitVertFloats *
sizeof(
float), hostMem);
643 vgGpu_.triangles = vkb::GenericBuffer(device, vk::BufferUsageFlagBits::eStorageBuffer,
644 kInitTriangles *
sizeof(uint32_t), hostMem);
645 vgGpu_.clusters = vkb::GenericBuffer(device, vk::BufferUsageFlagBits::eStorageBuffer,
646 kInitClusters *
sizeof(GpuVgCluster), hostMem);
647 vgGpu_.clusterAssets =
648 vkb::GenericBuffer(device, vk::BufferUsageFlagBits::eStorageBuffer,
649 kInitClusters *
sizeof(uint32_t), hostMem);
650 constexpr uint32_t kVisBytes = (kMaxVgClusters + 1) *
sizeof(uint32_t);
651 constexpr uint32_t kIndBytes = kMaxVgClusters *
sizeof(glm::uvec4);
652 vgGpu_.visible = vkb::GenericBuffer(device, vk::BufferUsageFlagBits::eStorageBuffer |
653 vk::BufferUsageFlagBits::eIndirectBuffer,
654 kVisBytes * kAsyncResourceCopies, hostMem);
655 vgGpu_.indirect = vkb::GenericBuffer(device, vk::BufferUsageFlagBits::eStorageBuffer |
656 vk::BufferUsageFlagBits::eIndirectBuffer,
657 kIndBytes * kAsyncResourceCopies, hostMem);
658 vgGpu_.assetMaterials =
659 vkb::GenericBuffer(device, vk::BufferUsageFlagBits::eStorageBuffer,
660 kMaxVgAssets *
sizeof(uint32_t) * kAsyncResourceCopies, hostMem);
662 vkb::GenericBuffer(device, vk::BufferUsageFlagBits::eStorageBuffer,
663 kMaxVgAssets *
sizeof(glm::mat4) * kAsyncResourceCopies, hostMem);
665 void *
m = vgGpu_.visible.map();
666 std::memset(
m, 0, kVisBytes * kAsyncResourceCopies);
667 vgGpu_.visible.unmap();
668 m = vgGpu_.assetMaterials.map();
669 std::memset(
m, 0, kMaxVgAssets *
sizeof(uint32_t) * kAsyncResourceCopies);
670 vgGpu_.assetMaterials.unmap();
671 m = vgGpu_.assetModels.map();
672 for (
size_t s = 0;
s < kAsyncResourceCopies; ++
s)
673 for (uint32_t
a = 0;
a < kMaxVgAssets; ++
a)
674 static_cast<glm::mat4 *
>(
m)[
s * kMaxVgAssets +
a] = glm::mat4(1.f);
675 vgGpu_.assetModels.unmap();
677 bindVgPoolBindless();
680void Graphics::growVgBuffers(uint32_t needClusters, uint32_t needVertices,
681 uint32_t needTriangles) {
683 const auto hostMem = kHostVisibleCoherent;
684 auto growBuffer = [&](vkb::GenericBuffer &dst, uint32_t elementSize, uint32_t oldElems,
686 if (newElems <= oldElems)
return;
687 vkb::GenericBuffer grown(device, vk::BufferUsageFlagBits::eStorageBuffer,
688 vk::DeviceSize(newElems) * elementSize, hostMem);
690 void *srcMap = dst.map();
691 void *dstMap = grown.map();
692 std::memcpy(dstMap, srcMap, vk::DeviceSize(oldElems) * elementSize);
697 dst = std::move(grown);
699 const uint32_t curClusters = uint32_t(vgGpu_.clusters.size /
sizeof(GpuVgCluster));
700 const uint32_t curVertFloats = uint32_t(vgGpu_.positions.size /
sizeof(
float));
701 const uint32_t curTriangles = uint32_t(vgGpu_.triangles.size /
sizeof(uint32_t));
702 growBuffer(vgGpu_.clusters, uint32_t(
sizeof(GpuVgCluster)), curClusters,
703 std::max(needClusters, curClusters * 2u));
704 growBuffer(vgGpu_.clusterAssets, uint32_t(
sizeof(uint32_t)), curClusters,
705 std::max(needClusters, curClusters * 2u));
706 growBuffer(vgGpu_.positions, uint32_t(
sizeof(
float)), curVertFloats,
707 std::max(needVertices, curVertFloats * 2u));
708 growBuffer(vgGpu_.triangles, uint32_t(
sizeof(uint32_t)), curTriangles,
709 std::max(needTriangles, curTriangles * 2u));
710 bindVgPoolBindless();
713void Graphics::bindVgPoolBindless() {
714 if (bindlessSets_.empty() || !vgGpu_.positions.buffer)
return;
715 auto bufWrite = [&](vk::DescriptorSet set, uint32_t binding, vk::Buffer buffer,
716 vk::DeviceSize offset, vk::DeviceSize size) {
717 vk::DescriptorBufferInfo info{buffer, offset, size};
718 vk::WriteDescriptorSet
w{};
720 w.dstBinding = binding;
721 w.descriptorCount = 1;
722 w.descriptorType = vk::DescriptorType::eStorageBuffer;
723 w.pBufferInfo = &info;
724 device->updateDescriptorSets(1, &
w, 0,
nullptr);
726 const vk::DeviceSize posBytes = vgGpu_.positions.size;
727 const vk::DeviceSize triBytes = vgGpu_.triangles.size;
728 const vk::DeviceSize clBytes = vgGpu_.clusters.size;
729 const vk::DeviceSize claBytes = vgGpu_.clusterAssets.size;
730 for (vk::DescriptorSet set : bindlessSets_) {
731 bufWrite(set, 22, vgGpu_.positions.buffer, 0, posBytes);
732 bufWrite(set, 23, vgGpu_.triangles.buffer, 0, triBytes);
733 bufWrite(set, 24, vgGpu_.clusters.buffer, 0, clBytes);
734 bufWrite(set, 25, vgGpu_.clusterAssets.buffer, 0, claBytes);
735 bufWrite(set, 28, vgGpu_.assetMaterials.buffer, 0, vgGpu_.assetMaterials.size);
736 bufWrite(set, 29, vgGpu_.assetModels.buffer, 0, vgGpu_.assetModels.size);
737 bufWrite(set, 26, vgGpu_.visible.buffer, 0, vgGpu_.visible.size);
738 bufWrite(set, 27, vgGpu_.indirect.buffer, 0, vgGpu_.indirect.size);
742void Graphics::bindVgFrameBindless(vk::DescriptorSet bindless,
size_t slot) {
743 if (!bindless || !vgGpu_.visible.buffer)
return;
744 const vk::DeviceSize slotVis = (vk::DeviceSize(kMaxVgClusters) + 1) *
sizeof(uint32_t);
745 const vk::DeviceSize slotInd = vk::DeviceSize(kMaxVgClusters) *
sizeof(glm::uvec4);
746 const vk::DeviceSize slotMat = vk::DeviceSize(kMaxVgAssets) *
sizeof(uint32_t);
747 const vk::DeviceSize slotModel = vk::DeviceSize(kMaxVgAssets) *
sizeof(glm::mat4);
748 const vk::DeviceSize visOff = slotVis * slot;
749 const vk::DeviceSize indOff = slotInd * slot;
750 const vk::DeviceSize matOff = slotMat * slot;
751 const vk::DeviceSize modelOff = slotModel * slot;
752 vk::DescriptorBufferInfo infos[4]{
753 {vgGpu_.visible.buffer, visOff, slotVis},
754 {vgGpu_.indirect.buffer, indOff, slotInd},
755 {vgGpu_.assetMaterials.buffer, matOff, slotMat},
756 {vgGpu_.assetModels.buffer, modelOff, slotModel},
758 vk::WriteDescriptorSet
w[4]{};
759 const uint32_t bindings[4] = {26, 27, 28, 29};
760 for (
int i = 0; i < 4; ++i) {
761 w[i].dstSet = bindless;
762 w[i].dstBinding = bindings[i];
763 w[i].descriptorCount = 1;
764 w[i].descriptorType = vk::DescriptorType::eStorageBuffer;
765 w[i].pBufferInfo = &infos[i];
767 device->updateDescriptorSets(4,
w, 0,
nullptr);
777 const uint32_t assetId = vgAssetCount_;
778 const uint32_t vertBase = vgVertexCount_;
779 const uint32_t triBase = vgTriangleCount_;
780 const uint32_t clusterBase = vgClusterCount_;
781 const uint32_t needClusters = clusterBase + uint32_t(asset.
clusterCount);
782 const uint32_t needVerts = uint32_t(vertBase + uint32_t(asset.
vertexCount)) * 3;
783 const uint32_t needTris = triBase + uint32_t(asset.
triangleCount);
784 const uint32_t curClusters = uint32_t(vgGpu_.clusters.size /
sizeof(
GpuVgCluster));
785 const uint32_t curVerts = uint32_t(vgGpu_.positions.size /
sizeof(
float));
786 const uint32_t curTris = uint32_t(vgGpu_.triangles.size /
sizeof(uint32_t));
787 if (needClusters > curClusters || needVerts > curVerts || needTris > curTris)
788 growVgBuffers(needClusters, needVerts, needTris);
790 void *posMap = vgGpu_.positions.map();
791 void *triMap = vgGpu_.triangles.map();
792 void *clMap = vgGpu_.clusters.map();
793 void *claMap = vgGpu_.clusterAssets.map();
794 if (!posMap || !triMap || !clMap || !claMap) {
795 if (posMap) vgGpu_.positions.unmap();
796 if (triMap) vgGpu_.triangles.unmap();
797 if (clMap) vgGpu_.clusters.unmap();
798 if (claMap) vgGpu_.clusterAssets.unmap();
801 std::memcpy(
static_cast<char *
>(posMap) +
size_t(vertBase) * 3 *
sizeof(
float), asset.
positions,
804 auto *dst =
static_cast<uint32_t *
>(triMap) + triBase;
807 std::memcpy(
static_cast<char *
>(clMap) + vgClusterCount_ *
sizeof(
GpuVgCluster),
810 auto *dst =
static_cast<GpuVgCluster *
>(clMap) + vgClusterCount_;
812 dst[i].u1[0] += triBase;
815 auto *cla =
static_cast<uint32_t *
>(claMap);
816 for (
int i = 0; i < asset.
clusterCount; ++i) cla[vgClusterCount_ + uint32_t(i)] = assetId;
817 vgGpu_.positions.unmap();
818 vgGpu_.triangles.unmap();
819 vgGpu_.clusters.unmap();
820 vgGpu_.clusterAssets.unmap();
822 vgClusterCount_ = needClusters;
823 vgVertexCount_ = vertBase + uint32_t(asset.
vertexCount);
824 vgTriangleCount_ = needTris;
825 vgAssetCount_ = assetId + 1;
828 void *
m = vgGpu_.assetModels.map();
829 auto *models =
static_cast<glm::mat4 *
>(
m);
830 for (
size_t s = 0;
s < kAsyncResourceCopies; ++
s)
831 models[
s * kMaxVgAssets + assetId] = glm::mat4(1.f);
832 vgGpu_.assetModels.unmap();
839 const auto *gpu =
static_cast<const GpuMesh *
>(
mesh->gpuHandle);
844 if (!
mesh || !
mesh->gpuHandle)
return false;
845 if (vgAssetId >= vgAssetCount_ || vgAssetId >= kMaxVgAssets)
return false;
846 auto *gpu =
static_cast<GpuMesh *
>(
mesh->gpuHandle);
849 gpu->gpuRecordIndex < meshTableRecords_.size()) {
850 meshTableRecords_[gpu->gpuRecordIndex].vgAssetId = vgAssetId;
857 uint32_t materialId) {
858 if (vgAssetId >= vgAssetCount_ || vgAssetId >= kMaxVgAssets)
return false;
859 if (vgGpu_.assetModels.buffer) {
860 const size_t slot = currentFrameSlot() % kAsyncResourceCopies;
861 void *
m = vgGpu_.assetModels.map();
862 static_cast<glm::mat4 *
>(
m)[slot * kMaxVgAssets + vgAssetId] =
model;
863 vgGpu_.assetModels.unmap();
864 void *am = vgGpu_.assetMaterials.map();
865 static_cast<uint32_t *
>(am)[slot * kMaxVgAssets + vgAssetId] = materialId;
866 vgGpu_.assetMaterials.unmap();
868 vgAnyThisFrame_ =
true;
873 if (!vgGpu_.visible.buffer || vgAssetCount_ == 0)
return 0;
874 void *map = vgGpu_.visible.map();
876 const size_t slot = vgLastVisible_ % kAsyncResourceCopies;
877 const uint32_t count =
static_cast<const uint32_t *
>(map)[slot * (kMaxVgClusters + 1)];
878 vgGpu_.visible.unmap();
882uint32_t Graphics::registerMeshRecord(
GpuMesh *gpu) {
894 if (map && vertexCount > 0) {
896 glm::vec3 minv(1e30f), maxv(-1e30f);
897 for (uint32_t i = 0; i < vertexCount; ++i) {
898 minv = glm::min(minv,
verts[i].
pos);
899 maxv = glm::max(maxv,
verts[i].
pos);
901 const glm::vec3 center = (minv + maxv) * 0.5f;
903 for (uint32_t i = 0; i < vertexCount; ++i)
904 radius = std::max(radius, glm::length(
verts[i].
pos - center));
911 appendGpuMeshToPool(*gpu);
912 const uint32_t
idx = uint32_t(meshTableRecords_.size());
913 meshTableRecords_.push_back(gpu->
record);
914 meshRecordOwners_.push_back(gpu);
920void Graphics::syncMeshTable() {
921 if (!meshTableBuffer_.buffer || meshTableRecords_.empty())
return;
922 meshTableBuffer_.updateLocal(vkb::FrameSlot::gpuIdle(), meshTableRecords_.data(),
932 material->getReceiveShadow() ? 1.f : 0.f,
933 material->getReceiveLight() ? 1.f : 0.f);
934 rec.texBomb = glm::vec4(
material->getTexCellBombScale(),
material->getTexCellBombStrength(),
935 material->getTexCellBombRotation(), 0.f);
936 rec.parallax = glm::vec4(
material->getParallaxScale(),
material->getParallaxMinLayers(),
937 material->getParallaxMaxLayers(), 0.f);
938 auto slotOf = [](Texture *t) {
940 return static_cast<GpuTexture *
>(t->gpuHandle)->bindlessIndex2D;
942 rec.textureSlots[0] = slotOf(
material->getAlbedoTexture());
943 rec.textureSlots[1] = slotOf(
material->getNormalTexture());
944 rec.textureSlots[2] = slotOf(
material->getHeightTexture());
947 rec.shadingModel = (
model ==
"unlit") ? 1u : (
model ==
"hair") ? 2u
948 : (
model ==
"custom") ? 3u
950 if (
material->getCastShadow()) rec.flags |= 1u;
951 if (
material->getCastOcclusion()) rec.flags |= 2u;
957 auto it = materialTableIndex_.find(
material);
958 if (it != materialTableIndex_.end()) {
959 materialTableRecords_[it->second] = buildMaterialRecord(
material);
964 const uint32_t
idx = uint32_t(materialTableRecords_.size());
966 materialTableRecords_.push_back(buildMaterialRecord(
material));
972 if (!materialTableBuffer_.buffer || materialTableRecords_.empty())
return;
973 materialTableBuffer_.updateLocal(vkb::FrameSlot::gpuIdle(), materialTableRecords_.data(),
974 materialTableRecords_.size() *
980 auto *gpu =
static_cast<GpuMesh *
>(
mesh->gpuHandle);
994 if (!mesh3dGpuDrivenPipeline || bindlessSets_.empty() || !meshTableBuffer_.buffer)
return false;
995 if (!instances || instanceCount == 0)
return false;
999 for (uint32_t i = 0; i < instanceCount; ++i) {
1000 builder.
add(i, instances[i].meshId, instances[i].materialId, 0);
1002 const uint32_t drawCount = builder.
build(meshTableRecords_);
1003 if (drawCount == 0)
return false;
1004 lastGpuDrivenDrawCount_ = drawCount;
1005 const auto &cmds = builder.
commands();
1009 std::vector<GpuInstance> sorted(instanceCount);
1010 for (uint32_t i = 0; i < instanceCount; ++i) sorted[i] = instances[order[i]];
1015 if (!instAlloc.
mapped || !cmdAlloc.
mapped)
return false;
1016 std::memcpy(instAlloc.
mapped, sorted.data(), instanceCount *
sizeof(
GpuInstance));
1022 const vk::DescriptorSet bindless = bindlessSetForFrame();
1023 if (!bindless)
return false;
1024 vk::DescriptorBufferInfo instInfo{arena.buffer(), instAlloc.
offset, instAlloc.
size};
1025 vk::WriteDescriptorSet instWrite{};
1026 instWrite.dstSet = bindless;
1027 instWrite.dstBinding = 4;
1028 instWrite.descriptorCount = 1;
1029 instWrite.descriptorType = vk::DescriptorType::eStorageBuffer;
1030 instWrite.pBufferInfo = &instInfo;
1031 device->updateDescriptorSets(1, &instWrite, 0,
nullptr);
1034 const Graphics::GpuDrivenFrameSet0 s0 = gpuDrivenFrameSet0();
1035 if (!s0.set)
return false;
1036 const uint32_t dynOffsets[2] = {s0.uboOffset, s0.shadowOffset};
1038 auto &cb = currentPresentCb();
1039 cb.bindPipeline(vk::PipelineBindPoint::eGraphics, mesh3dGpuDrivenPipeline);
1040 cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, mesh3dGpuDrivenPipelineLayout, 0, 1,
1041 &s0.set, 2, dynOffsets);
1042 cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, mesh3dGpuDrivenPipelineLayout, 1, 1,
1043 &bindless, 0,
nullptr);
1050 for (uint32_t i = 0; i < drawCount; ++i) {
1051 const GpuInstance &first = sorted[cmds[i].firstInstance];
1052 if (first.
meshId >= meshRecordOwners_.size()) {
1053 EV_ASSERT(
false,
"indirect draw references an unregistered mesh record");
1057 if (
mesh != boundMesh) {
1058 const vk::DeviceSize vbOffset = 0;
1059 cb.bindVertexBuffers(0, 1,
mesh->vertices, &vbOffset);
1060 cb.bindIndexBuffer(
mesh->indices.buffer, 0,
mesh->indexType);
1063 cb.drawIndexedIndirect(arena.buffer(), cmdAlloc.
offset + vk::DeviceSize(i) * stride, 1,
1074void gpuDrivenFrustumPlanes(
const glm::mat4 &
m, glm::vec4 planes[6]) {
1076 const glm::vec4 row0(
m[0][0],
m[1][0],
m[2][0],
m[3][0]);
1077 const glm::vec4 row1(
m[0][1],
m[1][1],
m[2][1],
m[3][1]);
1078 const glm::vec4 row2(
m[0][2],
m[1][2],
m[2][2],
m[3][2]);
1079 const glm::vec4 row3(
m[0][3],
m[1][3],
m[2][3],
m[3][3]);
1080 planes[0] = row3 + row0;
1081 planes[1] = row3 - row0;
1082 planes[2] = row3 + row1;
1083 planes[3] = row3 - row1;
1084 planes[4] = row3 + row2;
1085 planes[5] = row3 - row2;
1086 for (
int i = 0; i < 6; ++i) {
1087 const float len = glm::length(glm::vec3(planes[i]));
1088 if (len > 1e-8f) planes[i] /= len;
1094Graphics::GpuDrivenCullSlot &Graphics::gpuDrivenCullSlot(uint32_t frameSlot) {
1096 EV_ASSERT(!gpuDrivenCullSlots_.empty(),
"gpuDriven cull slot accessed before resources");
1097 return gpuDrivenCullSlots_[frameSlot % gpuDrivenCullSlots_.size()];
1100Graphics::GpuDrivenCullSlot &Graphics::currentGpuDrivenCullSlot() {
1101 return gpuDrivenCullSlot(currentFrameSlot());
1104void Graphics::ensureGpuDrivenCullResources(
int width,
int height) {
1106 if (gpuDrivenCullReady_ && gpuDrivenCullWidth ==
width && gpuDrivenCullHeight ==
height)
1108 destroyGpuDrivenCullResources();
1110 gpuDrivenCullWidth =
width;
1111 gpuDrivenCullHeight =
height;
1116 uint32_t maxMip = 0;
1118 uint32_t
w = uint32_t(
width);
1119 uint32_t
h = uint32_t(
height);
1121 mipOffsets[
m] = totalWords;
1122 const uint32_t mw = std::max(
w >>
m, 1u);
1123 const uint32_t mh = std::max(
h >>
m, 1u);
1124 totalWords += mw * mh;
1126 if (mw == 1 && mh == 1)
break;
1129 gpuDrivenCullMaxMip = maxMip;
1136 const vk::DeviceSize hzbBytes = totalWords *
sizeof(uint32_t);
1138 gpuDrivenCullSlots_.resize(frameSlotCount());
1139 for (
auto &slot : gpuDrivenCullSlots_) {
1141 vkb::GenericBuffer(device, vk::BufferUsageFlagBits::eStorageBuffer, flagBytes,
1142 kHostVisibleCoherent);
1143 slot.compacted = vkb::GenericBuffer(device, vk::BufferUsageFlagBits::eStorageBuffer,
1144 compactBytes, kHostVisibleCoherent);
1145 slot.indirect = vkb::GenericBuffer(device, vk::BufferUsageFlagBits::eStorageBuffer |
1146 vk::BufferUsageFlagBits::eIndirectBuffer,
1147 indirectBytes, kHostVisibleCoherent);
1148 slot.indirectNI = vkb::GenericBuffer(device, vk::BufferUsageFlagBits::eStorageBuffer |
1149 vk::BufferUsageFlagBits::eIndirectBuffer,
1150 indirectNIBytes, kHostVisibleCoherent);
1151 slot.bucketCounters =
1152 vkb::GenericBuffer(device, vk::BufferUsageFlagBits::eStorageBuffer, counterBytes,
1153 kHostVisibleCoherent);
1154 slot.hzb = vkb::GenericBuffer(device, vk::BufferUsageFlagBits::eStorageBuffer, hzbBytes,
1155 kHostVisibleCoherent);
1157 vkb::GenericBuffer(device, vk::BufferUsageFlagBits::eUniformBuffer,
1158 sizeof(GpuCullParams), kHostVisibleCoherent);
1160 void *hzbMap = slot.hzb.map();
1161 auto *u32 =
static_cast<uint32_t *
>(hzbMap);
1162 for (uint32_t
m = 0;
m <= maxMip; ++
m) u32[
m] = mipOffsets[
m];
1164 std::memset(
static_cast<char *
>(hzbMap) +
kHZBHeaderWords *
sizeof(uint32_t), 0,
1171 vk::DescriptorSetLayoutCreateInfo emptyInfo{};
1172 gpuDrivenComputeEmptyLayout_ = device->createDescriptorSetLayout(emptyInfo);
1173 vk::PushConstantRange pcr{vk::ShaderStageFlagBits::eCompute, 0, 20};
1174 vk::PipelineLayoutCreateInfo pli{};
1175 std::array<vk::DescriptorSetLayout, 2> computeLayouts{gpuDrivenComputeEmptyLayout_,
1176 bindlessSetLayout_};
1177 pli.setLayoutCount = uint32_t(computeLayouts.size());
1178 pli.pSetLayouts = computeLayouts.data();
1179 pli.pushConstantRangeCount = 1;
1180 pli.pPushConstantRanges = &pcr;
1181 gpuDrivenComputeLayout = device->createPipelineLayout(pli);
1183 const auto spvOf = [](
const uint32_t *
p,
size_t n) {
1184 return std::vector<uint32_t>(
p,
p +
n);
1186 hzbBuildPass_.
create(device, gpuDrivenComputeLayout,
1187 spvOf(hzb_build_comp_spv, hzb_build_comp_spv_count));
1188 cullPass_.
create(device, gpuDrivenComputeLayout,
1189 spvOf(gpu_cull_comp_spv, gpu_cull_comp_spv_count));
1190 emitPass_.
create(device, gpuDrivenComputeLayout,
1191 spvOf(gpu_emit_comp_spv, gpu_emit_comp_spv_count));
1192 vgCullPass_.
create(device, gpuDrivenComputeLayout,
1193 spvOf(vg_main_cull_comp_spv, vg_main_cull_comp_spv_count));
1194 gpuDrivenCullReady_ =
1198void Graphics::destroyGpuDrivenCullResources() {
1199 hzbBuildPass_ = ComputePass{};
1200 cullPass_ = ComputePass{};
1201 emitPass_ = ComputePass{};
1202 vgCullPass_ = ComputePass{};
1203 if (gpuDrivenComputeLayout) {
1204 device->destroyPipelineLayout(gpuDrivenComputeLayout);
1205 gpuDrivenComputeLayout =
nullptr;
1207 if (gpuDrivenComputeEmptyLayout_) {
1208 device->destroyDescriptorSetLayout(gpuDrivenComputeEmptyLayout_);
1209 gpuDrivenComputeEmptyLayout_ =
nullptr;
1211 for (
auto &slot : gpuDrivenCullSlots_) {
1212 slot.visibleFlags.release();
1213 slot.compacted.release();
1214 slot.indirect.release();
1215 slot.indirectNI.release();
1216 slot.bucketCounters.release();
1218 slot.cullParams.release();
1220 gpuDrivenCullSlots_.clear();
1221 gpuDrivenCullReady_ =
false;
1222 gpuDrivenCullWidth = 0;
1223 gpuDrivenCullHeight = 0;
1226void Graphics::recordGpuDrivenHzbBuild() {
1227 if (!gpuDrivenCullReady_)
return;
1228 auto *slot = currentGBufferSlot();
1229 if (!slot || !slot->depthGpu.sampler || !slot->depthGpu.imageView())
return;
1230 auto &cull = currentGpuDrivenCullSlot();
1231 auto &cb = currentPresentCb();
1234 vk::ImageMemoryBarrier imb{};
1235 imb.image = slot->depth.image();
1236 imb.oldLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
1237 imb.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
1238 imb.srcAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite;
1239 imb.dstAccessMask = vk::AccessFlagBits::eShaderRead;
1240 imb.subresourceRange = {vk::ImageAspectFlagBits::eDepth, 0, 1, 0, 1};
1241 cb.pipelineBarrier(vk::PipelineStageFlagBits::eEarlyFragmentTests |
1242 vk::PipelineStageFlagBits::eLateFragmentTests,
1243 vk::PipelineStageFlagBits::eComputeShader, {}, 0,
nullptr, 0,
nullptr, 1,
1249 uint32_t
w = uint32_t(gpuDrivenCullWidth);
1250 uint32_t
h = uint32_t(gpuDrivenCullHeight);
1251 for (uint32_t
m = 0;
m <= gpuDrivenCullMaxMip; ++
m) {
1252 mipOffsets[
m] = totalWords;
1253 const uint32_t mw = std::max(
w >>
m, 1u);
1254 const uint32_t mh = std::max(
h >>
m, 1u);
1255 totalWords += mw * mh;
1258 for (uint32_t
m = 0;
m <= gpuDrivenCullMaxMip; ++
m) {
1261 vk::BufferMemoryBarrier bmb{};
1262 bmb.buffer = cull.hzb.buffer;
1263 bmb.size = VK_WHOLE_SIZE;
1264 bmb.srcAccessMask = vk::AccessFlagBits::eShaderWrite;
1265 bmb.dstAccessMask = vk::AccessFlagBits::eShaderRead;
1266 cb.pipelineBarrier(vk::PipelineStageFlagBits::eComputeShader,
1267 vk::PipelineStageFlagBits::eComputeShader, {}, 0,
nullptr, 1, &bmb,
1270 const uint32_t mw = std::max(uint32_t(gpuDrivenCullWidth) >>
m, 1u);
1271 const uint32_t mh = std::max(uint32_t(gpuDrivenCullHeight) >>
m, 1u);
1277 uint32_t prevOffset;
1278 }
push{
m, mw, mh, 0
u, mipOffsets[
m > 0 ?
m - 1 : 0]};
1279 cb.pushConstants(gpuDrivenComputeLayout, vk::ShaderStageFlagBits::eCompute, 0,
1281 hzbBuildPass_.
record(cb, (mw + 7) / 8, (mh + 7) / 8, 1);
1287 if (!instances || instanceCount == 0)
return false;
1291 for (uint32_t i = 0; i < instanceCount; ++i) {
1292 builder.
add(i, instances[i].meshId, instances[i].materialId, 0);
1294 const uint32_t bucketCount = builder.
build(meshTableRecords_);
1296 const auto &cmds = builder.
commands();
1299 std::vector<GpuInstance> sorted(instanceCount);
1300 for (uint32_t i = 0; i < instanceCount; ++i) sorted[i] = instances[order[i]];
1302 gpuDrivenBucketIds_.assign(instanceCount, 0);
1303 gpuDrivenBucketOffsets_.resize(bucketCount);
1304 gpuDrivenBucketMeshIds_.resize(bucketCount);
1305 for (uint32_t
b = 0;
b < bucketCount; ++
b) {
1306 gpuDrivenBucketOffsets_[
b] = cmds[
b].firstInstance;
1307 gpuDrivenBucketMeshIds_[
b] = sorted[cmds[
b].firstInstance].meshId;
1311 for (uint32_t j = 0; j < instanceCount; ++j) {
1312 while (
b + 1 < bucketCount && cmds[
b + 1].firstInstance <= j) ++
b;
1313 gpuDrivenBucketIds_[j] =
b;
1320 gpuDrivenInstAlloc_ = arena.alloc(instanceCount *
sizeof(
GpuInstance), 64);
1321 gpuDrivenBucketIdAlloc_ = arena.alloc(instanceCount *
sizeof(uint32_t), 64);
1322 gpuDrivenBucketOffAlloc_ = arena.alloc(bucketCount *
sizeof(uint32_t), 64);
1323 if (!gpuDrivenInstAlloc_.
mapped || !gpuDrivenBucketIdAlloc_.
mapped ||
1324 !gpuDrivenBucketOffAlloc_.
mapped) {
1325 gpuDrivenCullInstanceCount_ = 0;
1328 std::memcpy(gpuDrivenInstAlloc_.
mapped, sorted.data(),
1330 std::memcpy(gpuDrivenBucketIdAlloc_.
mapped, gpuDrivenBucketIds_.data(),
1331 instanceCount *
sizeof(uint32_t));
1332 std::memcpy(gpuDrivenBucketOffAlloc_.
mapped, gpuDrivenBucketOffsets_.data(),
1333 bucketCount *
sizeof(uint32_t));
1335 gpuDrivenBucketCount_ = bucketCount;
1336 gpuDrivenCullInstanceCount_ = instanceCount;
1337 lastGpuDrivenDrawCount_ = bucketCount;
1340 const vk::DescriptorSet bindless = bindlessSetForFrame();
1342 gpuDrivenCullInstanceCount_ = 0;
1345 auto bufWrite = [&](uint32_t binding, vk::Buffer buffer, vk::DeviceSize offset,
1346 vk::DeviceSize size) {
1347 vk::DescriptorBufferInfo info{buffer, offset, size};
1348 vk::WriteDescriptorSet
w{};
1349 w.dstSet = bindless;
1350 w.dstBinding = binding;
1351 w.descriptorCount = 1;
1352 w.descriptorType = vk::DescriptorType::eStorageBuffer;
1353 w.pBufferInfo = &info;
1354 device->updateDescriptorSets(1, &
w, 0,
nullptr);
1356 bufWrite(17, arena.buffer(), gpuDrivenInstAlloc_.
offset, gpuDrivenInstAlloc_.
size);
1357 bufWrite(12, arena.buffer(), gpuDrivenBucketIdAlloc_.
offset, gpuDrivenBucketIdAlloc_.
size);
1358 bufWrite(13, arena.buffer(), gpuDrivenBucketOffAlloc_.
offset, gpuDrivenBucketOffAlloc_.
size);
1362void Graphics::gpuDrivenRecordComputeSection(
const glm::mat4 &
viewProj,
const glm::vec3 &
eye,
1363 float fovYDeg,
float nearZ,
float farZ) {
1364 if (!gpuDrivenCullReady_)
return;
1365 auto &slot = currentGpuDrivenCullSlot();
1366 auto &cb = currentPresentCb();
1367 const vk::DescriptorSet bindless = bindlessSetForFrame();
1368 if (!bindless)
return;
1371 ensureFlatNormalTexture3D();
1372 ensureFlatHeightTexture3D();
1373 ensureDefaultEnvCubemap();
1377 createGBufferResources(gbufferWidth > 0 ? gbufferWidth : int(swapchain.extent.
width),
1378 gbufferHeight > 0 ? gbufferHeight : int(swapchain.extent.
height));
1380 auto *gbSlot = currentGBufferSlot();
1381 if (gbSlot && gbSlot->visIDGpu.sampler && gbSlot->visBaryGpu.sampler) {
1382 vk::DescriptorImageInfo visIDInfo{gbSlot->visIDGpu.sampler, gbSlot->visIDGpu.imageView(),
1383 vk::ImageLayout::eShaderReadOnlyOptimal};
1384 vk::DescriptorImageInfo visBaryInfo{gbSlot->visBaryGpu.sampler,
1385 gbSlot->visBaryGpu.imageView(),
1386 vk::ImageLayout::eShaderReadOnlyOptimal};
1387 vk::WriteDescriptorSet
w[2]{};
1388 w[0].dstSet = bindless;
1389 w[0].dstBinding = 15;
1390 w[0].descriptorCount = 1;
1391 w[0].descriptorType = vk::DescriptorType::eCombinedImageSampler;
1392 w[0].pImageInfo = &visIDInfo;
1393 w[1].dstSet = bindless;
1394 w[1].dstBinding = 16;
1395 w[1].descriptorCount = 1;
1396 w[1].descriptorType = vk::DescriptorType::eCombinedImageSampler;
1397 w[1].pImageInfo = &visBaryInfo;
1398 device->updateDescriptorSets(2,
w, 0,
nullptr);
1401 bindVgFrameBindless(bindless, currentFrameSlot() % kAsyncResourceCopies);
1403 GpuCullParams params{};
1405 gpuDrivenFrustumPlanes(
viewProj, params.frustumPlanes);
1406 params.cameraPos = glm::vec4(
eye, 0.f);
1407 const int w = gpuDrivenCullWidth;
1408 const int h = gpuDrivenCullHeight;
1409 params.screen = glm::vec4(
float(
w),
float(
h), 1.f /
float(
w), 1.f /
float(
h));
1410 const float fovRad = fovYDeg * 0.017453292519943295f;
1411 params.clipNearFar =
1412 glm::vec4(nearZ, farZ,
float(
h) * 0.5f / std::tan(
fovRad * 0.5f), 1.f);
1414 glm::vec4(
float(gpuDrivenCullMaxMip), 0.f,
float(
w),
float(
h));
1415 params.counts = glm::uvec4(gpuDrivenCullInstanceCount_, gpuDrivenBucketCount_,
1418 void *pMap = slot.cullParams.map();
1419 std::memcpy(pMap, ¶ms,
sizeof(params));
1420 slot.cullParams.unmap();
1423 auto bufWrite = [&](uint32_t binding, vk::Buffer buffer, vk::DeviceSize offset,
1424 vk::DeviceSize size, vk::DescriptorType
type) {
1425 vk::DescriptorBufferInfo info{buffer, offset, size};
1426 vk::WriteDescriptorSet
w{};
1427 w.dstSet = bindless;
1428 w.dstBinding = binding;
1429 w.descriptorCount = 1;
1430 w.descriptorType =
type;
1431 w.pBufferInfo = &info;
1432 device->updateDescriptorSets(1, &
w, 0,
nullptr);
1434 bufWrite(6, slot.cullParams.buffer, 0,
sizeof(GpuCullParams),
1435 vk::DescriptorType::eUniformBuffer);
1436 bufWrite(7, slot.visibleFlags.buffer, 0, VK_WHOLE_SIZE,
1437 vk::DescriptorType::eStorageBuffer);
1438 bufWrite(8, slot.compacted.buffer, 0, VK_WHOLE_SIZE, vk::DescriptorType::eStorageBuffer);
1439 bufWrite(9, slot.indirect.buffer, 0, VK_WHOLE_SIZE, vk::DescriptorType::eStorageBuffer);
1440 bufWrite(30, slot.indirectNI.buffer, 0, VK_WHOLE_SIZE, vk::DescriptorType::eStorageBuffer);
1441 bufWrite(14, slot.bucketCounters.buffer, 0, VK_WHOLE_SIZE,
1442 vk::DescriptorType::eStorageBuffer);
1444 bufWrite(4, slot.compacted.buffer, 0, VK_WHOLE_SIZE, vk::DescriptorType::eStorageBuffer);
1446 bufWrite(10, slot.hzb.buffer, 0, VK_WHOLE_SIZE, vk::DescriptorType::eStorageBuffer);
1448 auto *gbSlot = currentGBufferSlot();
1449 if (gbSlot && gbSlot->depthGpu.sampler && gbSlot->depthGpu.imageView()) {
1450 vk::DescriptorImageInfo depthInfo{gbSlot->depthGpu.sampler, gbSlot->depthGpu.imageView(),
1451 vk::ImageLayout::eShaderReadOnlyOptimal};
1452 vk::WriteDescriptorSet wd{};
1453 wd.dstSet = bindless;
1455 wd.descriptorCount = 1;
1456 wd.descriptorType = vk::DescriptorType::eCombinedImageSampler;
1457 wd.pImageInfo = &depthInfo;
1458 device->updateDescriptorSets(1, &wd, 0,
nullptr);
1465 cb.bindDescriptorSets(vk::PipelineBindPoint::eCompute, gpuDrivenComputeLayout, 1, 1,
1466 &bindless, 0,
nullptr);
1468 recordGpuDrivenHzbBuild();
1472 vk::BufferMemoryBarrier bmb{};
1473 bmb.buffer = slot.hzb.buffer;
1474 bmb.size = VK_WHOLE_SIZE;
1475 bmb.srcAccessMask = vk::AccessFlagBits::eShaderWrite;
1476 bmb.dstAccessMask = vk::AccessFlagBits::eShaderRead;
1477 cb.pipelineBarrier(vk::PipelineStageFlagBits::eComputeShader,
1478 vk::PipelineStageFlagBits::eComputeShader, {}, 0,
nullptr, 1, &bmb, 0,
1484 float fovYDeg,
float nearZ,
float farZ) {
1485 gpuDrivenRecordComputeSection(
viewProj,
eye, fovYDeg, nearZ, farZ);
1489 float nearZ,
float farZ) {
1490 if (!gpuDrivenCullReady_ || gpuDrivenCullInstanceCount_ == 0)
return;
1491 gpuDrivenLastCullSlot_ = uint32_t(currentFrameSlot());
1492 auto &slot = currentGpuDrivenCullSlot();
1493 auto &cb = currentPresentCb();
1496 const vk::DescriptorSet bindless = bindlessSetForFrame();
1497 if (!bindless)
return;
1501 void *flagsMap = slot.visibleFlags.map();
1503 slot.visibleFlags.unmap();
1504 void *counterMap = slot.bucketCounters.map();
1506 slot.bucketCounters.unmap();
1510 void *cmdMap = slot.indirect.map();
1512 slot.indirect.unmap();
1515 gpuDrivenRecordComputeSection(
viewProj,
eye, fovYDeg, nearZ, farZ);
1517 const uint32_t groups = (gpuDrivenCullInstanceCount_ + 63u) / 64u;
1520 vk::BufferMemoryBarrier bmb{};
1522 bmb.size = VK_WHOLE_SIZE;
1523 bmb.srcAccessMask = vk::AccessFlagBits::eHostWrite | vk::AccessFlagBits::eShaderWrite;
1524 bmb.dstAccessMask = vk::AccessFlagBits::eShaderRead;
1525 cb.pipelineBarrier(vk::PipelineStageFlagBits::eHost |
1526 vk::PipelineStageFlagBits::eComputeShader,
1527 vk::PipelineStageFlagBits::eComputeShader, {}, 0,
nullptr, 1, &bmb, 0,
1530 cullPass_.
record(cb, groups, 1, 1);
1534 vk::BufferMemoryBarrier bmb{};
1535 bmb.buffer = slot.visibleFlags.buffer;
1536 bmb.size = VK_WHOLE_SIZE;
1537 bmb.srcAccessMask = vk::AccessFlagBits::eShaderWrite;
1538 bmb.dstAccessMask = vk::AccessFlagBits::eShaderRead;
1539 cb.pipelineBarrier(vk::PipelineStageFlagBits::eComputeShader,
1540 vk::PipelineStageFlagBits::eComputeShader, {}, 0,
nullptr, 1, &bmb, 0,
1543 emitPass_.
record(cb, groups, 1, 1);
1547 vk::BufferMemoryBarrier bmb[2]{};
1548 bmb[0].buffer = slot.compacted.buffer;
1549 bmb[0].size = VK_WHOLE_SIZE;
1550 bmb[0].srcAccessMask = vk::AccessFlagBits::eShaderWrite;
1551 bmb[0].dstAccessMask = vk::AccessFlagBits::eShaderRead;
1552 bmb[1].buffer = slot.indirect.buffer;
1553 bmb[1].size = VK_WHOLE_SIZE;
1554 bmb[1].srcAccessMask = vk::AccessFlagBits::eShaderWrite;
1555 bmb[1].dstAccessMask = vk::AccessFlagBits::eIndirectCommandRead;
1556 cb.pipelineBarrier(vk::PipelineStageFlagBits::eComputeShader,
1557 vk::PipelineStageFlagBits::eVertexShader |
1558 vk::PipelineStageFlagBits::eDrawIndirect,
1559 {}, 0,
nullptr, 2, bmb, 0,
nullptr);
1564 if (!gpuDrivenScenePassPending_)
return;
1565 gpuDrivenScenePassPending_ =
false;
1566 if (beginSceneColorRenderPass()) {
1567 ensureScenePassPipelines(activeScenePass(), activeSceneSamples());
1569 ensureScenePassPipelines(renderpass, vk::SampleCountFlagBits::e1);
1570 beginSwapchainColorPass();
1572 swapchainPassOpen =
true;
1575Graphics::GpuDrivenFrameSet0 Graphics::gpuDrivenFrameSet0() {
1577 ensureFlatNormalTexture3D();
1578 ensureFlatHeightTexture3D();
1579 ensureDefaultEnvCubemap();
1582 auto *gpuNormal =
static_cast<GpuTexture *
>(mesh3dNormalTexture ? mesh3dNormalTexture->
gpuHandle
1584 auto *gpuHeight =
static_cast<GpuTexture *
>(mesh3dHeightTexture ? mesh3dHeightTexture->
gpuHandle
1586 Texture *envTex = mesh3dEnvTexture ? mesh3dEnvTexture : defaultEnvCubemap;
1591 ubo.
model = glm::mat4(1.f);
1592 ubo.
tint = glm::vec4(1.f);
1594 ubo.
lightDir.w = float(lightCount);
1597 ubo.
ambient = glm::vec4(glm::vec3(mesh3dLighting.
ambient), mesh3dMetallic);
1598 for (
int i = 0; i < lightCount; ++i) ubo.
lights[i] = mesh3dLighting.
lights[i];
1600 for (
int i = 0; i < lightCount; ++i) {
1608 if (glm::length(
d) < 1e-6f)
d = glm::vec3(0.f, 1.f, 0.f);
1609 else d = glm::normalize(
d);
1610 ubo.
lightDir = glm::vec4(
d,
float(lightCount));
1612 glm::vec4(glm::vec3(mesh3dLighting.
lights[dirI].
color), mesh3dEnvIntensity);
1614 ubo.
lightDir = glm::vec4(0.f, 1.f, 0.f,
float(lightCount));
1615 ubo.
lightColor = glm::vec4(0.f, 0.f, 0.f, mesh3dEnvIntensity);
1617 auto &fslots = currentMesh3dFrameSlots();
1618 if (fslots.drawIndex >= fslots.capacity) {
1619 std::fprintf(stderr,
"[vulkan] mesh3d UBO ring exhausted; gpu-driven draw skipped\n");
1622 const size_t slot = fslots.drawIndex++;
1623 ensureMesh3dStrides();
1624 const uint32_t uboOffset = uint32_t(slot) * mesh3dUboStride;
1625 const uint32_t shadowOffset = uint32_t(slot) * shadowUboStride;
1626 updateRingLocal(fslots.uboRing, uboOffset, &ubo,
sizeof(ubo));
1627 ShadowUBO shadowUbo = mesh3dShadows.
ubo;
1628 if (!mesh3dShadows.
active) {
1629 shadowUbo.
bias.y = 0.f;
1630 shadowUbo.splits.w = 0.f;
1632 shadowUbo.bias.z = mesh3dShadowReceive ? 1.f : 0.f;
1633 updateRingLocal(fslots.shadowRing, shadowOffset, &shadowUbo,
sizeof(shadowUbo));
1634 vk::DescriptorSet set = mesh3dSetFor(gpuTex, gpuNormal, gpuEnv, gpuHeight, gpuDepth, fslots);
1635 return {set, uboOffset, shadowOffset};
1639 if (!gpuDrivenCullReady_ || gpuDrivenBucketCount_ == 0)
return;
1640 if (!swapchainPassOpen && !sceneColorPassOpen)
return;
1641 auto &slot = currentGpuDrivenCullSlot();
1643 const Graphics::GpuDrivenFrameSet0 s0 = gpuDrivenFrameSet0();
1644 if (!s0.set)
return;
1645 const uint32_t dynOffsets[2] = {s0.uboOffset, s0.shadowOffset};
1647 auto &cb = currentPresentCb();
1648 cb.bindPipeline(vk::PipelineBindPoint::eGraphics, mesh3dGpuDrivenPipeline);
1649 cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, mesh3dGpuDrivenPipelineLayout, 0, 1,
1650 &s0.set, 2, dynOffsets);
1651 const vk::DescriptorSet bindless = bindlessSetForFrame();
1652 if (!bindless)
return;
1653 cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, mesh3dGpuDrivenPipelineLayout, 1, 1,
1654 &bindless, 0,
nullptr);
1658 for (uint32_t
b = 0;
b < gpuDrivenBucketCount_; ++
b) {
1659 const uint32_t meshId = gpuDrivenBucketMeshIds_[
b];
1660 if (meshId >= meshRecordOwners_.size())
continue;
1662 if (
mesh != boundMesh) {
1663 const vk::DeviceSize vbOffset = 0;
1664 cb.bindVertexBuffers(0, 1,
mesh->vertices, &vbOffset);
1665 cb.bindIndexBuffer(
mesh->indices.buffer, 0,
mesh->indexType);
1668 cb.drawIndexedIndirect(slot.indirect.buffer, vk::DeviceSize(
b) * stride, 1, stride);
1673 if (!gpuDrivenCullReady_)
return;
1674 if (gpuDrivenBucketCount_ == 0 && !vgAnyThisFrame_)
return;
1677 createGBufferResources(gbufferWidth > 0 ? gbufferWidth : int(swapchain.extent.width),
1678 gbufferHeight > 0 ? gbufferHeight : int(swapchain.extent.height));
1679 auto *slot = currentGBufferSlot();
1680 if (!slot || !gbufferVisPipeline || !gbufferVisRenderPass || !slot->visFramebuffer)
return;
1681 auto &cull = currentGpuDrivenCullSlot();
1682 auto &cb = currentPresentCb();
1686 const uint32_t
w = uint32_t(gbufferWidth);
1687 const uint32_t
h = uint32_t(gbufferHeight);
1688 std::array<vk::ClearValue, 6> clears{};
1689 clears[0].color = vk::ClearColorValue(std::array<float, 4>{0, 0, 0, 0});
1690 clears[1].color = vk::ClearColorValue(std::array<float, 4>{1, 1, 1, 1});
1691 clears[2].color = vk::ClearColorValue(std::array<float, 4>{0, 0, 0, 0});
1692 clears[3].color = vk::ClearColorValue(std::array<uint32_t, 4>{0xFFFFFFFFu, 0
u, 0
u, 0
u});
1693 clears[4].color = vk::ClearColorValue(std::array<float, 4>{0, 0, 0, 0});
1694 clears[5].depthStencil = vk::ClearDepthStencilValue{1.0f, 0};
1695 vk::RenderPassBeginInfo rpBegin{};
1696 rpBegin.renderPass = gbufferVisRenderPass;
1697 rpBegin.framebuffer = slot->visFramebuffer;
1698 rpBegin.renderArea = vk::Rect2D{{0, 0}, {
w,
h}};
1699 rpBegin.clearValueCount = uint32_t(clears.size());
1700 rpBegin.pClearValues = clears.data();
1701 slot->normal.beginColorAttachment();
1702 slot->depthColor.beginColorAttachment();
1703 slot->albedo.beginColorAttachment();
1704 slot->visID.beginColorAttachment();
1705 slot->visBary.beginColorAttachment();
1706 slot->depth.beginDepthAttachment();
1707 cb.beginRenderPass(rpBegin, vk::SubpassContents::eInline);
1708 setViewportAndScissor(cb,
w,
h);
1710 const Graphics::GpuDrivenFrameSet0 s0 = gpuDrivenFrameSet0();
1711 const vk::DescriptorSet bindless = bindlessSetForFrame();
1712 if (s0.set && bindless) {
1713 const uint32_t dynOffsets[2] = {s0.uboOffset, s0.shadowOffset};
1714 cb.bindPipeline(vk::PipelineBindPoint::eGraphics, gbufferVisPipeline);
1715 cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, mesh3dGpuDrivenPipelineLayout, 0,
1716 1, &s0.set, 2, dynOffsets);
1717 cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, mesh3dGpuDrivenPipelineLayout, 1,
1718 1, &bindless, 0,
nullptr);
1719 const vk::DeviceSize stride =
sizeof(glm::uvec4);
1720 for (uint32_t
b = 0;
b < gpuDrivenBucketCount_; ++
b) {
1721 cb.drawIndirect(cull.indirectNI.buffer, vk::DeviceSize(
b) * stride, 1, stride);
1723 if (vgAnyThisFrame_) drawVgClusters(cb);
1726 slot->normal.endSampledLayout();
1727 slot->depthColor.endSampledLayout();
1728 slot->albedo.endSampledLayout();
1729 slot->visID.endSampledLayout();
1730 slot->visBary.endSampledLayout();
1731 slot->depth.endSampledLayout();
1735 if (!gpuDrivenCullReady_)
return;
1736 if (gpuDrivenBucketCount_ == 0 && !vgAnyThisFrame_)
return;
1737 if (!swapchainPassOpen && !sceneColorPassOpen)
return;
1738 if (!resolveVisPipeline || !mesh3dGpuDrivenPipelineLayout)
return;
1739 auto *slot = currentGBufferSlot();
1740 if (!slot || !slot->visIDGpu.sampler || !slot->visBaryGpu.sampler)
return;
1741 auto &cb = currentPresentCb();
1742 const vk::DescriptorSet bindless = bindlessSetForFrame();
1743 if (!bindless)
return;
1745 const Graphics::GpuDrivenFrameSet0 s0 = gpuDrivenFrameSet0();
1746 if (!s0.set)
return;
1747 const uint32_t dynOffsets[2] = {s0.uboOffset, s0.shadowOffset};
1748 cb.bindPipeline(vk::PipelineBindPoint::eGraphics, resolveVisPipeline);
1749 cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, mesh3dGpuDrivenPipelineLayout, 0, 1,
1750 &s0.set, 2, dynOffsets);
1751 cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, mesh3dGpuDrivenPipelineLayout, 1, 1,
1752 &bindless, 0,
nullptr);
1754 uint32_t(sceneColorPassOpen ? sceneColorWidth : int(swapchain.extent.width));
1756 uint32_t(sceneColorPassOpen ? sceneColorHeight : int(swapchain.extent.height));
1757 setViewportAndScissor(cb,
w,
h);
1758 cb.draw(3, 1, 0, 0);
1761void Graphics::createResolveVisPipeline(
const vkb::BuiltRenderPass &rp,
1762 vk::SampleCountFlagBits samples) {
1763 destroyPipeline(device, resolveVisPipeline);
1764 if (!mesh3dGpuDrivenPipelineLayout)
return;
1765 std::vector<uint32_t>
vert(resolve_vis_vert_spv, resolve_vis_vert_spv +
1766 resolve_vis_vert_spv_count);
1767 std::vector<uint32_t>
frag(resolve_vis_frag_spv, resolve_vis_frag_spv +
1768 resolve_vis_frag_spv_count);
1769 vk::ShaderModule vertModule = vkb::PipelineBuilder::createShaderModule(device.instance,
vert);
1770 vk::ShaderModule fragModule = vkb::PipelineBuilder::createShaderModule(device.instance,
frag);
1771 resolveVisPipeline =
1772 device.createPipeline()
1773 .useClassicPipeline(vertModule, fragModule)
1774 .setPipelineLayout(mesh3dGpuDrivenPipelineLayout)
1775 .setDynamicStatesViewportScissor()
1776 .setRasterizer(vk::PolygonMode::eFill,
false,
false, 1.0f,
1777 vk::CullModeFlagBits::eNone, vk::FrontFace::eClockwise)
1778 .setMultisampler(
false, samples)
1779 .setDepthStencil(
true,
true, vk::CompareOp::eLessOrEqual)
1780 .setColorAttachmentCount(1)
1782 device->destroyShaderModule(vertModule);
1783 device->destroyShaderModule(fragModule);
1786void Graphics::recordVgCull() {
1787 if (!gpuDrivenCullReady_ || vgAssetCount_ == 0 || vgClusterCount_ == 0)
return;
1788 if (!vgCullPass_.
pipeline() || !vgGpu_.visible.buffer)
return;
1789 auto &cb = currentPresentCb();
1790 const vk::DescriptorSet bindless = bindlessSetForFrame();
1791 if (!bindless)
return;
1793 const size_t slot = currentFrameSlot() % kAsyncResourceCopies;
1794 vgLastVisible_ = uint32_t(slot);
1799 const vk::DeviceSize slotVis = (vk::DeviceSize(kMaxVgClusters) + 1) *
sizeof(uint32_t);
1800 void *map = vgGpu_.visible.map();
1802 std::memset(
static_cast<char *
>(map) + slotVis * slot, 0,
sizeof(uint32_t));
1803 vgGpu_.visible.unmap();
1806 cb.bindDescriptorSets(vk::PipelineBindPoint::eCompute, gpuDrivenComputeLayout, 1, 1,
1807 &bindless, 0,
nullptr);
1808 const uint32_t
push[4]{vgClusterCount_, 0
u, 0
u, 0
u};
1809 cb.pushConstants(gpuDrivenComputeLayout, vk::ShaderStageFlagBits::eCompute, 0,
sizeof(
push),
1811 vgCullPass_.
record(cb, (vgClusterCount_ + 63u) / 64u);
1815 vk::BufferMemoryBarrier bmb[2]{};
1816 bmb[0].buffer = vgGpu_.visible.buffer;
1817 bmb[0].size = VK_WHOLE_SIZE;
1818 bmb[0].srcAccessMask = vk::AccessFlagBits::eShaderWrite;
1819 bmb[0].dstAccessMask = vk::AccessFlagBits::eShaderRead;
1820 bmb[1].buffer = vgGpu_.indirect.buffer;
1821 bmb[1].size = VK_WHOLE_SIZE;
1822 bmb[1].srcAccessMask = vk::AccessFlagBits::eShaderWrite;
1823 bmb[1].dstAccessMask = vk::AccessFlagBits::eIndirectCommandRead;
1824 cb.pipelineBarrier(vk::PipelineStageFlagBits::eComputeShader,
1825 vk::PipelineStageFlagBits::eVertexShader |
1826 vk::PipelineStageFlagBits::eDrawIndirect,
1827 {}, 0,
nullptr, 2, bmb, 0,
nullptr);
1831void Graphics::drawVgClusters(vk::CommandBuffer cb) {
1832 if (!gbufferVgVisPipeline || vgAssetCount_ == 0 || vgClusterCount_ == 0)
return;
1833 const vk::DescriptorSet bindless = bindlessSetForFrame();
1834 if (!bindless || !mesh3dGpuDrivenPipelineLayout)
return;
1837 const size_t slot = currentFrameSlot() % kAsyncResourceCopies;
1838 const vk::DeviceSize slotInd = vk::DeviceSize(kMaxVgClusters) *
sizeof(glm::uvec4);
1839 const vk::DeviceSize slotVis = (vk::DeviceSize(kMaxVgClusters) + 1) *
sizeof(uint32_t);
1840 cb.bindPipeline(vk::PipelineBindPoint::eGraphics, gbufferVgVisPipeline);
1841 cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, mesh3dGpuDrivenPipelineLayout, 1, 1,
1842 &bindless, 0,
nullptr);
1843 cb.drawIndirectCount(vgGpu_.indirect.buffer, slotInd * slot, vgGpu_.visible.buffer,
1844 slotVis * slot, kMaxVgClusters,
sizeof(glm::uvec4));
1848 if (!gpuDrivenCullReady_ || gpuDrivenCullSlots_.empty())
return 0;
1849 auto &slot = gpuDrivenCullSlots_[gpuDrivenLastCullSlot_ % gpuDrivenCullSlots_.size()];
1851 void *map = slot.visibleFlags.map();
1854 const auto *flags =
static_cast<const uint32_t *
>(map);
1855 for (uint32_t i = 0; i <
n; ++i) count += flags[i] != 0 ? 1u : 0
u;
1856 slot.visibleFlags.unmap();
1861 if (!gpuDrivenCullReady_ || gpuDrivenCullSlots_.empty())
return 0;
1862 auto &slot = gpuDrivenCullSlots_[gpuDrivenLastCullSlot_ % gpuDrivenCullSlots_.size()];
1864 void *map = slot.indirect.map();
1868 for (uint32_t
b = 0;
b <
n; ++
b) count += cmds[
b].instanceCount > 0 ? 1u : 0
u;
1869 slot.indirect.unmap();
1880 return static_cast<GpuMesh *
>(
mesh->gpuHandle)->gpuRecordIndex;
1883void Graphics::createMesh3DGpuDrivenPipeline() {
1884 if (mesh3dGpuDrivenPipeline)
return;
1885 if (!mesh3dSetLayout || !bindlessSetLayout_ || !gpuDrivenCaps_.
gpuDrivenAvailable())
return;
1890 std::array<vk::DescriptorSetLayout, 2> setLayouts{mesh3dSetLayout, bindlessSetLayout_};
1891 vk::PipelineLayoutCreateInfo pli{};
1892 pli.setLayoutCount = uint32_t(setLayouts.size());
1893 pli.pSetLayouts = setLayouts.data();
1894 mesh3dGpuDrivenPipelineLayout = device->createPipelineLayout(pli);
1895 mesh3dGpuDrivenPipeline =
1896 createMesh3DStylePipeline(embeddedSpirv(mesh3d_gpudriven_vert_spv),
1897 embeddedSpirv(mesh3d_gpudriven_frag_spv),
1898 mesh3dGpuDrivenPipelineLayout, renderpass,
1899 vk::SampleCountFlagBits::e1);
#define EV_ASSERT(cond,...)
Assert an internal engine invariant (state that must always hold).
std::vector< std::uint32_t > verts
std::unique_ptr< RenderControl > renderControl_
CPU-side builder for GPU-driven indirect draws (stage 1).
uint32_t build(const std::vector< GpuMeshRecord > &meshTable)
Sort + merge. meshTable supplies indexCount/firstIndex/vertexBase.
void add(uint32_t seq, uint32_t meshId, uint32_t materialId, uint32_t pipelineId)
seq = monotonic instance sequence (stable order tiebreak).
const std::vector< GpuIndirectCommand > & commands() const
const std::vector< uint32_t > & sortedInstanceOrder() const
Instance indices in the sorted order the caller must upload.
Packages shading method + surface parameters into one attachable asset.
GPU mesh handle (+ optional CPU morph targets).
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
bool create(vkb::Device &device, vk::PipelineLayout layout, const std::vector< uint32_t > &spv)
Create from embedded SPIR-V words; layout must outlive the pass.
vk::Pipeline pipeline() const
void record(vk::CommandBuffer cb, uint32_t groupsX, uint32_t groupsY=1, uint32_t groupsZ=1) const
Record one dispatch (local size baked into the shader).
Per-frame GPU allocation arena (host-visible coherent).
vk::Buffer buffer() const
bool gpuDrivenCullEnabled() const
Stage 2 cull is live for this frame (GPU-written commands).
uint32_t gpuDrivenMeshRecord(Mesh *mesh) override
GPU mesh-table slot for a mesh (kInvalidGpuDrivenSlot when not uploaded).
uint32_t gpuDrivenVgAssetId(Mesh *mesh) const override
VG asset id attached to a mesh (kInvalidGpuDrivenSlot when none).
uint32_t debugMeshRecordIndex(Mesh *mesh) const
bool gpuDrivenVgSetInstance(uint32_t vgAssetId, const glm::mat4 &model, uint32_t materialId) override
void gpuDrivenVgComputeSection(const glm::mat4 &viewProj, const glm::vec3 &eye, float fovYDeg, float nearZ, float farZ) override
Record the HZB build + cull-params section (VG-only frames).
bool gpuDrivenMaterialUsable(Material *material) override
Whether a material can be shaded by the GPU-driven opaque path. Backends/drivers with descriptor-inde...
void syncMaterialTable()
Upload all registered material records to the GPU table.
uint32_t debugGpuDrivenCulledDrawCount() const
uint32_t debugGpuDrivenVgVisibleCount() const
Debug readback: visible VG clusters from the last cull.
uint32_t materialTableGetOrCreate(eve::graphics::Material *material)
Get (or lazily create) the GPU material-table slot for a material.
bool gpuDrivenCullBegin(const GpuInstance *instances, uint32_t instanceCount)
Upload sorted instances + bucket metadata for the cull chain.
void gpuDrivenResolve() override
Record the fullscreen vis resolve inside the open scene color pass.
bool gpuDrivenResolveWanted() const override
Stage 3: vis+resolve live for this frame (opt-in, 1x scene pass).
void gpuDrivenDrawOpaque()
Draw the opaque geometry with GPU-written indirect commands.
bool gpuDrivenEnabled() const override
Whether the GPU-driven opaque path is currently enabled.
uint32_t gpuDrivenVgUpload(const GpuVgAssetUpload &asset) override
Upload a virtual-geometry asset into the shared cluster pool.
void gpuDrivenOpenScenePass()
Open the scene color pass that begin3DFrame deferred (cull path).
uint32_t debugBindlessIndex(Texture *tex) const
Test/debug helpers (valid when the GPU-driven path is live).
uint32_t debugGpuDrivenVisibleCount() const
Debug readback: visible instances / non-empty buckets from the last cull.
void gpuDrivenRecordVisPass() override
Record the GBuffer vis pass (opaque indirect draws write visID/visBary).
bool gpuDrivenVgAttachToMesh(Mesh *mesh, uint32_t vgAssetId) override
bool gpuDrivenSubmitOpaque(const GpuInstance *instances, uint32_t instanceCount) override
Upload + record GPU-driven opaque draws (call inside the open 3D frame). The backend sorts instances ...
void gpuDrivenCullEmit(const glm::mat4 &viewProj, const glm::vec3 &eye, float fovYDeg, float nearZ, float farZ)
Record the cull + emit compute dispatches for the current frame.
FrameArena & currentFrameArena()
Per-frame arena for the current swapchain frame slot.
constexpr uint32_t kMaxGpuDrivenBuckets
eve::graphics::GpuIndirectCommand GpuIndirectCommand
constexpr uint32_t kMaxHzbMips
constexpr uint32_t kMaxBindlessCubemaps
constexpr uint32_t kMaxGpuDrivenInstances
constexpr uint32_t kInvalidBindlessSlot
constexpr uint32_t kHZBHeaderWords
constexpr uint32_t kMaxBindlessTextures
eve::graphics::GpuMeshRecord GpuMeshRecord
GPU-driven rendering shared constants + std430 GPU layouts.
eve::graphics::GpuMaterialRecord GpuMaterialRecord
eve::graphics::GpuInstance GpuInstance
Indirect draw command; layout identical to VkDrawIndexedIndirectCommand.
Per-instance GPU record (std430). Mirrors GLSL GpuInstance.
GPU material table record (std430). Mirrors GLSL GpuMaterialRecord.
glm::vec4 boundsCenterRadius
Neutral GPU upload for one virtual-geometry asset. Raw arrays so the graphics module does not depend ...
const std::uint32_t * triangles
const GpuVgCluster * clusters
GPU-packed cluster node (std430, 4 x uvec4). Mirrors the virtualgeometry module's VgGpuCluster layout...
static constexpr int kMaxLights
Light3DGpu lights[kMaxLights]
bool gpuDrivenAvailable() const
bool gpuDrivenCullAvailable() const
Stage 2: GPU frustum/HZB cull + GPU-written indirect commands.
vkb::HostVertexBuffer vertices
uint32_t gpuRecordIndex
Index into the GPU mesh table (GpuMeshRecord).
GpuMeshRecord record
CPU-side record for this mesh (bounds/ranges); uploaded by registerMeshRecord.
Light3DGpu lights[Lighting3DPack::kMaxLights]