diff --git a/data/shaders/nv_ray_tracing_basic/closesthit.rchit b/data/shaders/nv_ray_tracing_basic/closesthit.rchit new file mode 100644 index 00000000..dea0056d --- /dev/null +++ b/data/shaders/nv_ray_tracing_basic/closesthit.rchit @@ -0,0 +1,11 @@ +#version 460 +#extension GL_NV_ray_tracing : require +#extension GL_EXT_nonuniform_qualifier : enable + +layout(location = 0) rayPayloadInNV vec3 hitValue; +hitAttributeNV vec3 attribs; + +void main() +{ + hitValue = vec3(1.0, 1.0, 1.0); +} diff --git a/data/shaders/nv_ray_tracing_basic/closesthit.rchit.spv b/data/shaders/nv_ray_tracing_basic/closesthit.rchit.spv new file mode 100644 index 00000000..63bfa61a Binary files /dev/null and b/data/shaders/nv_ray_tracing_basic/closesthit.rchit.spv differ diff --git a/data/shaders/nv_ray_tracing_basic/miss.rmiss b/data/shaders/nv_ray_tracing_basic/miss.rmiss new file mode 100644 index 00000000..a00c59b5 --- /dev/null +++ b/data/shaders/nv_ray_tracing_basic/miss.rmiss @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 hitValue; + +void main() +{ + hitValue = vec3(0.0, 0.0, 0.2); +} \ No newline at end of file diff --git a/data/shaders/nv_ray_tracing_basic/miss.rmiss.spv b/data/shaders/nv_ray_tracing_basic/miss.rmiss.spv new file mode 100644 index 00000000..81c14644 Binary files /dev/null and b/data/shaders/nv_ray_tracing_basic/miss.rmiss.spv differ diff --git a/data/shaders/nv_ray_tracing_basic/raygen.rgen b/data/shaders/nv_ray_tracing_basic/raygen.rgen new file mode 100644 index 00000000..c11ca526 --- /dev/null +++ b/data/shaders/nv_ray_tracing_basic/raygen.rgen @@ -0,0 +1,32 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(binding = 0, set = 0) uniform accelerationStructureNV 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) rayPayloadNV vec3 hitValue; + +void main() +{ + const vec2 pixelCenter = vec2(gl_LaunchIDNV.xy) + vec2(0.5); + const vec2 inUV = pixelCenter/vec2(gl_LaunchSizeNV.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) ; + + uint rayFlags = gl_RayFlagsOpaqueNV; + uint cullMask = 0xff; + float tmin = 0.001; + float tmax = 10000.0; + + traceNV(topLevelAS, rayFlags, cullMask, 0, 0, 0, origin.xyz, tmin, direction.xyz, tmax, 0); + + imageStore(image, ivec2(gl_LaunchIDNV.xy), vec4(hitValue, 0.0)); +} diff --git a/data/shaders/nv_ray_tracing_basic/raygen.rgen.spv b/data/shaders/nv_ray_tracing_basic/raygen.rgen.spv new file mode 100644 index 00000000..e388213f Binary files /dev/null and b/data/shaders/nv_ray_tracing_basic/raygen.rgen.spv differ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 7745b90f..de4f07d0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -74,6 +74,7 @@ set(EXAMPLES multithreading multiview negativeviewportheight + nv_ray_tracing_basic occlusionquery offscreen parallaxmapping diff --git a/examples/nv_ray_tracing_basic/nv_ray_tracing_basic.cpp b/examples/nv_ray_tracing_basic/nv_ray_tracing_basic.cpp new file mode 100644 index 00000000..bad39abe --- /dev/null +++ b/examples/nv_ray_tracing_basic/nv_ray_tracing_basic.cpp @@ -0,0 +1,731 @@ +/* +* Vulkan Example - Basic example for ray tracing using VK_NV_ray_tracing +* +* Copyright (C) by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#include +#include +#include +#include +#include + +#define GLM_FORCE_RADIANS +#define GLM_FORCE_DEPTH_ZERO_TO_ONE +#include +#include +#include + +#include +#include "vulkanexamplebase.h" +#include "VulkanDevice.hpp" +#include "VulkanBuffer.hpp" + +#define ENABLE_VALIDATION false + +// Ray tracing acceleration structure +struct AccelerationStructure { + VkDeviceMemory memory; + VkAccelerationStructureInfoNV accelerationStructureInfo; + VkAccelerationStructureNV accelerationStructure; + uint64_t handle; +}; + +// Ray tracing geometry instance +struct GeometryInstance { + float transform[12]; + uint32_t instanceId : 24; + uint32_t mask : 8; + uint32_t instanceOffset : 24; + uint32_t flags : 8; + uint64_t accelerationStructureHandle; +}; + +// Indices for the different ray tracing shader types used in this example +#define SHADER_INDEX_RAYGEN 0 +#define SHADER_INDEX_MISS 1 +#define SHADER_INDEX_CLOSEST_HIT 2 + +class VulkanExample : public VulkanExampleBase +{ +public: + PFN_vkCreateAccelerationStructureNV vkCreateAccelerationStructureNV; + PFN_vkBindAccelerationStructureMemoryNV vkBindAccelerationStructureMemoryNV; + PFN_vkGetAccelerationStructureHandleNV vkGetAccelerationStructureHandleNV; + PFN_vkGetAccelerationStructureMemoryRequirementsNV vkGetAccelerationStructureMemoryRequirementsNV; + PFN_vkCmdBuildAccelerationStructureNV vkCmdBuildAccelerationStructureNV; + PFN_vkCreateRayTracingPipelinesNV vkCreateRayTracingPipelinesNV; + PFN_vkGetRayTracingShaderGroupHandlesNV vkGetRayTracingShaderGroupHandlesNV; + PFN_vkCmdTraceRaysNV vkCmdTraceRaysNV; + + VkPhysicalDeviceRayTracingPropertiesNV rayTracingProperties{}; + + AccelerationStructure bottomLevelAS; + AccelerationStructure topLevelAS; + + vks::Buffer vertexBuffer; + vks::Buffer indexBuffer; + uint32_t indexCount; + vks::Buffer shaderBindingTable; + + 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(ENABLE_VALIDATION) + { + title = "VK_NV_ray_tracing"; + settings.overlay = true; + 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)); + // Enable instance and device extensions required to use VK_NV_ray_tracing + enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_NV_RAY_TRACING_EXTENSION_NAME); + } + + ~VulkanExample() + { + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + + vertexBuffer.destroy(); + indexBuffer.destroy(); + ubo.destroy(); + } + + /* + 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); + } + + /* + The bottom level acceleration structure contains the scene's geometry (vertices, triangles) + */ + void createBottomLevelAccelerationStructure(const VkGeometryNV* geometries) + { + VkAccelerationStructureInfoNV accelerationStructureInfo{}; + accelerationStructureInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_INFO_NV; + accelerationStructureInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV; + accelerationStructureInfo.instanceCount = 0; + accelerationStructureInfo.geometryCount = 1; + accelerationStructureInfo.pGeometries = geometries; + + VkAccelerationStructureCreateInfoNV accelerationStructureCI{}; + accelerationStructureCI.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_NV; + accelerationStructureCI.info = accelerationStructureInfo; + VK_CHECK_RESULT(vkCreateAccelerationStructureNV(device, &accelerationStructureCI, nullptr, &bottomLevelAS.accelerationStructure)); + + VkAccelerationStructureMemoryRequirementsInfoNV memoryRequirementsInfo{}; + memoryRequirementsInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_INFO_NV; + memoryRequirementsInfo.accelerationStructure = bottomLevelAS.accelerationStructure; + + VkMemoryRequirements2 memoryRequirements2{}; + vkGetAccelerationStructureMemoryRequirementsNV(device, &memoryRequirementsInfo, &memoryRequirements2); + + VkMemoryAllocateInfo memoryAllocateInfo = vks::initializers::memoryAllocateInfo(); + memoryAllocateInfo.allocationSize = memoryRequirements2.memoryRequirements.size; + memoryAllocateInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memoryRequirements2.memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device, &memoryAllocateInfo, nullptr, &bottomLevelAS.memory)); + + VkBindAccelerationStructureMemoryInfoNV accelerationStructureMemoryInfo{}; + accelerationStructureMemoryInfo.sType = VK_STRUCTURE_TYPE_BIND_ACCELERATION_STRUCTURE_MEMORY_INFO_NV; + accelerationStructureMemoryInfo.accelerationStructure = bottomLevelAS.accelerationStructure; + accelerationStructureMemoryInfo.memory = bottomLevelAS.memory; + VK_CHECK_RESULT(vkBindAccelerationStructureMemoryNV(device, 1, &accelerationStructureMemoryInfo)); + + VK_CHECK_RESULT(vkGetAccelerationStructureHandleNV(device, bottomLevelAS.accelerationStructure, sizeof(uint64_t), &bottomLevelAS.handle)); + } + + /* + The top level acceleration structure contains the scene's object instances + */ + void createTopLevelAccelerationStructure() + { + VkAccelerationStructureInfoNV accelerationStructureInfo{}; + accelerationStructureInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_INFO_NV; + accelerationStructureInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_NV; + accelerationStructureInfo.instanceCount = 1; + accelerationStructureInfo.geometryCount = 0; + + VkAccelerationStructureCreateInfoNV accelerationStructureCI{}; + accelerationStructureCI.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_NV; + accelerationStructureCI.info = accelerationStructureInfo; + VK_CHECK_RESULT(vkCreateAccelerationStructureNV(device, &accelerationStructureCI, nullptr, &topLevelAS.accelerationStructure)); + + VkAccelerationStructureMemoryRequirementsInfoNV memoryRequirementsInfo{}; + memoryRequirementsInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_INFO_NV; + memoryRequirementsInfo.accelerationStructure = topLevelAS.accelerationStructure; + + VkMemoryRequirements2 memoryRequirements2{}; + vkGetAccelerationStructureMemoryRequirementsNV(device, &memoryRequirementsInfo, &memoryRequirements2); + + VkMemoryAllocateInfo memoryAllocateInfo = vks::initializers::memoryAllocateInfo(); + memoryAllocateInfo.allocationSize = memoryRequirements2.memoryRequirements.size; + memoryAllocateInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memoryRequirements2.memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device, &memoryAllocateInfo, nullptr, &topLevelAS.memory)); + + VkBindAccelerationStructureMemoryInfoNV accelerationStructureMemoryInfo{}; + accelerationStructureMemoryInfo.sType = VK_STRUCTURE_TYPE_BIND_ACCELERATION_STRUCTURE_MEMORY_INFO_NV; + accelerationStructureMemoryInfo.accelerationStructure = topLevelAS.accelerationStructure; + accelerationStructureMemoryInfo.memory = topLevelAS.memory; + VK_CHECK_RESULT(vkBindAccelerationStructureMemoryNV(device, 1, &accelerationStructureMemoryInfo)); + + VK_CHECK_RESULT(vkGetAccelerationStructureHandleNV(device, topLevelAS.accelerationStructure, sizeof(uint64_t), &topLevelAS.handle)); + } + + /* + Create scene geometry and ray tracing acceleration structures + */ + void createScene() + { + // Setup vertices for a single uv-mapped quad made from two triangles + struct Vertex { + float pos[4]; + }; + std::vector vertices = { + { { 1.0f, 1.0f, 0.0f, 1.0f } }, + { { -1.0f, 1.0f, 0.0f, 1.0f } }, + { { 0.0f, -1.0f, 0.0f, 1.0f } } + }; + + // Setup indices + std::vector indices = { 0, 1, 2 }; + indexCount = static_cast(indices.size()); + + // 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_VERTEX_BUFFER_BIT, + 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_INDEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &indexBuffer, + indices.size() * sizeof(uint32_t), + indices.data())); + + /* + Create the bottom level acceleration structure containing the actual scene geometry + */ + VkGeometryNV geometry{}; + geometry.sType = VK_STRUCTURE_TYPE_GEOMETRY_NV; + geometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_NV; + geometry.geometry.triangles.sType = VK_STRUCTURE_TYPE_GEOMETRY_TRIANGLES_NV; + geometry.geometry.triangles.vertexData = vertexBuffer.buffer; + geometry.geometry.triangles.vertexOffset = 0; + geometry.geometry.triangles.vertexCount = static_cast(vertices.size()); + geometry.geometry.triangles.vertexStride = sizeof(Vertex); + geometry.geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32A32_SFLOAT; + geometry.geometry.triangles.indexData = indexBuffer.buffer; + geometry.geometry.triangles.indexOffset = 0; + geometry.geometry.triangles.indexCount = indexCount; + geometry.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32; + geometry.geometry.triangles.transformData = VK_NULL_HANDLE; + geometry.geometry.triangles.transformOffset = 0; + geometry.geometry.aabbs = {}; + geometry.geometry.aabbs.sType = { VK_STRUCTURE_TYPE_GEOMETRY_AABB_NV }; + geometry.flags = VK_GEOMETRY_OPAQUE_BIT_NV; + + createBottomLevelAccelerationStructure(&geometry); + + /* + Create the top-level acceleration structure that contains geometry instance information + */ + + // Single instance with a 3x3 transform matrix for the ray traced triangle + vks::Buffer instanceBuffer; + const float transform[12] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + }; + + GeometryInstance instance{}; + std::memcpy(instance.transform, transform, sizeof(transform)); + instance.instanceId = 0; + instance.mask = 0xff; + instance.instanceOffset = 0; + instance.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_CULL_DISABLE_BIT_NV; + instance.accelerationStructureHandle = bottomLevelAS.handle; + + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_RAY_TRACING_BIT_NV, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &instanceBuffer, + sizeof(instance), + &instance)); + + createTopLevelAccelerationStructure(); + + /* + Build the acceleration structure + */ + + // Acceleration structure build requires some scratch space to store temporary information + VkAccelerationStructureMemoryRequirementsInfoNV memoryRequirementsInfo{}; + memoryRequirementsInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_INFO_NV; + memoryRequirementsInfo.type = VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV; + + VkMemoryRequirements2 memReqBottomLevelAS; + memoryRequirementsInfo.accelerationStructure = bottomLevelAS.accelerationStructure; + vkGetAccelerationStructureMemoryRequirementsNV(device, &memoryRequirementsInfo, &memReqBottomLevelAS); + + VkMemoryRequirements2 memReqTopLevelAS; + memoryRequirementsInfo.accelerationStructure = topLevelAS.accelerationStructure; + vkGetAccelerationStructureMemoryRequirementsNV(device, &memoryRequirementsInfo, &memReqTopLevelAS); + + const VkDeviceSize scratchBufferSize = std::max(memReqBottomLevelAS.memoryRequirements.size, memReqTopLevelAS.memoryRequirements.size); + + vks::Buffer scratchBuffer; + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_RAY_TRACING_BIT_NV, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + &scratchBuffer, + scratchBufferSize)); + + VkCommandBuffer cmdBuffer = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + + /* + Build bottom level acceleration structure + */ + VkAccelerationStructureInfoNV buildInfo{}; + buildInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_INFO_NV; + buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV; + buildInfo.geometryCount = 1; + buildInfo.pGeometries = &geometry; + + vkCmdBuildAccelerationStructureNV( + cmdBuffer, + &buildInfo, + VK_NULL_HANDLE, + 0, + VK_FALSE, + bottomLevelAS.accelerationStructure, + VK_NULL_HANDLE, + scratchBuffer.buffer, + 0); + + VkMemoryBarrier memoryBarrier = vks::initializers::memoryBarrier(); + memoryBarrier.srcAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_NV | VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_NV; + memoryBarrier.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_NV | VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_NV; + vkCmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_NV, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_NV, 0, 1, &memoryBarrier, 0, 0, 0, 0); + + /* + Build top-level acceleration structure + */ + buildInfo.pGeometries = 0; + buildInfo.geometryCount = 0; + buildInfo.instanceCount = 1; + + vkCmdBuildAccelerationStructureNV( + cmdBuffer, + &buildInfo, + instanceBuffer.buffer, + 0, + VK_FALSE, + topLevelAS.accelerationStructure, + VK_NULL_HANDLE, + scratchBuffer.buffer, + 0); + + vkCmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_NV, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_NV, 0, 1, &memoryBarrier, 0, 0, 0, 0); + + vulkanDevice->flushCommandBuffer(cmdBuffer, queue); + + scratchBuffer.destroy(); + } + + VkDeviceSize copyShaderIdentifier(uint8_t* data, const uint8_t* shaderHandleStorage, uint32_t groupIndex) { + const uint32_t shaderGroupHandleSize = rayTracingProperties.shaderGroupHandleSize; + memcpy(data, shaderHandleStorage + groupIndex * shaderGroupHandleSize, shaderGroupHandleSize); + data += shaderGroupHandleSize; + return shaderGroupHandleSize; + } + + /* + Create the Shader Binding Table that binds the programs and top-level acceleration structure + */ + void createShaderBindingTable() { + // Create buffer for the shader binding table + const uint32_t sbtSize = rayTracingProperties.shaderGroupHandleSize * 3; + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_RAY_TRACING_BIT_NV, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + &shaderBindingTable, + sbtSize)); + shaderBindingTable.map(); + + auto shaderHandleStorage = new uint8_t[sbtSize]; + // Get shader identifiers + VK_CHECK_RESULT(vkGetRayTracingShaderGroupHandlesNV(device, pipeline, 0, 3, sbtSize, shaderHandleStorage)); + auto* data = static_cast(shaderBindingTable.mapped); + // Copy the shader identifiers to the shader binding table + VkDeviceSize offset = 0; + data += copyShaderIdentifier(data, shaderHandleStorage, SHADER_INDEX_RAYGEN); + data += copyShaderIdentifier(data, shaderHandleStorage, SHADER_INDEX_MISS); + data += copyShaderIdentifier(data, shaderHandleStorage, SHADER_INDEX_CLOSEST_HIT); + shaderBindingTable.unmap(); + } + + /* + Create the descriptor sets used for the ray tracing dispatch + */ + void createDescriptorSets() + { + std::vector poolSizes = { + { VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV, 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)); + + VkWriteDescriptorSetAccelerationStructureNV descriptorAccelerationStructureInfo{}; + descriptorAccelerationStructureInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_NV; + descriptorAccelerationStructureInfo.accelerationStructureCount = 1; + descriptorAccelerationStructureInfo.pAccelerationStructures = &topLevelAS.accelerationStructure; + + 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_NV; + + 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_NV; + accelerationStructureLayoutBinding.descriptorCount = 1; + accelerationStructureLayoutBinding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_NV; + + VkDescriptorSetLayoutBinding resultImageLayoutBinding{}; + resultImageLayoutBinding.binding = 1; + resultImageLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + resultImageLayoutBinding.descriptorCount = 1; + resultImageLayoutBinding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_NV; + + VkDescriptorSetLayoutBinding uniformBufferBinding{}; + uniformBufferBinding.binding = 2; + uniformBufferBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uniformBufferBinding.descriptorCount = 1; + uniformBufferBinding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_NV; + + std::vector bindings({ + accelerationStructureLayoutBinding, + resultImageLayoutBinding, + uniformBufferBinding + }); + + VkDescriptorSetLayoutCreateInfo layoutInfo{}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout)); + + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{}; + pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutCreateInfo.setLayoutCount = 1; + pipelineLayoutCreateInfo.pSetLayouts = &descriptorSetLayout; + + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); + + std::array shaderStages; + shaderStages[SHADER_INDEX_RAYGEN] = loadShader(getAssetPath() + "shaders/nv_ray_tracing_basic/raygen.rgen.spv", VK_SHADER_STAGE_RAYGEN_BIT_NV); + shaderStages[SHADER_INDEX_MISS] = loadShader(getAssetPath() + "shaders/nv_ray_tracing_basic/miss.rmiss.spv", VK_SHADER_STAGE_MISS_BIT_NV); + shaderStages[SHADER_INDEX_CLOSEST_HIT] = loadShader(getAssetPath() + "shaders/nv_ray_tracing_basic/closesthit.rchit.spv", VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV); + + /* + Setup ray tracing shader groups + */ + std::array groups{}; + for (auto& group : groups) { + // Init all groups with some default values + group.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_NV; + group.generalShader = VK_SHADER_UNUSED_NV; + group.closestHitShader = VK_SHADER_UNUSED_NV; + group.anyHitShader = VK_SHADER_UNUSED_NV; + group.intersectionShader = VK_SHADER_UNUSED_NV; + } + + // Links shaders and types to ray tracing shader groups + groups[SHADER_INDEX_RAYGEN].type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_NV; + groups[SHADER_INDEX_RAYGEN].generalShader = SHADER_INDEX_RAYGEN; + groups[SHADER_INDEX_MISS].type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_NV; + groups[SHADER_INDEX_MISS].generalShader = SHADER_INDEX_MISS; + groups[SHADER_INDEX_CLOSEST_HIT].type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_NV; + groups[SHADER_INDEX_CLOSEST_HIT].generalShader = VK_SHADER_UNUSED_NV; + groups[SHADER_INDEX_CLOSEST_HIT].closestHitShader = SHADER_INDEX_CLOSEST_HIT; + + VkRayTracingPipelineCreateInfoNV rayPipelineInfo{}; + rayPipelineInfo.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_NV; + rayPipelineInfo.stageCount = static_cast(shaderStages.size()); + rayPipelineInfo.pStages = shaderStages.data(); + rayPipelineInfo.groupCount = static_cast(groups.size()); + rayPipelineInfo.pGroups = groups.data(); + rayPipelineInfo.maxRecursionDepth = 1; + rayPipelineInfo.layout = pipelineLayout; + VK_CHECK_RESULT(vkCreateRayTracingPipelinesNV(device, nullptr, 1, &rayPipelineInfo, 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(); + } + + /* + Command buffer generation + */ + void buildCommandBuffers() + { + 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) + { + VkClearValue clearValues[2]; + clearValues[0].color = defaultClearColor; + clearValues[1].depthStencil = { 1.0f, 0 }; + + VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); + renderPassBeginInfo.renderPass = renderPass; + renderPassBeginInfo.framebuffer = frameBuffers[i]; + renderPassBeginInfo.renderArea.extent.width = width; + renderPassBeginInfo.renderArea.extent.height = height; + renderPassBeginInfo.clearValueCount = 2; + renderPassBeginInfo.pClearValues = clearValues; + + VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); + + /* + Dispatch the ray tracing commands + */ + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_RAY_TRACING_NV, pipeline); + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_RAY_TRACING_NV, pipelineLayout, 0, 1, &descriptorSet, 0, 0); + + // Calculate shader binding offsets, which is pretty straight forward in our example + VkDeviceSize bindingOffsetRayGenShader = 0; + VkDeviceSize bindingOffsetMissShader = rayTracingProperties.shaderGroupHandleSize; + VkDeviceSize bindingOffsetHitShader = rayTracingProperties.shaderGroupHandleSize * 2; + VkDeviceSize bindingStride = rayTracingProperties.shaderGroupHandleSize; + + vkCmdTraceRaysNV(drawCmdBuffers[i], + shaderBindingTable.buffer, bindingOffsetRayGenShader, + shaderBindingTable.buffer, bindingOffsetMissShader, bindingStride, + shaderBindingTable.buffer, bindingOffsetHitShader, bindingStride, + VK_NULL_HANDLE, 0, 0, + width, height, 1); + + /* + Copy raytracing output to swap chain image + */ + + // Prepare current swapchain 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); + + //@todo: Default render pass setup willl overwrite contents + //vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + //drawUI(drawCmdBuffers[i]); + //vkCmdEndRenderPass(drawCmdBuffers[i]); + + 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 prepare() + { + VulkanExampleBase::prepare(); + + // Query the ray tracing properties of the current implementation, we will need them later on + rayTracingProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PROPERTIES_NV; + VkPhysicalDeviceProperties2 deviceProps2{}; + deviceProps2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + deviceProps2.pNext = &rayTracingProperties; + vkGetPhysicalDeviceProperties2(physicalDevice, &deviceProps2); + + // Get VK_NV_ray_tracing related function pointers + vkCreateAccelerationStructureNV = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCreateAccelerationStructureNV")); + vkBindAccelerationStructureMemoryNV = reinterpret_cast(vkGetDeviceProcAddr(device, "vkBindAccelerationStructureMemoryNV")); + vkGetAccelerationStructureHandleNV = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetAccelerationStructureHandleNV")); + vkGetAccelerationStructureMemoryRequirementsNV = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetAccelerationStructureMemoryRequirementsNV")); + vkCmdBuildAccelerationStructureNV = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdBuildAccelerationStructureNV")); + vkCreateRayTracingPipelinesNV = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCreateRayTracingPipelinesNV")); + vkGetRayTracingShaderGroupHandlesNV = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetRayTracingShaderGroupHandlesNV")); + vkCmdTraceRaysNV = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdTraceRaysNV")); + + createScene(); + 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()