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 00000000..031bef11 Binary files /dev/null and b/data/shaders/glsl/raytracingsbtdata/closesthit.rchit.spv differ diff --git a/data/shaders/glsl/raytracingsbtdata/miss.rmiss b/data/shaders/glsl/raytracingsbtdata/miss.rmiss new file mode 100644 index 00000000..36eb1b4b --- /dev/null +++ b/data/shaders/glsl/raytracingsbtdata/miss.rmiss @@ -0,0 +1,17 @@ +#version 460 +#extension GL_EXT_ray_tracing : enable + +layout(location = 0) rayPayloadInEXT vec3 hitValue; + +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 + // miss record + hitValue = vec3(r, g, b); +} \ 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 00000000..8f6019c1 Binary files /dev/null and b/data/shaders/glsl/raytracingsbtdata/miss.rmiss.spv differ diff --git a/data/shaders/glsl/raytracingsbtdata/raygen.rgen b/data/shaders/glsl/raytracingsbtdata/raygen.rgen new file mode 100644 index 00000000..42282dab --- /dev/null +++ b/data/shaders/glsl/raytracingsbtdata/raygen.rgen @@ -0,0 +1,52 @@ +#version 460 +#extension GL_EXT_ray_tracing : enable + +layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS; +layout(binding = 1, set = 0, rgba8) uniform image2D image; +layout(binding = 2, set = 0) uniform CameraProperties +{ + mat4 viewInverse; + mat4 projInverse; +} cam; + +layout(location = 0) rayPayloadEXT vec3 hitValue; + +layout(shaderRecordEXT, std430) buffer SBT { + float r; + float g; + float b; +}; + +void main() +{ + const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + vec2(0.5); + const vec2 inUV = pixelCenter/vec2(gl_LaunchSizeEXT.xy); + vec2 d = inUV * 2.0 - 1.0; + + vec4 origin = cam.viewInverse * vec4(0,0,0,1); + vec4 target = cam.projInverse * vec4(d.x, d.y, 1, 1) ; + vec4 direction = cam.viewInverse*vec4(normalize(target.xyz), 0) ; + + float tmin = 0.001; + float tmax = 10000.0; + + // use border to demonstrate raygen record data + if (all(greaterThan(gl_LaunchIDEXT.xy, ivec2(16, 16))) && all(lessThan(gl_LaunchIDEXT.xy, gl_LaunchSizeEXT.xy - ivec2(16, 16)))) + { + // Generate a checker board pattern to trace out rays or use hit record data + ivec2 pos = ivec2(gl_LaunchIDEXT / 16); + if (((pos.x + pos.y % 2) % 2) == 0) { + // This will set hit value to either hit or miss SBT record color + traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, origin.xyz, tmin, direction.xyz, tmax, 0); + } + else { + // Set the hit value to the raygen SBT data + hitValue = vec3(r, g, b); + } + } + else { + // Set hit value to black + hitValue = vec3(0.0, 0.0, 0.0); + } + imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(hitValue, 0.0)); +} diff --git a/data/shaders/glsl/raytracingsbtdata/raygen.rgen.spv b/data/shaders/glsl/raytracingsbtdata/raygen.rgen.spv new file mode 100644 index 00000000..7713c044 Binary files /dev/null and b/data/shaders/glsl/raytracingsbtdata/raygen.rgen.spv differ diff --git a/data/shaders/hlsl/raytracingsbtdata/closesthit.rchit b/data/shaders/hlsl/raytracingsbtdata/closesthit.rchit new file mode 100644 index 00000000..1b306450 --- /dev/null +++ b/data/shaders/hlsl/raytracingsbtdata/closesthit.rchit @@ -0,0 +1,27 @@ +// Copyright 2020 Google LLC + +struct Attribute +{ + float2 attribs; +}; + +struct Payload +{ +[[vk::location(0)]] float3 hitValue; +}; + +struct SBT { + float r; + float g; + float b; +}; +[[vk::shader_record_ext]] +ConstantBuffer 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 00000000..41e87ffc Binary files /dev/null and b/data/shaders/hlsl/raytracingsbtdata/closesthit.rchit.spv differ diff --git a/data/shaders/hlsl/raytracingsbtdata/miss.rmiss b/data/shaders/hlsl/raytracingsbtdata/miss.rmiss new file mode 100644 index 00000000..6d9fad60 --- /dev/null +++ b/data/shaders/hlsl/raytracingsbtdata/miss.rmiss @@ -0,0 +1,22 @@ +// Copyright 2020 Google LLC + +struct Payload +{ +[[vk::location(0)]] float3 hitValue; +}; + +struct SBT { + float r; + float g; + float b; +}; +[[vk::shader_record_ext]] +ConstantBuffer sbt; + +[shader("miss")] +void main(inout Payload p) +{ + // Update the hit value to the hit record SBT data associated with this + // miss record + p.hitValue = float3(sbt.r, sbt.g, sbt.g); +} \ No newline at end of file diff --git a/data/shaders/hlsl/raytracingsbtdata/miss.rmiss.spv b/data/shaders/hlsl/raytracingsbtdata/miss.rmiss.spv new file mode 100644 index 00000000..90f3c1bf Binary files /dev/null and b/data/shaders/hlsl/raytracingsbtdata/miss.rmiss.spv differ diff --git a/data/shaders/hlsl/raytracingsbtdata/raygen.rgen b/data/shaders/hlsl/raytracingsbtdata/raygen.rgen new file mode 100644 index 00000000..e79749c2 --- /dev/null +++ b/data/shaders/hlsl/raytracingsbtdata/raygen.rgen @@ -0,0 +1,65 @@ +// Copyright 2020 Google LLC + +RaytracingAccelerationStructure rs : register(t0); +RWTexture2D 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; + + Payload payload; + + // use border to demonstrate raygen record data + if (all(LaunchID.xy > int2(16, 16)) && all(LaunchID.xy < LaunchSize.xy - int2(16, 16))) + { + // Generate a checker board pattern to trace out rays or use hit record data + int2 pos = int2(LaunchID.xy / 16); + if (((pos.x + pos.y % 2) % 2) == 0) { + // This will set hit value to either hit or miss SBT record color + TraceRay(rs, RAY_FLAG_FORCE_OPAQUE, 0xff, 0, 0, 0, rayDesc, payload); + } + else { + // Set the hit value to the raygen SBT data + payload.hitValue = float3(sbt.r, sbt.g, sbt.b); + } + } + else { + // Set hit value to black + payload.hitValue = float3(0.0, 0.0, 0.0); + } + + 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 00000000..7a2516cd Binary files /dev/null and b/data/shaders/hlsl/raytracingsbtdata/raygen.rgen.spv differ 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..46e4f43a --- /dev/null +++ b/examples/raytracingsbtdata/raytracingsbtdata.cpp @@ -0,0 +1,936 @@ +/* +* 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 +* +* Example by Nate Morrical (https://github.com/natevm) +* +* 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 + In this example, we embed data in each record that can be read by the device during ray tracing + + SBT Layout used in this sample: + + /----------------\ + | raygen handle | + | - - - - - - - | + | raygen data | + |----------------| + | miss handle | + | - - - - - - - | + | miss data | + |----------------| + | hit handle | + | - - - - - - - | + | hit data | + \----------------/ + + */ + 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; + + // We allocate space for the handle (which is like lambda function pointers to call in the ray tracing pipeline) + // as well as the data to pass to those functions (which act as the variables being "captured" by those lambda functions) + 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); + + // Copy over raygen record data + glm::vec3 color1(0.5f, 0.5f, 0.5f); + memcpy(((uint8_t*)(raygenShaderBindingTable.mapped)) + handleSize, &color1, sizeof(glm::vec3)); + + // Copy over miss record data + glm::vec3 color2(1.f, 1.f, 1.f); + memcpy(((uint8_t*)(missShaderBindingTable.mapped)) + handleSize, &color2, sizeof(glm::vec3)); + + // Copy over hit group record data + glm::vec3 color3(1.f, 0.f, 0.f); + memcpy(((uint8_t*)(hitShaderBindingTable.mapped)) + handleSize, &color3, 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); + + // Note, we add 3 * sizeof(float) to each SBT entry size to account for the data sections of these records + // that we use to store our color data + VkStridedDeviceAddressRegionKHR raygenShaderSbtEntry{}; + raygenShaderSbtEntry.deviceAddress = getBufferDeviceAddress(raygenShaderBindingTable.buffer); + raygenShaderSbtEntry.stride = handleSizeAligned; + raygenShaderSbtEntry.size = vks::tools::alignedSize(handleSizeAligned + 3 * sizeof(float), rayTracingPipelineProperties.shaderGroupBaseAlignment); + + VkStridedDeviceAddressRegionKHR missShaderSbtEntry{}; + missShaderSbtEntry.deviceAddress = getBufferDeviceAddress(missShaderBindingTable.buffer); + missShaderSbtEntry.stride = handleSizeAligned; + missShaderSbtEntry.size = vks::tools::alignedSize(handleSizeAligned + 3 * sizeof(float), rayTracingPipelineProperties.shaderGroupBaseAlignment); + + VkStridedDeviceAddressRegionKHR hitShaderSbtEntry{}; + hitShaderSbtEntry.deviceAddress = getBufferDeviceAddress(hitShaderBindingTable.buffer); + hitShaderSbtEntry.stride = handleSizeAligned; + hitShaderSbtEntry.size = vks::tools::alignedSize(handleSizeAligned + 3 * sizeof(float), rayTracingPipelineProperties.shaderGroupBaseAlignment); + + 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()