12#include "graphics/shaders/rt_reflection_rchit_spv.inc"
13#include "graphics/shaders/rt_reflection_rgen_spv.inc"
14#include "graphics/shaders/rt_reflection_rmiss_spv.inc"
27uint32_t alignedSize(uint32_t
value, uint32_t alignment) {
28 if (alignment == 0)
return value;
29 return (
value + alignment - 1u) & ~(alignment - 1u);
32vk::TransformMatrixKHR toTransformMatrix(
const glm::mat4&
m) {
34 vk::TransformMatrixKHR out{};
35 const float* src = &
m[0][0];
36 out.matrix[0][0] = src[0];
37 out.matrix[0][1] = src[4];
38 out.matrix[0][2] = src[8];
39 out.matrix[0][3] = src[12];
40 out.matrix[1][0] = src[1];
41 out.matrix[1][1] = src[5];
42 out.matrix[1][2] = src[9];
43 out.matrix[1][3] = src[13];
44 out.matrix[2][0] = src[2];
45 out.matrix[2][1] = src[6];
46 out.matrix[2][2] = src[10];
47 out.matrix[2][3] = src[14];
58 memory = vk::DeviceMemory{};
70 o.buffer = vk::Buffer{};
71 o.memory = vk::DeviceMemory{};
83 vk::BufferCreateInfo ci{};
85 ci.usage =
usage | vk::BufferUsageFlagBits::eShaderDeviceAddress;
86 ci.sharingMode = vk::SharingMode::eExclusive;
87 buffer = (*device)->createBuffer(ci,
device->allocation_callbacks);
90 const auto memreq = (*device)->getBufferMemoryRequirements(
buffer);
92 vk::MemoryAllocateFlagsInfo flagsInfo{};
93 flagsInfo.flags = vk::MemoryAllocateFlagBits::eDeviceAddress;
95 vk::MemoryAllocateInfo allocInfo{};
96 allocInfo.pNext = &flagsInfo;
97 allocInfo.allocationSize = memreq.size;
98 allocInfo.memoryTypeIndex =
99 device->physical_device.findMemoryTypeIndex(memreq.memoryTypeBits, vk::MemoryPropertyFlagBits::eDeviceLocal);
100 memory = (*device)->allocateMemory(allocInfo,
device->allocation_callbacks);
103 vk::BufferDeviceAddressInfo addrInfo{};
105 address = (*device)->getBufferAddress(addrInfo);
115 vk::DeviceSize
bytes) {
121 using pfb = vk::MemoryPropertyFlagBits;
122 vkb::GenericBuffer staging(*
device, vk::BufferUsageFlagBits::eTransferSrc,
bytes,
123 pfb::eHostVisible | pfb::eHostCoherent);
124 staging.updateLocal(vkb::FrameSlot::gpuIdle(), data,
bytes);
126 auto cmd = (*device)->allocateCommandBuffers({pool, vk::CommandBufferLevel::ePrimary, 1}).front();
127 cmd.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
128 vk::BufferCopy copy{};
130 cmd.copyBuffer(staging.buffer,
buffer, copy);
132 vk::SubmitInfo submit{};
133 submit.commandBufferCount = 1;
134 submit.pCommandBuffers = &cmd;
135 queue.submit(submit, vk::Fence{});
137 (*device)->freeCommandBuffers(pool, cmd);
143 (*device)->destroyAccelerationStructureKHR(
handle,
device->allocation_callbacks);
144 handle = vk::AccelerationStructureKHR{};
153 storage = std::move(o.storage);
154 deviceAddress = o.deviceAddress;
156 o.handle = vk::AccelerationStructureKHR{};
169 auto* base = eve::ModuleManager::getInstance<eve::graphics::Graphics>(
"Graphics");
170 if (!base) base = eve::graphics::Graphics::create();
172 if (!vg || !vg->supportsRayTracing()) {
173 if (vg) caps_ = vg->rayTracingCaps();
177 vkb::Device*
current = &vg->getDevice();
181 vg->getDevice().getQueue(vkb::QueueType::graphics));
190 auto* base = eve::ModuleManager::getInstance<eve::graphics::Graphics>(
"Graphics");
197 auto* base = eve::ModuleManager::getInstance<eve::graphics::Graphics>(
"Graphics");
203 vk::Queue graphicsQueue) {
207 uploadPool_ = uploadPool;
208 graphicsQueue_ = graphicsQueue;
214 if (pipeline_) (*device_)->destroyPipeline(pipeline_, device_->allocation_callbacks);
215 if (pipelineLayout_) (*device_)->destroyPipelineLayout(pipelineLayout_, device_->allocation_callbacks);
216 if (setLayout_) (*device_)->destroyDescriptorSetLayout(setLayout_, device_->allocation_callbacks);
217 if (descriptorPool_) (*device_)->destroyDescriptorPool(descriptorPool_, device_->allocation_callbacks);
218 if (sampler_) (*device_)->destroySampler(sampler_, device_->allocation_callbacks);
219 if (raygenModule_) (*device_)->destroyShaderModule(raygenModule_, device_->allocation_callbacks);
220 if (missModule_) (*device_)->destroyShaderModule(missModule_, device_->allocation_callbacks);
221 if (closestHitModule_) (*device_)->destroyShaderModule(closestHitModule_, device_->allocation_callbacks);
223 pipeline_ = vk::Pipeline{};
224 pipelineLayout_ = vk::PipelineLayout{};
225 setLayout_ = vk::DescriptorSetLayout{};
226 descriptorPool_ = vk::DescriptorPool{};
227 descriptorSet_ = vk::DescriptorSet{};
228 sampler_ = vk::Sampler{};
229 raygenModule_ = vk::ShaderModule{};
230 missModule_ = vk::ShaderModule{};
231 closestHitModule_ = vk::ShaderModule{};
232 outputImage_ = StorageColorImage{};
236 raygenRegion_ = vk::StridedDeviceAddressRegionKHR{};
237 missRegion_ = vk::StridedDeviceAddressRegionKHR{};
238 hitRegion_ = vk::StridedDeviceAddressRegionKHR{};
239 callableRegion_ = vk::StridedDeviceAddressRegionKHR{};
241 uploadPool_ = vk::CommandPool{};
242 graphicsQueue_ = vk::Queue{};
256 "addTriangleMesh: invalid geometry",
"graphics.raytracing"));
258 auto mesh = std::make_unique<TriangleMeshRecord>();
261 mesh->transform = transform;
263 const vk::DeviceSize vBytes = vk::DeviceSize(
vertexCount) * 3u *
sizeof(float);
264 const vk::DeviceSize iBytes = vk::DeviceSize(
indexCount) *
sizeof(uint32_t);
265 const auto usage = vk::BufferUsageFlagBits::eAccelerationStructureBuildInputReadOnlyKHR |
266 vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eTransferDst;
268 if (
auto r =
mesh->vertexBuffer.allocate(*device_, vBytes,
usage); !
r.ok())
270 if (
auto r =
mesh->indexBuffer.allocate(*device_, iBytes,
usage); !
r.ok())
272 if (
auto r =
mesh->vertexBuffer.upload(uploadPool_, graphicsQueue_, positionsXYZ, vBytes); !
r.ok())
274 if (
auto r =
mesh->indexBuffer.upload(uploadPool_, graphicsQueue_,
indices, iBytes); !
r.ok())
278 const uint32_t
id = uint32_t(meshes_.size());
279 meshes_.push_back(std::move(
mesh));
289 if (gpu->vertexCount < 3 || gpu->indexCount < 3 || (gpu->indexCount % 3u) != 0)
293 auto& vb = eve::graphics::vulkan::meshDrawVertices(*gpu);
298 std::vector<float>
positions(
size_t(gpu->vertexCount) * 3u);
300 void* mapped = vb.map();
302 for (uint32_t i = 0; i < gpu->vertexCount; ++i) {
310 std::vector<uint32_t>
indices(gpu->indexCount);
311 if (!gpu->cpuIndices.empty() && gpu->cpuIndices.size() >= gpu->indexCount) {
312 std::copy_n(gpu->cpuIndices.begin(), gpu->indexCount,
indices.begin());
314 auto& ib = eve::graphics::vulkan::meshDrawIndices(*gpu);
318 void* mapped = ib.map();
319 if (gpu->indexType == vk::IndexType::eUint16) {
320 const auto* src =
static_cast<const uint16_t*
>(mapped);
321 for (uint32_t i = 0; i < gpu->indexCount; ++i)
indices[i] = src[i];
323 std::memcpy(
indices.data(), mapped,
size_t(gpu->indexCount) *
sizeof(uint32_t));
332 vk::AccelerationStructureGeometryTrianglesDataKHR
triangles{};
333 triangles.vertexFormat = vk::Format::eR32G32B32Sfloat;
334 triangles.vertexData.deviceAddress =
mesh.vertexBuffer.address;
335 triangles.vertexStride =
sizeof(float) * 3;
337 triangles.indexType = vk::IndexType::eUint32;
338 triangles.indexData.deviceAddress =
mesh.indexBuffer.address;
340 vk::AccelerationStructureGeometryKHR geometry{};
341 geometry.geometryType = vk::GeometryTypeKHR::eTriangles;
342 geometry.flags = vk::GeometryFlagBitsKHR::eOpaque;
345 vk::AccelerationStructureBuildGeometryInfoKHR
buildInfo{};
346 buildInfo.type = vk::AccelerationStructureTypeKHR::eBottomLevel;
347 buildInfo.flags = vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace;
348 buildInfo.mode = vk::BuildAccelerationStructureModeKHR::eBuild;
352 const uint32_t primitiveCount =
mesh.indexCount / 3;
353 const auto sizes = (*device_)->getAccelerationStructureBuildSizesKHR(vk::AccelerationStructureBuildTypeKHR::eDevice,
356 auto storageResult =
mesh.blas.storage.allocate(
357 *device_, sizes.accelerationStructureSize,
358 vk::BufferUsageFlagBits::eAccelerationStructureStorageKHR | vk::BufferUsageFlagBits::eShaderDeviceAddress);
359 if (!storageResult.ok())
return storageResult;
361 vk::AccelerationStructureCreateInfoKHR createInfo{};
362 createInfo.buffer =
mesh.blas.storage.buffer;
363 createInfo.size = sizes.accelerationStructureSize;
364 createInfo.type = vk::AccelerationStructureTypeKHR::eBottomLevel;
365 mesh.blas.handle = (*device_)->createAccelerationStructureKHR(createInfo, device_->allocation_callbacks);
366 mesh.blas.device = device_;
368 vk::AccelerationStructureDeviceAddressInfoKHR addrInfo{};
369 addrInfo.accelerationStructure =
mesh.blas.handle;
370 mesh.blas.deviceAddress = (*device_)->getAccelerationStructureAddressKHR(addrInfo);
372 DeviceAddressBuffer scratch;
374 scratch.allocate(*device_, sizes.buildScratchSize,
375 vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eShaderDeviceAddress);
380 buildInfo.scratchData.deviceAddress = scratch.address;
382 vk::AccelerationStructureBuildRangeInfoKHR
range{};
383 range.primitiveCount = primitiveCount;
384 const vk::AccelerationStructureBuildRangeInfoKHR* ranges = &
range;
386 auto cmd = (*device_)->allocateCommandBuffers({uploadPool_, vk::CommandBufferLevel::ePrimary, 1}).front();
387 cmd.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
388 cmd.buildAccelerationStructuresKHR(
buildInfo, ranges);
390 vk::SubmitInfo submit{};
391 submit.commandBufferCount = 1;
392 submit.pCommandBuffers = &cmd;
393 graphicsQueue_.submit(submit, vk::Fence{});
394 graphicsQueue_.waitIdle();
395 (*device_)->freeCommandBuffers(uploadPool_, cmd);
399Result<void> VulkanRayTracing::buildTlas() {
403 std::vector<vk::AccelerationStructureInstanceKHR>
instances;
405 for (
const auto&
mesh : meshes_) {
406 vk::AccelerationStructureInstanceKHR inst{};
407 inst.transform = toTransformMatrix(
mesh->transform);
408 inst.instanceCustomIndex = 0;
410 inst.instanceShaderBindingTableRecordOffset = 0;
411 inst.flags = uint32_t(vk::GeometryInstanceFlagBitsKHR::eTriangleFacingCullDisable);
412 inst.accelerationStructureReference =
mesh->blas.deviceAddress;
416 DeviceAddressBuffer instanceBuffer;
418 if (
auto r = instanceBuffer.allocate(*device_,
bytes,
419 vk::BufferUsageFlagBits::eAccelerationStructureBuildInputReadOnlyKHR |
420 vk::BufferUsageFlagBits::eShaderDeviceAddress |
421 vk::BufferUsageFlagBits::eTransferDst);
424 if (
auto r = instanceBuffer.upload(uploadPool_, graphicsQueue_,
instances.data(),
bytes); !
r.ok())
return r;
426 vk::AccelerationStructureGeometryInstancesDataKHR instancesData{};
427 instancesData.arrayOfPointers = VK_FALSE;
428 instancesData.data.deviceAddress = instanceBuffer.address;
430 vk::AccelerationStructureGeometryKHR geometry{};
431 geometry.geometryType = vk::GeometryTypeKHR::eInstances;
432 geometry.geometry.instances = instancesData;
434 vk::AccelerationStructureBuildGeometryInfoKHR
buildInfo{};
435 buildInfo.type = vk::AccelerationStructureTypeKHR::eTopLevel;
436 buildInfo.flags = vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace;
437 buildInfo.mode = vk::BuildAccelerationStructureModeKHR::eBuild;
441 const uint32_t primitiveCount = uint32_t(
instances.size());
442 const auto sizes = (*device_)->getAccelerationStructureBuildSizesKHR(vk::AccelerationStructureBuildTypeKHR::eDevice,
446 *device_, sizes.accelerationStructureSize,
447 vk::BufferUsageFlagBits::eAccelerationStructureStorageKHR | vk::BufferUsageFlagBits::eShaderDeviceAddress);
451 vk::AccelerationStructureCreateInfoKHR createInfo{};
453 createInfo.size = sizes.accelerationStructureSize;
454 createInfo.type = vk::AccelerationStructureTypeKHR::eTopLevel;
455 tlas_.
handle = (*device_)->createAccelerationStructureKHR(createInfo, device_->allocation_callbacks);
458 vk::AccelerationStructureDeviceAddressInfoKHR addrInfo{};
459 addrInfo.accelerationStructure = tlas_.
handle;
460 tlas_.
deviceAddress = (*device_)->getAccelerationStructureAddressKHR(addrInfo);
462 DeviceAddressBuffer scratch;
464 scratch.allocate(*device_, sizes.buildScratchSize,
465 vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eShaderDeviceAddress);
470 buildInfo.scratchData.deviceAddress = scratch.address;
472 vk::AccelerationStructureBuildRangeInfoKHR
range{};
473 range.primitiveCount = primitiveCount;
474 const vk::AccelerationStructureBuildRangeInfoKHR* ranges = &
range;
476 auto cmd = (*device_)->allocateCommandBuffers({uploadPool_, vk::CommandBufferLevel::ePrimary, 1}).front();
477 cmd.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
478 cmd.buildAccelerationStructuresKHR(
buildInfo, ranges);
480 vk::SubmitInfo submit{};
481 submit.commandBufferCount = 1;
482 submit.pCommandBuffers = &cmd;
483 graphicsQueue_.submit(submit, vk::Fence{});
484 graphicsQueue_.waitIdle();
485 (*device_)->freeCommandBuffers(uploadPool_, cmd);
499 auto makeModule = [&](
const uint32_t* words,
size_t count) -> vk::ShaderModule {
500 vk::ShaderModuleCreateInfo ci{};
501 ci.codeSize =
count *
sizeof(uint32_t);
503 return (*device_)->createShaderModule(ci, device_->allocation_callbacks);
505 raygenModule_ = makeModule(rt_reflection_rgen_spv, rt_reflection_rgen_spv_count);
506 missModule_ = makeModule(rt_reflection_rmiss_spv, rt_reflection_rmiss_spv_count);
507 closestHitModule_ = makeModule(rt_reflection_rchit_spv, rt_reflection_rchit_spv_count);
509 vk::DescriptorSetLayoutBinding
bindings[5]{};
511 bindings[0].descriptorType = vk::DescriptorType::eAccelerationStructureKHR;
513 bindings[0].stageFlags = vk::ShaderStageFlagBits::eRaygenKHR;
515 bindings[1].descriptorType = vk::DescriptorType::eStorageImage;
517 bindings[1].stageFlags = vk::ShaderStageFlagBits::eRaygenKHR;
519 bindings[2].descriptorType = vk::DescriptorType::eCombinedImageSampler;
521 bindings[2].stageFlags = vk::ShaderStageFlagBits::eRaygenKHR;
523 bindings[3].descriptorType = vk::DescriptorType::eCombinedImageSampler;
525 bindings[3].stageFlags = vk::ShaderStageFlagBits::eRaygenKHR;
527 bindings[4].descriptorType = vk::DescriptorType::eCombinedImageSampler;
529 bindings[4].stageFlags = vk::ShaderStageFlagBits::eRaygenKHR;
531 vk::DescriptorSetLayoutCreateInfo layoutCi{};
532 layoutCi.bindingCount = 5;
534 setLayout_ = (*device_)->createDescriptorSetLayout(layoutCi, device_->allocation_callbacks);
536 vk::PushConstantRange push{};
537 push.stageFlags = vk::ShaderStageFlagBits::eRaygenKHR;
539 push.size =
sizeof(PushConstants);
541 vk::PipelineLayoutCreateInfo plCi{};
542 plCi.setLayoutCount = 1;
543 plCi.pSetLayouts = &setLayout_;
544 plCi.pushConstantRangeCount = 1;
545 plCi.pPushConstantRanges = &push;
546 pipelineLayout_ = (*device_)->createPipelineLayout(plCi, device_->allocation_callbacks);
548 std::array<vk::PipelineShaderStageCreateInfo, 3> stages{};
549 stages[0].stage = vk::ShaderStageFlagBits::eRaygenKHR;
550 stages[0].module = raygenModule_;
551 stages[0].pName =
"main";
552 stages[1].stage = vk::ShaderStageFlagBits::eMissKHR;
553 stages[1].module = missModule_;
554 stages[1].pName =
"main";
555 stages[2].stage = vk::ShaderStageFlagBits::eClosestHitKHR;
556 stages[2].module = closestHitModule_;
557 stages[2].pName =
"main";
559 std::array<vk::RayTracingShaderGroupCreateInfoKHR, 3>
groups{};
560 groups[0].type = vk::RayTracingShaderGroupTypeKHR::eGeneral;
561 groups[0].generalShader = 0;
562 groups[0].closestHitShader = VK_SHADER_UNUSED_KHR;
563 groups[0].anyHitShader = VK_SHADER_UNUSED_KHR;
564 groups[0].intersectionShader = VK_SHADER_UNUSED_KHR;
565 groups[1].type = vk::RayTracingShaderGroupTypeKHR::eGeneral;
566 groups[1].generalShader = 1;
567 groups[1].closestHitShader = VK_SHADER_UNUSED_KHR;
568 groups[1].anyHitShader = VK_SHADER_UNUSED_KHR;
569 groups[1].intersectionShader = VK_SHADER_UNUSED_KHR;
570 groups[2].type = vk::RayTracingShaderGroupTypeKHR::eTrianglesHitGroup;
571 groups[2].generalShader = VK_SHADER_UNUSED_KHR;
572 groups[2].closestHitShader = 2;
573 groups[2].anyHitShader = VK_SHADER_UNUSED_KHR;
574 groups[2].intersectionShader = VK_SHADER_UNUSED_KHR;
576 vk::RayTracingPipelineCreateInfoKHR pipeCi{};
577 pipeCi.stageCount = uint32_t(stages.size());
578 pipeCi.pStages = stages.data();
579 pipeCi.groupCount = uint32_t(
groups.size());
580 pipeCi.pGroups =
groups.data();
581 pipeCi.maxPipelineRayRecursionDepth =
583 pipeCi.layout = pipelineLayout_;
585 auto created = (*device_)->createRayTracingPipelinesKHR(vk::DeferredOperationKHR{}, vk::PipelineCache{}, pipeCi,
586 device_->allocation_callbacks);
587 if (
created.result != vk::Result::eSuccess ||
created.value.empty())
590 pipeline_ =
created.value.front();
592 return createShaderBindingTable();
595Result<void> VulkanRayTracing::createShaderBindingTable() {
599 const uint32_t handleSizeAligned = alignedSize(handleSize, handleAlignment);
600 const uint32_t groupCount = 3;
601 const uint32_t sbtSize = groupCount * handleSizeAligned;
603 std::vector<uint8_t> handles(sbtSize);
604 auto result = (*device_)->getRayTracingShaderGroupHandlesKHR(pipeline_, 0, groupCount, sbtSize, handles.data());
605 if (result != vk::Result::eSuccess)
610 const uint32_t raygenSize = alignedSize(handleSizeAligned, baseAlignment);
611 const uint32_t missSize = alignedSize(handleSizeAligned, baseAlignment);
612 const uint32_t hitSize = alignedSize(handleSizeAligned, baseAlignment);
613 const uint32_t total = raygenSize + missSize + hitSize;
615 std::vector<uint8_t> sbt(total, 0);
616 std::memcpy(sbt.data(), handles.data(), handleSize);
617 std::memcpy(sbt.data() + raygenSize, handles.data() + handleSizeAligned, handleSize);
618 std::memcpy(sbt.data() + raygenSize + missSize, handles.data() + 2 * handleSizeAligned, handleSize);
620 if (
auto r = sbtBuffer_.
allocate(*device_, total,
621 vk::BufferUsageFlagBits::eShaderBindingTableKHR |
622 vk::BufferUsageFlagBits::eShaderDeviceAddress |
623 vk::BufferUsageFlagBits::eTransferDst);
626 if (
auto r = sbtBuffer_.
upload(uploadPool_, graphicsQueue_, sbt.data(), total); !
r.
ok())
return r;
628 raygenRegion_.deviceAddress = sbtBuffer_.
address;
629 raygenRegion_.stride = raygenSize;
630 raygenRegion_.size = raygenSize;
632 missRegion_.deviceAddress = sbtBuffer_.
address + raygenSize;
633 missRegion_.stride = handleSizeAligned;
634 missRegion_.size = missSize;
636 hitRegion_.deviceAddress = sbtBuffer_.
address + raygenSize + missSize;
637 hitRegion_.stride = handleSizeAligned;
638 hitRegion_.size = hitSize;
640 callableRegion_ = vk::StridedDeviceAddressRegionKHR{};
644Result<void> VulkanRayTracing::ensureDescriptorSets(vk::ImageView outputView, vk::ImageView sceneView,
645 vk::ImageView depthView, vk::ImageView normalView) {
646 if (!descriptorPool_) {
647 std::array<vk::DescriptorPoolSize, 3> sizes{};
648 sizes[0] = {vk::DescriptorType::eAccelerationStructureKHR, 4};
649 sizes[1] = {vk::DescriptorType::eStorageImage, 4};
650 sizes[2] = {vk::DescriptorType::eCombinedImageSampler, 12};
651 vk::DescriptorPoolCreateInfo poolCi{};
653 poolCi.poolSizeCount = uint32_t(sizes.size());
654 poolCi.pPoolSizes = sizes.data();
655 descriptorPool_ = (*device_)->createDescriptorPool(poolCi, device_->allocation_callbacks);
657 if (!descriptorSet_) {
658 vk::DescriptorSetAllocateInfo alloc{};
659 alloc.descriptorPool = descriptorPool_;
660 alloc.descriptorSetCount = 1;
661 alloc.pSetLayouts = &setLayout_;
662 descriptorSet_ = (*device_)->allocateDescriptorSets(alloc).front();
665 vk::WriteDescriptorSetAccelerationStructureKHR asWrite{};
666 asWrite.accelerationStructureCount = 1;
667 asWrite.pAccelerationStructures = &tlas_.
handle;
669 vk::WriteDescriptorSet writes[5]{};
670 writes[0].pNext = &asWrite;
671 writes[0].dstSet = descriptorSet_;
672 writes[0].dstBinding = 0;
673 writes[0].descriptorCount = 1;
674 writes[0].descriptorType = vk::DescriptorType::eAccelerationStructureKHR;
677 vk::DescriptorImageInfo outInfo{};
678 outInfo.imageView = outputView;
679 outInfo.imageLayout = vk::ImageLayout::eGeneral;
680 writes[1].dstSet = descriptorSet_;
681 writes[1].dstBinding = 1;
682 writes[1].descriptorCount = 1;
683 writes[1].descriptorType = vk::DescriptorType::eStorageImage;
684 writes[1].pImageInfo = &outInfo;
686 if (
auto r = ensureSampler(); !
r.
ok())
return r;
688 vk::DescriptorImageInfo sceneInfo{};
689 sceneInfo.sampler = sampler_;
690 sceneInfo.imageView = sceneView;
691 sceneInfo.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
692 writes[2].dstSet = descriptorSet_;
693 writes[2].dstBinding = 2;
694 writes[2].descriptorCount = 1;
695 writes[2].descriptorType = vk::DescriptorType::eCombinedImageSampler;
696 writes[2].pImageInfo = &sceneInfo;
698 vk::DescriptorImageInfo depthInfo{};
699 depthInfo.sampler = sampler_;
700 depthInfo.imageView = depthView;
701 depthInfo.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
702 writes[3].dstSet = descriptorSet_;
703 writes[3].dstBinding = 3;
704 writes[3].descriptorCount = 1;
705 writes[3].descriptorType = vk::DescriptorType::eCombinedImageSampler;
706 writes[3].pImageInfo = &depthInfo;
708 vk::DescriptorImageInfo normalInfo{};
709 normalInfo.sampler = sampler_;
710 normalInfo.imageView = normalView;
711 normalInfo.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
712 writes[4].dstSet = descriptorSet_;
713 writes[4].dstBinding = 4;
714 writes[4].descriptorCount = 1;
715 writes[4].descriptorType = vk::DescriptorType::eCombinedImageSampler;
716 writes[4].pImageInfo = &normalInfo;
718 (*device_)->updateDescriptorSets(writes, {});
722Result<void> VulkanRayTracing::ensureSampler() {
726 vk::SamplerCreateInfo sci{};
727 sci.magFilter = vk::Filter::eNearest;
728 sci.minFilter = vk::Filter::eNearest;
729 sci.mipmapMode = vk::SamplerMipmapMode::eNearest;
730 sci.addressModeU = vk::SamplerAddressMode::eClampToEdge;
731 sci.addressModeV = vk::SamplerAddressMode::eClampToEdge;
732 sci.addressModeW = vk::SamplerAddressMode::eClampToEdge;
733 sampler_ = (*device_)->createSampler(sci, device_->allocation_callbacks);
737Result<void> VulkanRayTracing::ensureOutputImage(uint32_t
width, uint32_t
height) {
743 outputImage_ = StorageColorImage{};
744 outputWidth_ =
width;
752 const glm::mat4&
viewProj,
const glm::vec3& eyeWorld) {
754 if (!gfx || !sceneColor || !hwDepth || !worldNormal || !dest)
757 if (meshes_.empty() || !tlas_.
handle) {
766 if (
auto r = ensurePipeline(); !
r.
ok())
return r;
768 auto textureView = [](
Texture* tex) -> vk::ImageView {
769 if (!tex || !tex->gpuHandle)
return {};
785 const vk::ImageView outView = outputImage_.imageView();
786 const vk::ImageView sceneView = textureView(sceneColor);
787 const vk::ImageView depthView = textureView(hwDepth);
788 const vk::ImageView normalView = textureView(worldNormal);
789 if (!outView || !sceneView || !depthView || !normalView)
793 if (
auto r = ensureDescriptorSets(outView, sceneView, depthView, normalView); !
r.
ok())
return r;
795 PushConstants push{};
798 push.eye = glm::vec4(eyeWorld, 0.f);
800 auto record = [&](vk::CommandBuffer cmd) {
801 outputImage_.setLayout(cmd, vk::ImageLayout::eGeneral);
803 vk::MemoryBarrier memBarrier{};
804 memBarrier.srcAccessMask =
805 vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
806 memBarrier.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
808 vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eLateFragmentTests,
809 vk::PipelineStageFlagBits::eRayTracingShaderKHR, {}, memBarrier, {}, {});
811 cmd.bindPipeline(vk::PipelineBindPoint::eRayTracingKHR, pipeline_);
812 cmd.bindDescriptorSets(vk::PipelineBindPoint::eRayTracingKHR, pipelineLayout_, 0, descriptorSet_, {});
813 cmd.pushConstants(pipelineLayout_, vk::ShaderStageFlagBits::eRaygenKHR, 0,
sizeof(PushConstants), &push);
814 cmd.traceRaysKHR(raygenRegion_, missRegion_, hitRegion_, callableRegion_,
width,
height, 1);
817 outputImage_.setLayout(cmd, vk::ImageLayout::eTransferSrcOptimal);
818 destCanvas->colorImage().setLayout(cmd, vk::ImageLayout::eTransferDstOptimal);
819 vk::ImageCopy region{};
820 region.srcSubresource = {vk::ImageAspectFlagBits::eColor, 0, 0, 1};
821 region.dstSubresource = {vk::ImageAspectFlagBits::eColor, 0, 0, 1};
823 cmd.copyImage(outputImage_.image(), vk::ImageLayout::eTransferSrcOptimal, destCanvas->colorImage().image(),
824 vk::ImageLayout::eTransferDstOptimal, region);
825 destCanvas->colorImage().setLayout(cmd, vk::ImageLayout::eShaderReadOnlyOptimal);
828 if (vkGfx->canRecordPostSceneGpuWork()) {
829 record(vkGfx->postSceneCommandBuffer());
831 vkGfx->waitForSharedGpuResources();
832 vkb::executeImmediately(device_->instance, uploadPool_, graphicsQueue_, record);
839struct RegisterCapability {
840 RegisterCapability() { eve::cap::provide<IRayTracing>(&
vulkanRayTracing()); }
std::vector< std::uint32_t > verts
std::vector< BuildingInstanceSnapshot > instances
Stable, structured diagnostics shared by engine modules.
std::uint32_t vertexCount
vk::UniqueDeviceMemory memory
std::vector< std::uint32_t > indices
std::vector< float > positions
std::vector< std::shared_ptr< DeviceBytes > > bindings
std::unique_ptr< gpgpu::GpuBuffer > buffer
Structured operation status used by the common Result foundation.
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
bool ok() const noexcept
Whether this result represents a non-failure outcome.
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
static Result failure(Status status)
Construct a failed result from a structured status.
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
virtual int getWidth() const =0
Returns the width.
virtual Texture * getTexture()=0
Sampleable color buffer; screen Canvas returns nullptr.
virtual int getHeight() const =0
Returns the height.
GPU mesh handle (+ optional CPU morph targets).
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
Vulkan implementation of IRayTracing (KHR ray-tracing pipeline).
Result< void > rebuildScene() override
Rebuild scene.
void clearScene() override
Clears scene.
~VulkanRayTracing() override
Releases VulkanRayTracing resources.
Result< uint32_t > addMesh(Mesh *mesh, const glm::mat4 &transform) override
Adds mesh.
RayTracingCaps caps() const override
Caps.
void attachDevice(vkb::Device *device, const RayTracingCaps &caps, vk::CommandPool uploadPool, vk::Queue graphicsQueue)
Bind to the live Vulkan Graphics device (call after init). @ownership device is borrowed; Graphics re...
Result< void > ensureAttached()
Lazily bind to the process Graphics singleton when RT is available.
bool isAvailable() const override
True when available.
void detachDevice()
Drop GPU resources when Graphics tears down.
Result< uint32_t > addTriangleMesh(const float *positionsXYZ, int vertexCount, const uint32_t *indices, int indexCount, const glm::mat4 &transform) override
Adds triangle mesh.
Result< void > applyReflections(Graphics *gfx, Texture *sceneColor, Texture *hwDepth, Texture *worldNormal, Canvas *dest, const glm::mat4 &invViewProj, const glm::mat4 &viewProj, const glm::vec3 &eyeWorld) override
Applies reflections.
EVENGINE_API_BACKENDS public API.
bool supportsRayTracing() const override
Supports ray tracing.
RayTracingCaps rayTracingCaps() const override
Ray tracing caps.
OffscreenCanvas public API.
VulkanRayTracing & vulkanRayTracing()
Process-wide Vulkan RT backend; also registered as IRayTracing.
std::string buildInfo()
One-line build metadata for diagnostics (printed by eve --version).
Hardware ray-tracing capabilities probed once at Vulkan device creation.
uint32_t shaderGroupHandleAlignment
bool rayTracingAvailable() const
True when BLAS/TLAS + ray-tracing pipelines can be created.
uint32_t maxRecursionDepth
uint32_t shaderGroupHandleSize
uint32_t shaderGroupBaseAlignment
Bottom- or top-level acceleration structure handle.
vk::DeviceAddress deviceAddress
void steal(AccelerationStructure &o) noexcept
Steal.
DeviceAddressBuffer storage
vk::AccelerationStructureKHR handle
Device-local buffer with shader device address (for AS builds / SBT). @ownership Owns the Vulkan buff...
vk::DeviceAddress address
Result< void > allocate(vkb::Device &dev, vk::DeviceSize bytes, vk::BufferUsageFlags usage)
Allocate a buffer usage that includes SHADER_DEVICE_ADDRESS.
Result< void > upload(vk::CommandPool pool, vk::Queue queue, const void *data, vk::DeviceSize bytes)
Upload host bytes via a staging buffer (stalls the queue).
void steal(DeviceAddressBuffer &o) noexcept
Steal.
TriangleMeshRecord public API.
vk::ImageView imageView() const
Image view.