From 9e58ad537795588f9d2f07a450a74eee7664e60f Mon Sep 17 00:00:00 2001 From: n8vm Date: Thu, 8 Sep 2022 12:57:00 -0600 Subject: [PATCH] Adding a simple example demonstrating how to use SBT record data --- .../glsl/raytracingsbtdata/closesthit.rchit | 19 + .../raytracingsbtdata/closesthit.rchit.spv | Bin 0 -> 1184 bytes .../shaders/glsl/raytracingsbtdata/miss.rmiss | 9 + .../glsl/raytracingsbtdata/miss.rmiss.spv | Bin 0 -> 300 bytes .../glsl/raytracingsbtdata/raygen.rgen | 39 + .../glsl/raytracingsbtdata/raygen.rgen.spv | Bin 0 -> 3168 bytes .../hlsl/raytracingsbtdata/closesthit.rchit | 27 + .../raytracingsbtdata/closesthit.rchit.spv | Bin 0 -> 728 bytes .../shaders/hlsl/raytracingsbtdata/miss.rmiss | 12 + .../hlsl/raytracingsbtdata/miss.rmiss.spv | Bin 0 -> 324 bytes .../hlsl/raytracingsbtdata/raygen.rgen | 49 + .../hlsl/raytracingsbtdata/raygen.rgen.spv | Bin 0 -> 2768 bytes examples/CMakeLists.txt | 1 + .../raytracingsbtdata/raytracingsbtdata.cpp | 922 ++++++++++++++++++ 14 files changed, 1078 insertions(+) create mode 100644 data/shaders/glsl/raytracingsbtdata/closesthit.rchit create mode 100644 data/shaders/glsl/raytracingsbtdata/closesthit.rchit.spv create mode 100644 data/shaders/glsl/raytracingsbtdata/miss.rmiss create mode 100644 data/shaders/glsl/raytracingsbtdata/miss.rmiss.spv create mode 100644 data/shaders/glsl/raytracingsbtdata/raygen.rgen create mode 100644 data/shaders/glsl/raytracingsbtdata/raygen.rgen.spv create mode 100644 data/shaders/hlsl/raytracingsbtdata/closesthit.rchit create mode 100644 data/shaders/hlsl/raytracingsbtdata/closesthit.rchit.spv create mode 100644 data/shaders/hlsl/raytracingsbtdata/miss.rmiss create mode 100644 data/shaders/hlsl/raytracingsbtdata/miss.rmiss.spv create mode 100644 data/shaders/hlsl/raytracingsbtdata/raygen.rgen create mode 100644 data/shaders/hlsl/raytracingsbtdata/raygen.rgen.spv create mode 100644 examples/raytracingsbtdata/raytracingsbtdata.cpp diff --git a/data/shaders/glsl/raytracingsbtdata/closesthit.rchit b/data/shaders/glsl/raytracingsbtdata/closesthit.rchit new file mode 100644 index 00000000..17e642a1 --- /dev/null +++ b/data/shaders/glsl/raytracingsbtdata/closesthit.rchit @@ -0,0 +1,19 @@ +#version 460 +#extension GL_EXT_ray_tracing : enable +#extension GL_EXT_nonuniform_qualifier : enable + +layout(location = 0) rayPayloadInEXT vec3 hitValue; +hitAttributeEXT vec2 attribs; + +layout(shaderRecordEXT, std430) buffer SBT { + float r; + float g; + float b; +}; + +void main() +{ + // Update the hit value to the hit record SBT data associated with this + // geometry ID and ray ID + hitValue = vec3(r, g, b); +} diff --git a/data/shaders/glsl/raytracingsbtdata/closesthit.rchit.spv b/data/shaders/glsl/raytracingsbtdata/closesthit.rchit.spv new file mode 100644 index 0000000000000000000000000000000000000000..9a3bcd74c613ad8fb455a67362e0a6578ae67604 GIT binary patch literal 1184 zcmYk5T~8BH5Qe88+af3k2>6LDD1IP~7ivt58a1XQ_JUYKxZ7+?!A)Qrx7!P@G~us# z=|3RBzu}FE@3TFJ>>(~HRmCS!oRGLCTa`6s75$av ze@k4Iw%rLo@9&1mFc~ND;V?Z3zmKEi_%I%%%GZ3pIEDqv6#EBOw=3$|i_+8nAj#6W z|7kc(4@Tn9^86?h-5Uu9`7Gb!Y%e+<50ny9vHSAfkGpzeHILy_naAAisDQacPev{G z;%p1ihqFF$mmKwQ%(-8inJUgTY+;ki>tto~p$#^E1{c)&&AWyxYiTop3QU6q!9lX*E zc-{wxJ^mFF$n$LW$v4IPAH;HB{WS7#i9NiPhUY9e{!AM1qKv)6`?@8?>{g>))-5ZB z_jN0ZsfG7-Hx*M?eBLc#c(1wbG{xR`#KG@qBIds_5D&R9FB2 literal 0 HcmV?d00001 diff --git a/data/shaders/glsl/raytracingsbtdata/miss.rmiss b/data/shaders/glsl/raytracingsbtdata/miss.rmiss new file mode 100644 index 00000000..569a38b9 --- /dev/null +++ b/data/shaders/glsl/raytracingsbtdata/miss.rmiss @@ -0,0 +1,9 @@ +#version 460 +#extension GL_EXT_ray_tracing : enable + +layout(location = 0) rayPayloadInEXT vec3 hitValue; + +void main() +{ + // for now, we do nothing in the miss program +} \ No newline at end of file diff --git a/data/shaders/glsl/raytracingsbtdata/miss.rmiss.spv b/data/shaders/glsl/raytracingsbtdata/miss.rmiss.spv new file mode 100644 index 0000000000000000000000000000000000000000..c67830c845fab718d13beec3b8dfe48fd729e1b8 GIT binary patch literal 300 zcmZQ(Qf6mhWn|!D-~eI>5M*Fts25}a^4S=I1H$6HJ%Zwk5-a0NiV~AE^U@i(f#Qrn zrS3k#K6=F^DS9TR1|Xe$49s8yKyv&*HAh7l7+8Sp+{Da0kOU_XGXo(LkUj%c!~#^~ z?i24C5rSeo11nGlSsyD<1f(}3vm`7rr!*DFWDo|L0y0MtY$5}gEe2$>0mT%6SR7~q yJCFwPL9S8&@;QL?J)oOF;?__;$Xt+^8PE=pc_4d10Ho&!P=pmo{{{Ni0*C=34cAR%=TO4{CAnO)ZIodem;XVex%3EtRX~sjXyv>dMtO@jU4AX#my^xe@p4QRn(4 zw}Pn6!U5rga6zaDRiP$q3VHo~T-79fFs)_Jt{QzCrk1Sb-D%mZr*{2rLUSjM z8tekU^U~*oRu~7BxYG^eBnr1gDf2+OhP)GnUoWjKNcG^UrqqYNZbZpy(A*ApxxBkCp*pvs zI{H%?J{C-;u0}@Z9G(c&#~CrGhqFpL-Ez1SHfOa!e$Fmx1r3swq;tl@!f!fzbj}<7 zcj*h#=^djWEa>0#vnl&LHz@9f)Na?0IJQW8pm)Tjr_}VSX5zx1lzz_3tdB`QuNe+t|C(m%gC{>a z=L<&0t5d^&Davp>boSFF{w(ZgGUX{P?U@=m7R4mak%TjCk;c_967 zt@B;tgU(%sV|~TTRwH|QEYOp`j^^B&X6!!G%s?j&I^Q$H_7+I~PdKI5ctbNW*=u$X z*Umcf3)m79ebaOFhqsP;@VTb_nJ*^v>>n|_1NaVU-Y3v+(|PB!I&Zn@d}k9q`sE&d zI-_%zOFcSoF7a+DKX>Sm!2MYk%iZD4f&&bC(UmPeqXO$c`g^3~NICA)ZS9V|z!4k! z;{u%JIw_fZX1PvDh69Xjt{0`V{*%x3l4Rn+lZ!sVS+18Q!~N{-Uy%&QJ+63eTyoLl zndi<($CfqX(r>s~t#i-b5H1MZKXRSZJSD*YA{*}7Tb|?25rZ>=vlv$-6N7taF|JC6 z$A)vdE|8nMhz;N2y8^t~+?0$B_t9+LlMG)`$EoF(fFFF(!|J>(oxIpkC*Ka7)j1=% zcdxVJ;K@h)c>%v=eZ6y{-7g*dE`POwHJ^NA3e0GIxQu3YmMrJH_5tG(Qx0W9*!%an*7v&S;2G9;%egL& z{o1*OlIex{e<2wTFnawP;^@)Z=tx=%-&`Q@eHy5NJjBADJ&=#L@k@bs5q(&5Q#c^l z-EVmgo65m?8UZ>TE}+rEjfJf~@oi#gJp sbt; + +[shader("closesthit")] +void main(inout Payload p, in float2 attribs) +{ + // Update the hit value to the hit record SBT data associated with this + // geometry ID and ray ID + p.hitValue = float3(sbt.r, sbt.g, sbt.g); +} diff --git a/data/shaders/hlsl/raytracingsbtdata/closesthit.rchit.spv b/data/shaders/hlsl/raytracingsbtdata/closesthit.rchit.spv new file mode 100644 index 0000000000000000000000000000000000000000..240ae6c8bc00f14a72490b0487737f50178b50eb GIT binary patch literal 728 zcmY+CPfG#;5XC3m)w0se)J|e}3_L~<1qqSRMO37lth)XIQ`t1|)R*gX)Ys?|^nTke z)ynM5ym|9xW-*zlrOn+jv#g!!&6Br;z2?m_maLZuLEy5TTH}EhCI4* zmbSMOGvX(2wxE!Z!AR=o%D)J3rRFGJZ&!Z3GxbJ((DsMRVAR-*$9{14*sXUOUDHG- z image : register(u1); + +struct CameraProperties +{ + float4x4 viewInverse; + float4x4 projInverse; +}; +cbuffer cam : register(b2) { CameraProperties cam; }; + +struct Payload +{ +[[vk::location(0)]] float3 hitValue; +}; + +struct SBT { + float r; + float g; + float b; +}; +[[vk::shader_record_ext]] +ConstantBuffer sbt; + +[shader("raygeneration")] +void main() +{ + uint3 LaunchID = DispatchRaysIndex(); + uint3 LaunchSize = DispatchRaysDimensions(); + + const float2 pixelCenter = float2(LaunchID.xy) + float2(0.5, 0.5); + const float2 inUV = pixelCenter/float2(LaunchSize.xy); + float2 d = inUV * 2.0 - 1.0; + float4 target = mul(cam.projInverse, float4(d.x, d.y, 1, 1)); + + RayDesc rayDesc; + rayDesc.Origin = mul(cam.viewInverse, float4(0,0,0,1)).xyz; + rayDesc.Direction = mul(cam.viewInverse, float4(normalize(target.xyz), 0)).xyz; + rayDesc.TMin = 0.001; + rayDesc.TMax = 10000.0; + + // Initialize the hit value to the raygen SBT data + Payload payload; + payload.hitValue = float3(sbt.r, sbt.g, sbt.b); + TraceRay(rs, RAY_FLAG_FORCE_OPAQUE, 0xff, 0, 0, 0, rayDesc, payload); + + image[int2(LaunchID.xy)] = float4(payload.hitValue, 0.0); +} diff --git a/data/shaders/hlsl/raytracingsbtdata/raygen.rgen.spv b/data/shaders/hlsl/raytracingsbtdata/raygen.rgen.spv new file mode 100644 index 0000000000000000000000000000000000000000..c9039b7bbc933ae40dc1b1f590bb3f6dc9ee08b0 GIT binary patch literal 2768 zcmZXUYf}_Q6ownvT_X||?YBw zCo6x+Pw_XT%JOgc#nShg>4PKo*1LVqdoF#tPxo|n4z6?g4(HrP_gMdoUf1dV>`mw2 zHFvv$t5#-auZ>U5M#0K#90j$;;@oHavvwG*D1L+d$v8GqohU8E_0sVZhsnFqbulHj zO|Hki+~%~Ba|=OZ5guz!$O{Ewo$!UQUg**fepvx|vE7n`S}mLpqabcH7prm9uEp&r zygnm)>KiTLTTngD#Vf6_bhKV-ECh3*^t@JLjgNX>6?wl}upm+PT*4tkc$9cEBALo?q(U^4l#F_r5{-M(|^jBUdzE5Car*8v^_EGQ8==33LN5{^- zi07QLc6992HLmRRgfW;-pK$nY7RHsw{HWLBScACo8g6YIeip|)Vc2?OIuSUl$9iOT z*0MgqSsplI;OG;1jN2iZx{JaAMIh@bGKP0b-y#8y_#x#--zwk(EJiJ1luh5BsDBMx z%ns>rDROS7fGR`het)jLzA<7hZ4cOuiXK#qE{O;2W}AUl;B! zH4{fIHjv?n`OcI0?vnn!X7*AEdugv*CuDp0_VmX-9OgISIo7oO|00=q z{IT;5vOQdvj6J)DN$K!_VKt8E%y<(OfqQIcLoaT4-h8IKkM$3qssL|3)82>gA7_lu zO#!~*&tpb1xrxPAksojRmVoWNZai-UogCeoZwqRQ_p{Cl99|`1Ryi=!63m*W)U$`#- literal 0 HcmV?d00001 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index ea95fdeb..c90b6b68 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -131,6 +131,7 @@ set(EXAMPLES raytracingbasic raytracingcallable raytracingreflections + raytracingsbtdata raytracingshadows renderheadless screenshot diff --git a/examples/raytracingsbtdata/raytracingsbtdata.cpp b/examples/raytracingsbtdata/raytracingsbtdata.cpp new file mode 100644 index 00000000..b78c7f45 --- /dev/null +++ b/examples/raytracingsbtdata/raytracingsbtdata.cpp @@ -0,0 +1,922 @@ +/* +* Vulkan Example - Hardware accelerated ray tracing example using SBT data +* +* Uses the data section of each shader binding table record to color the background and geometry +* +* Copyright (C) 2019-2020 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#include "vulkanexamplebase.h" + +// Holds data for a ray tracing scratch buffer that is used as a temporary storage +struct RayTracingScratchBuffer +{ + uint64_t deviceAddress = 0; + VkBuffer handle = VK_NULL_HANDLE; + VkDeviceMemory memory = VK_NULL_HANDLE; +}; + +// Ray tracing acceleration structure +struct AccelerationStructure { + VkAccelerationStructureKHR handle; + uint64_t deviceAddress = 0; + VkDeviceMemory memory; + VkBuffer buffer; +}; + +class VulkanExample : public VulkanExampleBase +{ +public: + PFN_vkGetBufferDeviceAddressKHR vkGetBufferDeviceAddressKHR; + PFN_vkCreateAccelerationStructureKHR vkCreateAccelerationStructureKHR; + PFN_vkDestroyAccelerationStructureKHR vkDestroyAccelerationStructureKHR; + PFN_vkGetAccelerationStructureBuildSizesKHR vkGetAccelerationStructureBuildSizesKHR; + PFN_vkGetAccelerationStructureDeviceAddressKHR vkGetAccelerationStructureDeviceAddressKHR; + PFN_vkCmdBuildAccelerationStructuresKHR vkCmdBuildAccelerationStructuresKHR; + PFN_vkBuildAccelerationStructuresKHR vkBuildAccelerationStructuresKHR; + PFN_vkCmdTraceRaysKHR vkCmdTraceRaysKHR; + PFN_vkGetRayTracingShaderGroupHandlesKHR vkGetRayTracingShaderGroupHandlesKHR; + PFN_vkCreateRayTracingPipelinesKHR vkCreateRayTracingPipelinesKHR; + + VkPhysicalDeviceRayTracingPipelinePropertiesKHR rayTracingPipelineProperties{}; + VkPhysicalDeviceAccelerationStructureFeaturesKHR accelerationStructureFeatures{}; + + VkPhysicalDeviceBufferDeviceAddressFeatures enabledBufferDeviceAddresFeatures{}; + VkPhysicalDeviceRayTracingPipelineFeaturesKHR enabledRayTracingPipelineFeatures{}; + VkPhysicalDeviceAccelerationStructureFeaturesKHR enabledAccelerationStructureFeatures{}; + + AccelerationStructure bottomLevelAS{}; + AccelerationStructure topLevelAS{}; + + vks::Buffer vertexBuffer; + vks::Buffer indexBuffer; + uint32_t indexCount; + vks::Buffer transformBuffer; + std::vector shaderGroups{}; + vks::Buffer raygenShaderBindingTable; + vks::Buffer missShaderBindingTable; + vks::Buffer hitShaderBindingTable; + + struct StorageImage { + VkDeviceMemory memory; + VkImage image; + VkImageView view; + VkFormat format; + } storageImage; + + struct UniformData { + glm::mat4 viewInverse; + glm::mat4 projInverse; + } uniformData; + vks::Buffer ubo; + + VkPipeline pipeline; + VkPipelineLayout pipelineLayout; + VkDescriptorSet descriptorSet; + VkDescriptorSetLayout descriptorSetLayout; + + VulkanExample() : VulkanExampleBase() + { + title = "Ray tracing SBT data"; + settings.overlay = false; + camera.type = Camera::CameraType::lookat; + camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 512.0f); + camera.setRotation(glm::vec3(0.0f, 0.0f, 0.0f)); + camera.setTranslation(glm::vec3(0.0f, 0.0f, -2.5f)); + + // Require Vulkan 1.1 + apiVersion = VK_API_VERSION_1_1; + + // Ray tracing related extensions required by this sample + enabledDeviceExtensions.push_back(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME); + + // Required by VK_KHR_acceleration_structure + enabledDeviceExtensions.push_back(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME); + + // Required for VK_KHR_ray_tracing_pipeline + enabledDeviceExtensions.push_back(VK_KHR_SPIRV_1_4_EXTENSION_NAME); + + // Required by VK_KHR_spirv_1_4 + enabledDeviceExtensions.push_back(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME); + } + + ~VulkanExample() + { + vkDestroyPipeline(device, pipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + vkDestroyImageView(device, storageImage.view, nullptr); + vkDestroyImage(device, storageImage.image, nullptr); + vkFreeMemory(device, storageImage.memory, nullptr); + vkFreeMemory(device, bottomLevelAS.memory, nullptr); + vkDestroyBuffer(device, bottomLevelAS.buffer, nullptr); + vkDestroyAccelerationStructureKHR(device, bottomLevelAS.handle, nullptr); + vkFreeMemory(device, topLevelAS.memory, nullptr); + vkDestroyBuffer(device, topLevelAS.buffer, nullptr); + vkDestroyAccelerationStructureKHR(device, topLevelAS.handle, nullptr); + vertexBuffer.destroy(); + indexBuffer.destroy(); + transformBuffer.destroy(); + raygenShaderBindingTable.destroy(); + missShaderBindingTable.destroy(); + hitShaderBindingTable.destroy(); + ubo.destroy(); + } + + /* + Create a scratch buffer to hold temporary data for a ray tracing acceleration structure + */ + RayTracingScratchBuffer createScratchBuffer(VkDeviceSize size) + { + RayTracingScratchBuffer scratchBuffer{}; + + VkBufferCreateInfo bufferCreateInfo{}; + bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferCreateInfo.size = size; + bufferCreateInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + VK_CHECK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &scratchBuffer.handle)); + + VkMemoryRequirements memoryRequirements{}; + vkGetBufferMemoryRequirements(device, scratchBuffer.handle, &memoryRequirements); + + VkMemoryAllocateFlagsInfo memoryAllocateFlagsInfo{}; + memoryAllocateFlagsInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO; + memoryAllocateFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + + VkMemoryAllocateInfo memoryAllocateInfo = {}; + memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memoryAllocateInfo.pNext = &memoryAllocateFlagsInfo; + memoryAllocateInfo.allocationSize = memoryRequirements.size; + memoryAllocateInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device, &memoryAllocateInfo, nullptr, &scratchBuffer.memory)); + VK_CHECK_RESULT(vkBindBufferMemory(device, scratchBuffer.handle, scratchBuffer.memory, 0)); + + VkBufferDeviceAddressInfoKHR bufferDeviceAddressInfo{}; + bufferDeviceAddressInfo.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; + bufferDeviceAddressInfo.buffer = scratchBuffer.handle; + scratchBuffer.deviceAddress = vkGetBufferDeviceAddressKHR(device, &bufferDeviceAddressInfo); + + return scratchBuffer; + } + + void deleteScratchBuffer(RayTracingScratchBuffer& scratchBuffer) + { + if (scratchBuffer.memory != VK_NULL_HANDLE) { + vkFreeMemory(device, scratchBuffer.memory, nullptr); + } + if (scratchBuffer.handle != VK_NULL_HANDLE) { + vkDestroyBuffer(device, scratchBuffer.handle, nullptr); + } + } + + void createAccelerationStructureBuffer(AccelerationStructure &accelerationStructure, VkAccelerationStructureBuildSizesInfoKHR buildSizeInfo) + { + VkBufferCreateInfo bufferCreateInfo{}; + bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferCreateInfo.size = buildSizeInfo.accelerationStructureSize; + bufferCreateInfo.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + VK_CHECK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &accelerationStructure.buffer)); + VkMemoryRequirements memoryRequirements{}; + vkGetBufferMemoryRequirements(device, accelerationStructure.buffer, &memoryRequirements); + VkMemoryAllocateFlagsInfo memoryAllocateFlagsInfo{}; + memoryAllocateFlagsInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO; + memoryAllocateFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + VkMemoryAllocateInfo memoryAllocateInfo{}; + memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memoryAllocateInfo.pNext = &memoryAllocateFlagsInfo; + memoryAllocateInfo.allocationSize = memoryRequirements.size; + memoryAllocateInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device, &memoryAllocateInfo, nullptr, &accelerationStructure.memory)); + VK_CHECK_RESULT(vkBindBufferMemory(device, accelerationStructure.buffer, accelerationStructure.memory, 0)); + } + + + /* + Gets the device address from a buffer that's required for some of the buffers used for ray tracing + */ + uint64_t getBufferDeviceAddress(VkBuffer buffer) + { + VkBufferDeviceAddressInfoKHR bufferDeviceAI{}; + bufferDeviceAI.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; + bufferDeviceAI.buffer = buffer; + return vkGetBufferDeviceAddressKHR(device, &bufferDeviceAI); + } + + /* + Set up a storage image that the ray generation shader will be writing to + */ + void createStorageImage() + { + VkImageCreateInfo image = vks::initializers::imageCreateInfo(); + image.imageType = VK_IMAGE_TYPE_2D; + image.format = swapChain.colorFormat; + image.extent.width = width; + image.extent.height = height; + image.extent.depth = 1; + image.mipLevels = 1; + image.arrayLayers = 1; + image.samples = VK_SAMPLE_COUNT_1_BIT; + image.tiling = VK_IMAGE_TILING_OPTIMAL; + image.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT; + image.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &storageImage.image)); + + VkMemoryRequirements memReqs; + vkGetImageMemoryRequirements(device, storageImage.image, &memReqs); + VkMemoryAllocateInfo memoryAllocateInfo = vks::initializers::memoryAllocateInfo(); + memoryAllocateInfo.allocationSize = memReqs.size; + memoryAllocateInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device, &memoryAllocateInfo, nullptr, &storageImage.memory)); + VK_CHECK_RESULT(vkBindImageMemory(device, storageImage.image, storageImage.memory, 0)); + + VkImageViewCreateInfo colorImageView = vks::initializers::imageViewCreateInfo(); + colorImageView.viewType = VK_IMAGE_VIEW_TYPE_2D; + colorImageView.format = swapChain.colorFormat; + colorImageView.subresourceRange = {}; + colorImageView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + colorImageView.subresourceRange.baseMipLevel = 0; + colorImageView.subresourceRange.levelCount = 1; + colorImageView.subresourceRange.baseArrayLayer = 0; + colorImageView.subresourceRange.layerCount = 1; + colorImageView.image = storageImage.image; + VK_CHECK_RESULT(vkCreateImageView(device, &colorImageView, nullptr, &storageImage.view)); + + VkCommandBuffer cmdBuffer = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + vks::tools::setImageLayout(cmdBuffer, storageImage.image, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_GENERAL, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); + vulkanDevice->flushCommandBuffer(cmdBuffer, queue); + } + + /* + Create the bottom level acceleration structure contains the scene's actual geometry (vertices, triangles) + */ + void createBottomLevelAccelerationStructure() + { + // Setup vertices for a single triangle + struct Vertex { + float pos[3]; + }; + std::vector vertices = { + { { 1.0f, 1.0f, 0.0f } }, + { { -1.0f, 1.0f, 0.0f } }, + { { 0.0f, -1.0f, 0.0f } } + }; + + // Setup indices + std::vector indices = { 0, 1, 2 }; + indexCount = static_cast(indices.size()); + + // Setup identity transform matrix + VkTransformMatrixKHR transformMatrix = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f + }; + + // Create buffers + // For the sake of simplicity we won't stage the vertex data to the GPU memory + // Vertex buffer + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &vertexBuffer, + vertices.size() * sizeof(Vertex), + vertices.data())); + // Index buffer + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &indexBuffer, + indices.size() * sizeof(uint32_t), + indices.data())); + // Transform buffer + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &transformBuffer, + sizeof(VkTransformMatrixKHR), + &transformMatrix)); + + VkDeviceOrHostAddressConstKHR vertexBufferDeviceAddress{}; + VkDeviceOrHostAddressConstKHR indexBufferDeviceAddress{}; + VkDeviceOrHostAddressConstKHR transformBufferDeviceAddress{}; + + vertexBufferDeviceAddress.deviceAddress = getBufferDeviceAddress(vertexBuffer.buffer); + indexBufferDeviceAddress.deviceAddress = getBufferDeviceAddress(indexBuffer.buffer); + transformBufferDeviceAddress.deviceAddress = getBufferDeviceAddress(transformBuffer.buffer); + + // Build + VkAccelerationStructureGeometryKHR accelerationStructureGeometry{}; + accelerationStructureGeometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR; + accelerationStructureGeometry.flags = VK_GEOMETRY_OPAQUE_BIT_KHR; + accelerationStructureGeometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR; + accelerationStructureGeometry.geometry.triangles.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR; + accelerationStructureGeometry.geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT; + accelerationStructureGeometry.geometry.triangles.vertexData = vertexBufferDeviceAddress; + accelerationStructureGeometry.geometry.triangles.maxVertex = 3; + accelerationStructureGeometry.geometry.triangles.vertexStride = sizeof(Vertex); + accelerationStructureGeometry.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32; + accelerationStructureGeometry.geometry.triangles.indexData = indexBufferDeviceAddress; + accelerationStructureGeometry.geometry.triangles.transformData.deviceAddress = 0; + accelerationStructureGeometry.geometry.triangles.transformData.hostAddress = nullptr; + accelerationStructureGeometry.geometry.triangles.transformData = transformBufferDeviceAddress; + + // Get size info + VkAccelerationStructureBuildGeometryInfoKHR accelerationStructureBuildGeometryInfo{}; + accelerationStructureBuildGeometryInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; + accelerationStructureBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; + accelerationStructureBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; + accelerationStructureBuildGeometryInfo.geometryCount = 1; + accelerationStructureBuildGeometryInfo.pGeometries = &accelerationStructureGeometry; + + const uint32_t numTriangles = 1; + VkAccelerationStructureBuildSizesInfoKHR accelerationStructureBuildSizesInfo{}; + accelerationStructureBuildSizesInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR; + vkGetAccelerationStructureBuildSizesKHR( + device, + VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, + &accelerationStructureBuildGeometryInfo, + &numTriangles, + &accelerationStructureBuildSizesInfo); + + createAccelerationStructureBuffer(bottomLevelAS, accelerationStructureBuildSizesInfo); + + VkAccelerationStructureCreateInfoKHR accelerationStructureCreateInfo{}; + accelerationStructureCreateInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR; + accelerationStructureCreateInfo.buffer = bottomLevelAS.buffer; + accelerationStructureCreateInfo.size = accelerationStructureBuildSizesInfo.accelerationStructureSize; + accelerationStructureCreateInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; + vkCreateAccelerationStructureKHR(device, &accelerationStructureCreateInfo, nullptr, &bottomLevelAS.handle); + + // Create a small scratch buffer used during build of the bottom level acceleration structure + RayTracingScratchBuffer scratchBuffer = createScratchBuffer(accelerationStructureBuildSizesInfo.buildScratchSize); + + VkAccelerationStructureBuildGeometryInfoKHR accelerationBuildGeometryInfo{}; + accelerationBuildGeometryInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; + accelerationBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; + accelerationBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; + accelerationBuildGeometryInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; + accelerationBuildGeometryInfo.dstAccelerationStructure = bottomLevelAS.handle; + accelerationBuildGeometryInfo.geometryCount = 1; + accelerationBuildGeometryInfo.pGeometries = &accelerationStructureGeometry; + accelerationBuildGeometryInfo.scratchData.deviceAddress = scratchBuffer.deviceAddress; + + VkAccelerationStructureBuildRangeInfoKHR accelerationStructureBuildRangeInfo{}; + accelerationStructureBuildRangeInfo.primitiveCount = numTriangles; + accelerationStructureBuildRangeInfo.primitiveOffset = 0; + accelerationStructureBuildRangeInfo.firstVertex = 0; + accelerationStructureBuildRangeInfo.transformOffset = 0; + std::vector accelerationBuildStructureRangeInfos = { &accelerationStructureBuildRangeInfo }; + + // Build the acceleration structure on the device via a one-time command buffer submission + // Some implementations may support acceleration structure building on the host (VkPhysicalDeviceAccelerationStructureFeaturesKHR->accelerationStructureHostCommands), but we prefer device builds + VkCommandBuffer commandBuffer = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + vkCmdBuildAccelerationStructuresKHR( + commandBuffer, + 1, + &accelerationBuildGeometryInfo, + accelerationBuildStructureRangeInfos.data()); + vulkanDevice->flushCommandBuffer(commandBuffer, queue); + + VkAccelerationStructureDeviceAddressInfoKHR accelerationDeviceAddressInfo{}; + accelerationDeviceAddressInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR; + accelerationDeviceAddressInfo.accelerationStructure = bottomLevelAS.handle; + bottomLevelAS.deviceAddress = vkGetAccelerationStructureDeviceAddressKHR(device, &accelerationDeviceAddressInfo); + + deleteScratchBuffer(scratchBuffer); + } + + /* + The top level acceleration structure contains the scene's object instances + */ + void createTopLevelAccelerationStructure() + { + VkTransformMatrixKHR transformMatrix = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f }; + + VkAccelerationStructureInstanceKHR instance{}; + instance.transform = transformMatrix; + instance.instanceCustomIndex = 0; + instance.mask = 0xFF; + instance.instanceShaderBindingTableRecordOffset = 0; + instance.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR; + instance.accelerationStructureReference = bottomLevelAS.deviceAddress; + + // Buffer for instance data + vks::Buffer instancesBuffer; + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &instancesBuffer, + sizeof(VkAccelerationStructureInstanceKHR), + &instance)); + + VkDeviceOrHostAddressConstKHR instanceDataDeviceAddress{}; + instanceDataDeviceAddress.deviceAddress = getBufferDeviceAddress(instancesBuffer.buffer); + + VkAccelerationStructureGeometryKHR accelerationStructureGeometry{}; + accelerationStructureGeometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR; + accelerationStructureGeometry.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR; + accelerationStructureGeometry.flags = VK_GEOMETRY_OPAQUE_BIT_KHR; + accelerationStructureGeometry.geometry.instances.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR; + accelerationStructureGeometry.geometry.instances.arrayOfPointers = VK_FALSE; + accelerationStructureGeometry.geometry.instances.data = instanceDataDeviceAddress; + + // Get size info + /* + The pSrcAccelerationStructure, dstAccelerationStructure, and mode members of pBuildInfo are ignored. Any VkDeviceOrHostAddressKHR members of pBuildInfo are ignored by this command, except that the hostAddress member of VkAccelerationStructureGeometryTrianglesDataKHR::transformData will be examined to check if it is NULL.* + */ + VkAccelerationStructureBuildGeometryInfoKHR accelerationStructureBuildGeometryInfo{}; + accelerationStructureBuildGeometryInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; + accelerationStructureBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; + accelerationStructureBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; + accelerationStructureBuildGeometryInfo.geometryCount = 1; + accelerationStructureBuildGeometryInfo.pGeometries = &accelerationStructureGeometry; + + uint32_t primitive_count = 1; + + VkAccelerationStructureBuildSizesInfoKHR accelerationStructureBuildSizesInfo{}; + accelerationStructureBuildSizesInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR; + vkGetAccelerationStructureBuildSizesKHR( + device, + VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, + &accelerationStructureBuildGeometryInfo, + &primitive_count, + &accelerationStructureBuildSizesInfo); + + createAccelerationStructureBuffer(topLevelAS, accelerationStructureBuildSizesInfo); + + VkAccelerationStructureCreateInfoKHR accelerationStructureCreateInfo{}; + accelerationStructureCreateInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR; + accelerationStructureCreateInfo.buffer = topLevelAS.buffer; + accelerationStructureCreateInfo.size = accelerationStructureBuildSizesInfo.accelerationStructureSize; + accelerationStructureCreateInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; + vkCreateAccelerationStructureKHR(device, &accelerationStructureCreateInfo, nullptr, &topLevelAS.handle); + + // Create a small scratch buffer used during build of the top level acceleration structure + RayTracingScratchBuffer scratchBuffer = createScratchBuffer(accelerationStructureBuildSizesInfo.buildScratchSize); + + VkAccelerationStructureBuildGeometryInfoKHR accelerationBuildGeometryInfo{}; + accelerationBuildGeometryInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; + accelerationBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; + accelerationBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; + accelerationBuildGeometryInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; + accelerationBuildGeometryInfo.dstAccelerationStructure = topLevelAS.handle; + accelerationBuildGeometryInfo.geometryCount = 1; + accelerationBuildGeometryInfo.pGeometries = &accelerationStructureGeometry; + accelerationBuildGeometryInfo.scratchData.deviceAddress = scratchBuffer.deviceAddress; + + VkAccelerationStructureBuildRangeInfoKHR accelerationStructureBuildRangeInfo{}; + accelerationStructureBuildRangeInfo.primitiveCount = 1; + accelerationStructureBuildRangeInfo.primitiveOffset = 0; + accelerationStructureBuildRangeInfo.firstVertex = 0; + accelerationStructureBuildRangeInfo.transformOffset = 0; + std::vector accelerationBuildStructureRangeInfos = { &accelerationStructureBuildRangeInfo }; + + // Build the acceleration structure on the device via a one-time command buffer submission + // Some implementations may support acceleration structure building on the host (VkPhysicalDeviceAccelerationStructureFeaturesKHR->accelerationStructureHostCommands), but we prefer device builds + VkCommandBuffer commandBuffer = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + vkCmdBuildAccelerationStructuresKHR( + commandBuffer, + 1, + &accelerationBuildGeometryInfo, + accelerationBuildStructureRangeInfos.data()); + vulkanDevice->flushCommandBuffer(commandBuffer, queue); + + VkAccelerationStructureDeviceAddressInfoKHR accelerationDeviceAddressInfo{}; + accelerationDeviceAddressInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR; + accelerationDeviceAddressInfo.accelerationStructure = topLevelAS.handle; + topLevelAS.deviceAddress = vkGetAccelerationStructureDeviceAddressKHR(device, &accelerationDeviceAddressInfo); + + deleteScratchBuffer(scratchBuffer); + instancesBuffer.destroy(); + } + + /* + Create the Shader Binding Tables that binds the programs and top-level acceleration structure + + SBT Layout used in this sample: + + /-----------\ + | raygen | + |-----------| + | miss | + |-----------| + | hit | + \-----------/ + + */ + void createShaderBindingTable() { + const uint32_t handleSize = rayTracingPipelineProperties.shaderGroupHandleSize; + const uint32_t handleSizeAligned = vks::tools::alignedSize(rayTracingPipelineProperties.shaderGroupHandleSize, rayTracingPipelineProperties.shaderGroupHandleAlignment); + const uint32_t groupCount = static_cast(shaderGroups.size()); + const uint32_t sbtSize = groupCount * handleSizeAligned; + + std::vector shaderHandleStorage(sbtSize); + VK_CHECK_RESULT(vkGetRayTracingShaderGroupHandlesKHR(device, pipeline, 0, groupCount, sbtSize, shaderHandleStorage.data())); + + const VkBufferUsageFlags bufferUsageFlags = VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + const VkMemoryPropertyFlags memoryUsageFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + VK_CHECK_RESULT(vulkanDevice->createBuffer(bufferUsageFlags, memoryUsageFlags, &raygenShaderBindingTable, handleSize + sizeof(float) * 3)); + VK_CHECK_RESULT(vulkanDevice->createBuffer(bufferUsageFlags, memoryUsageFlags, &missShaderBindingTable, handleSize + sizeof(float) * 3)); + VK_CHECK_RESULT(vulkanDevice->createBuffer(bufferUsageFlags, memoryUsageFlags, &hitShaderBindingTable, handleSize + sizeof(float) * 3)); + + // Copy handles + raygenShaderBindingTable.map(); + missShaderBindingTable.map(); + hitShaderBindingTable.map(); + memcpy(raygenShaderBindingTable.mapped, shaderHandleStorage.data(), handleSize); + memcpy(missShaderBindingTable.mapped, shaderHandleStorage.data() + handleSizeAligned, handleSize); + memcpy(hitShaderBindingTable.mapped, shaderHandleStorage.data() + handleSizeAligned * 2, handleSize); + + // set raygen sbt colors + { + glm::vec3 color(1.f, 0.f, 0.f); + memcpy(((uint8_t*)(raygenShaderBindingTable.mapped)) + handleSize, &color, sizeof(glm::vec3)); + } + + // set chit sbt colors + { + glm::vec3 color(0.f, 1.f, 0.f); + memcpy(((uint8_t*)(hitShaderBindingTable.mapped)) + handleSize, &color, sizeof(glm::vec3)); + } + } + + /* + Create the descriptor sets used for the ray tracing dispatch + */ + void createDescriptorSets() + { + std::vector poolSizes = { + { VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1 }, + { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1 } + }; + VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 1); + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCreateInfo, nullptr, &descriptorPool)); + + VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocateInfo, &descriptorSet)); + + VkWriteDescriptorSetAccelerationStructureKHR descriptorAccelerationStructureInfo{}; + descriptorAccelerationStructureInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR; + descriptorAccelerationStructureInfo.accelerationStructureCount = 1; + descriptorAccelerationStructureInfo.pAccelerationStructures = &topLevelAS.handle; + + VkWriteDescriptorSet accelerationStructureWrite{}; + accelerationStructureWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + // The specialized acceleration structure descriptor has to be chained + accelerationStructureWrite.pNext = &descriptorAccelerationStructureInfo; + accelerationStructureWrite.dstSet = descriptorSet; + accelerationStructureWrite.dstBinding = 0; + accelerationStructureWrite.descriptorCount = 1; + accelerationStructureWrite.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; + + VkDescriptorImageInfo storageImageDescriptor{}; + storageImageDescriptor.imageView = storageImage.view; + storageImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + + VkWriteDescriptorSet resultImageWrite = vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, &storageImageDescriptor); + VkWriteDescriptorSet uniformBufferWrite = vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2, &ubo.descriptor); + + std::vector writeDescriptorSets = { + accelerationStructureWrite, + resultImageWrite, + uniformBufferWrite + }; + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, VK_NULL_HANDLE); + } + + /* + Create our ray tracing pipeline + */ + void createRayTracingPipeline() + { + VkDescriptorSetLayoutBinding accelerationStructureLayoutBinding{}; + accelerationStructureLayoutBinding.binding = 0; + accelerationStructureLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; + accelerationStructureLayoutBinding.descriptorCount = 1; + accelerationStructureLayoutBinding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR; + + VkDescriptorSetLayoutBinding resultImageLayoutBinding{}; + resultImageLayoutBinding.binding = 1; + resultImageLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + resultImageLayoutBinding.descriptorCount = 1; + resultImageLayoutBinding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR; + + VkDescriptorSetLayoutBinding uniformBufferBinding{}; + uniformBufferBinding.binding = 2; + uniformBufferBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uniformBufferBinding.descriptorCount = 1; + uniformBufferBinding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR; + + std::vector bindings({ + accelerationStructureLayoutBinding, + resultImageLayoutBinding, + uniformBufferBinding + }); + + VkDescriptorSetLayoutCreateInfo descriptorSetlayoutCI{}; + descriptorSetlayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptorSetlayoutCI.bindingCount = static_cast(bindings.size()); + descriptorSetlayoutCI.pBindings = bindings.data(); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetlayoutCI, nullptr, &descriptorSetLayout)); + + VkPipelineLayoutCreateInfo pipelineLayoutCI{}; + pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutCI.setLayoutCount = 1; + pipelineLayoutCI.pSetLayouts = &descriptorSetLayout; + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout)); + + /* + Setup ray tracing shader groups + */ + std::vector shaderStages; + + // Ray generation group + { + shaderStages.push_back(loadShader(getShadersPath() + "raytracingsbtdata/raygen.rgen.spv", VK_SHADER_STAGE_RAYGEN_BIT_KHR)); + VkRayTracingShaderGroupCreateInfoKHR shaderGroup{}; + shaderGroup.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR; + shaderGroup.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR; + shaderGroup.generalShader = static_cast(shaderStages.size()) - 1; + shaderGroup.closestHitShader = VK_SHADER_UNUSED_KHR; + shaderGroup.anyHitShader = VK_SHADER_UNUSED_KHR; + shaderGroup.intersectionShader = VK_SHADER_UNUSED_KHR; + shaderGroups.push_back(shaderGroup); + } + + // Miss group + { + shaderStages.push_back(loadShader(getShadersPath() + "raytracingsbtdata/miss.rmiss.spv", VK_SHADER_STAGE_MISS_BIT_KHR)); + VkRayTracingShaderGroupCreateInfoKHR shaderGroup{}; + shaderGroup.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR; + shaderGroup.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR; + shaderGroup.generalShader = static_cast(shaderStages.size()) - 1; + shaderGroup.closestHitShader = VK_SHADER_UNUSED_KHR; + shaderGroup.anyHitShader = VK_SHADER_UNUSED_KHR; + shaderGroup.intersectionShader = VK_SHADER_UNUSED_KHR; + shaderGroups.push_back(shaderGroup); + } + + // Closest hit group + { + shaderStages.push_back(loadShader(getShadersPath() + "raytracingsbtdata/closesthit.rchit.spv", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR)); + VkRayTracingShaderGroupCreateInfoKHR shaderGroup{}; + shaderGroup.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR; + shaderGroup.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR; + shaderGroup.generalShader = VK_SHADER_UNUSED_KHR; + shaderGroup.closestHitShader = static_cast(shaderStages.size()) - 1; + shaderGroup.anyHitShader = VK_SHADER_UNUSED_KHR; + shaderGroup.intersectionShader = VK_SHADER_UNUSED_KHR; + shaderGroups.push_back(shaderGroup); + } + + /* + Create the ray tracing pipeline + */ + VkRayTracingPipelineCreateInfoKHR rayTracingPipelineCI{}; + rayTracingPipelineCI.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR; + rayTracingPipelineCI.stageCount = static_cast(shaderStages.size()); + rayTracingPipelineCI.pStages = shaderStages.data(); + rayTracingPipelineCI.groupCount = static_cast(shaderGroups.size()); + rayTracingPipelineCI.pGroups = shaderGroups.data(); + rayTracingPipelineCI.maxPipelineRayRecursionDepth = 1; + rayTracingPipelineCI.layout = pipelineLayout; + VK_CHECK_RESULT(vkCreateRayTracingPipelinesKHR(device, VK_NULL_HANDLE, VK_NULL_HANDLE, 1, &rayTracingPipelineCI, nullptr, &pipeline)); + } + + /* + Create the uniform buffer used to pass matrices to the ray tracing ray generation shader + */ + void createUniformBuffer() + { + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &ubo, + sizeof(uniformData), + &uniformData)); + VK_CHECK_RESULT(ubo.map()); + + updateUniformBuffers(); + } + + /* + If the window has been resized, we need to recreate the storage image and it's descriptor + */ + void handleResize() + { + // Delete allocated resources + vkDestroyImageView(device, storageImage.view, nullptr); + vkDestroyImage(device, storageImage.image, nullptr); + vkFreeMemory(device, storageImage.memory, nullptr); + // Recreate image + createStorageImage(); + // Update descriptor + VkDescriptorImageInfo storageImageDescriptor{ VK_NULL_HANDLE, storageImage.view, VK_IMAGE_LAYOUT_GENERAL }; + VkWriteDescriptorSet resultImageWrite = vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, &storageImageDescriptor); + vkUpdateDescriptorSets(device, 1, &resultImageWrite, 0, VK_NULL_HANDLE); + } + + /* + Command buffer generation + */ + void buildCommandBuffers() + { + if (resized) + { + handleResize(); + } + + VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); + + VkImageSubresourceRange subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + + for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) + { + VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); + + /* + Setup the buffer regions pointing to the shaders in our shader binding table + */ + + const uint32_t handleSizeAligned = vks::tools::alignedSize(rayTracingPipelineProperties.shaderGroupHandleSize, rayTracingPipelineProperties.shaderGroupHandleAlignment); + + VkStridedDeviceAddressRegionKHR raygenShaderSbtEntry{}; + raygenShaderSbtEntry.deviceAddress = getBufferDeviceAddress(raygenShaderBindingTable.buffer); + raygenShaderSbtEntry.stride = handleSizeAligned; + raygenShaderSbtEntry.size = handleSizeAligned; + + VkStridedDeviceAddressRegionKHR missShaderSbtEntry{}; + missShaderSbtEntry.deviceAddress = getBufferDeviceAddress(missShaderBindingTable.buffer); + missShaderSbtEntry.stride = handleSizeAligned; + missShaderSbtEntry.size = handleSizeAligned; + + VkStridedDeviceAddressRegionKHR hitShaderSbtEntry{}; + hitShaderSbtEntry.deviceAddress = getBufferDeviceAddress(hitShaderBindingTable.buffer); + hitShaderSbtEntry.stride = handleSizeAligned; + hitShaderSbtEntry.size = handleSizeAligned; + + VkStridedDeviceAddressRegionKHR callableShaderSbtEntry{}; + + /* + Dispatch the ray tracing commands + */ + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline); + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipelineLayout, 0, 1, &descriptorSet, 0, 0); + + vkCmdTraceRaysKHR( + drawCmdBuffers[i], + &raygenShaderSbtEntry, + &missShaderSbtEntry, + &hitShaderSbtEntry, + &callableShaderSbtEntry, + width, + height, + 1); + + /* + Copy ray tracing output to swap chain image + */ + + // Prepare current swap chain image as transfer destination + vks::tools::setImageLayout( + drawCmdBuffers[i], + swapChain.images[i], + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + subresourceRange); + + // Prepare ray tracing output image as transfer source + vks::tools::setImageLayout( + drawCmdBuffers[i], + storageImage.image, + VK_IMAGE_LAYOUT_GENERAL, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + subresourceRange); + + VkImageCopy copyRegion{}; + copyRegion.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }; + copyRegion.srcOffset = { 0, 0, 0 }; + copyRegion.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }; + copyRegion.dstOffset = { 0, 0, 0 }; + copyRegion.extent = { width, height, 1 }; + vkCmdCopyImage(drawCmdBuffers[i], storageImage.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, swapChain.images[i], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Region); + + // Transition swap chain image back for presentation + vks::tools::setImageLayout( + drawCmdBuffers[i], + swapChain.images[i], + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + subresourceRange); + + // Transition ray tracing output image back to general layout + vks::tools::setImageLayout( + drawCmdBuffers[i], + storageImage.image, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_IMAGE_LAYOUT_GENERAL, + subresourceRange); + + VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); + } + } + + void updateUniformBuffers() + { + uniformData.projInverse = glm::inverse(camera.matrices.perspective); + uniformData.viewInverse = glm::inverse(camera.matrices.view); + memcpy(ubo.mapped, &uniformData, sizeof(uniformData)); + } + + void getEnabledFeatures() + { + // Enable features required for ray tracing using feature chaining via pNext + enabledBufferDeviceAddresFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES; + enabledBufferDeviceAddresFeatures.bufferDeviceAddress = VK_TRUE; + + enabledRayTracingPipelineFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR; + enabledRayTracingPipelineFeatures.rayTracingPipeline = VK_TRUE; + enabledRayTracingPipelineFeatures.pNext = &enabledBufferDeviceAddresFeatures; + + enabledAccelerationStructureFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR; + enabledAccelerationStructureFeatures.accelerationStructure = VK_TRUE; + enabledAccelerationStructureFeatures.pNext = &enabledRayTracingPipelineFeatures; + + deviceCreatepNextChain = &enabledAccelerationStructureFeatures; + } + + void prepare() + { + VulkanExampleBase::prepare(); + + // Get ray tracing pipeline properties, which will be used later on in the sample + rayTracingPipelineProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR; + VkPhysicalDeviceProperties2 deviceProperties2{}; + deviceProperties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + deviceProperties2.pNext = &rayTracingPipelineProperties; + vkGetPhysicalDeviceProperties2(physicalDevice, &deviceProperties2); + + // Get acceleration structure properties, which will be used later on in the sample + accelerationStructureFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR; + VkPhysicalDeviceFeatures2 deviceFeatures2{}; + deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + deviceFeatures2.pNext = &accelerationStructureFeatures; + vkGetPhysicalDeviceFeatures2(physicalDevice, &deviceFeatures2); + + // Get the ray tracing and accelertion structure related function pointers required by this sample + vkGetBufferDeviceAddressKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetBufferDeviceAddressKHR")); + vkCmdBuildAccelerationStructuresKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdBuildAccelerationStructuresKHR")); + vkBuildAccelerationStructuresKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkBuildAccelerationStructuresKHR")); + vkCreateAccelerationStructureKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCreateAccelerationStructureKHR")); + vkDestroyAccelerationStructureKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkDestroyAccelerationStructureKHR")); + vkGetAccelerationStructureBuildSizesKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetAccelerationStructureBuildSizesKHR")); + vkGetAccelerationStructureDeviceAddressKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetAccelerationStructureDeviceAddressKHR")); + vkCmdTraceRaysKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdTraceRaysKHR")); + vkGetRayTracingShaderGroupHandlesKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetRayTracingShaderGroupHandlesKHR")); + vkCreateRayTracingPipelinesKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCreateRayTracingPipelinesKHR")); + + // Create the acceleration structures used to render the ray traced scene + createBottomLevelAccelerationStructure(); + createTopLevelAccelerationStructure(); + + createStorageImage(); + createUniformBuffer(); + createRayTracingPipeline(); + createShaderBindingTable(); + createDescriptorSets(); + buildCommandBuffers(); + prepared = true; + } + + void draw() + { + VulkanExampleBase::prepareFrame(); + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); + VulkanExampleBase::submitFrame(); + } + + virtual void render() + { + if (!prepared) + return; + draw(); + if (camera.updated) + updateUniformBuffers(); + } +}; + +VULKAN_EXAMPLE_MAIN()