Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
1 contributor

Users who have contributed to this file

732 lines (626 sloc) 30.3 KB
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <vector>
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <vulkan/vulkan.h>
#include "vulkanexamplebase.h"
#include "VulkanDevice.hpp"
#include "VulkanBuffer.hpp"
// Ray tracing acceleration structure
struct AccelerationStructure {
VkDeviceMemory memory;
VkAccelerationStructureNV accelerationStructure;
uint64_t handle;
};
// Ray tracing geometry instance
struct GeometryInstance {
glm::mat3x4 transform;
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 INDEX_RAYGEN 0
#define INDEX_MISS 1
#define INDEX_CLOSEST_HIT 2
class VulkanExample : public VulkanExampleBase
{
public:
PFN_vkCreateAccelerationStructureNV vkCreateAccelerationStructureNV;
PFN_vkDestroyAccelerationStructureNV vkDestroyAccelerationStructureNV;
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()
{
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()
{
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);
vkFreeMemory(device, topLevelAS.memory, nullptr);
vkDestroyAccelerationStructureNV(device, bottomLevelAS.accelerationStructure, nullptr);
vkDestroyAccelerationStructureNV(device, topLevelAS.accelerationStructure, nullptr);
vertexBuffer.destroy();
indexBuffer.destroy();
shaderBindingTable.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<Vertex> 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<uint32_t> indices = { 0, 1, 2 };
indexCount = static_cast<uint32_t>(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<uint32_t>(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 3x4 transform matrix for the ray traced triangle
vks::Buffer instanceBuffer;
glm::mat3x4 transform = {
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 geometryInstance{};
geometryInstance.transform = transform;
geometryInstance.instanceId = 0;
geometryInstance.mask = 0xff;
geometryInstance.instanceOffset = 0;
geometryInstance.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_CULL_DISABLE_BIT_NV;
geometryInstance.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(GeometryInstance),
&geometryInstance));
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();
instanceBuffer.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<uint8_t*>(shaderBindingTable.mapped);
// Copy the shader identifiers to the shader binding table
VkDeviceSize offset = 0;
data += copyShaderIdentifier(data, shaderHandleStorage, INDEX_RAYGEN);
data += copyShaderIdentifier(data, shaderHandleStorage, INDEX_MISS);
data += copyShaderIdentifier(data, shaderHandleStorage, INDEX_CLOSEST_HIT);
shaderBindingTable.unmap();
}
/*
Create the descriptor sets used for the ray tracing dispatch
*/
void createDescriptorSets()
{
std::vector<VkDescriptorPoolSize> 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<VkWriteDescriptorSet> writeDescriptorSets = {
accelerationStructureWrite,
resultImageWrite,
uniformBufferWrite
};
vkUpdateDescriptorSets(device, static_cast<uint32_t>(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<VkDescriptorSetLayoutBinding> bindings({
accelerationStructureLayoutBinding,
resultImageLayoutBinding,
uniformBufferBinding
});
VkDescriptorSetLayoutCreateInfo layoutInfo{};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.bindingCount = static_cast<uint32_t>(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));
const uint32_t shaderIndexRaygen = 0;
const uint32_t shaderIndexMiss = 1;
const uint32_t shaderIndexClosestHit = 2;
std::array<VkPipelineShaderStageCreateInfo, 3> shaderStages;
shaderStages[shaderIndexRaygen] = loadShader(getAssetPath() + "shaders/nv_ray_tracing_basic/raygen.rgen.spv", VK_SHADER_STAGE_RAYGEN_BIT_NV);
shaderStages[shaderIndexMiss] = loadShader(getAssetPath() + "shaders/nv_ray_tracing_basic/miss.rmiss.spv", VK_SHADER_STAGE_MISS_BIT_NV);
shaderStages[shaderIndexClosestHit] = loadShader(getAssetPath() + "shaders/nv_ray_tracing_basic/closesthit.rchit.spv", VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV);
/*
Setup ray tracing shader groups
*/
std::array<VkRayTracingShaderGroupCreateInfoNV, 3> 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[INDEX_RAYGEN].type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_NV;
groups[INDEX_RAYGEN].generalShader = shaderIndexRaygen;
groups[INDEX_MISS].type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_NV;
groups[INDEX_MISS].generalShader = shaderIndexMiss;
groups[INDEX_CLOSEST_HIT].type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_NV;
groups[INDEX_CLOSEST_HIT].generalShader = VK_SHADER_UNUSED_NV;
groups[INDEX_CLOSEST_HIT].closestHitShader = shaderIndexClosestHit;
VkRayTracingPipelineCreateInfoNV rayPipelineInfo{};
rayPipelineInfo.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_NV;
rayPipelineInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
rayPipelineInfo.pStages = shaderStages.data();
rayPipelineInfo.groupCount = static_cast<uint32_t>(groups.size());
rayPipelineInfo.pGroups = groups.data();
rayPipelineInfo.maxRecursionDepth = 1;
rayPipelineInfo.layout = pipelineLayout;
VK_CHECK_RESULT(vkCreateRayTracingPipelinesNV(device, VK_NULL_HANDLE, 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)
{
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 = rayTracingProperties.shaderGroupHandleSize * INDEX_RAYGEN;
VkDeviceSize bindingOffsetMissShader = rayTracingProperties.shaderGroupHandleSize * INDEX_MISS;
VkDeviceSize bindingOffsetHitShader = rayTracingProperties.shaderGroupHandleSize * INDEX_CLOSEST_HIT;
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, &copyRegion);
// 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<PFN_vkCreateAccelerationStructureNV>(vkGetDeviceProcAddr(device, "vkCreateAccelerationStructureNV"));
vkDestroyAccelerationStructureNV = reinterpret_cast<PFN_vkDestroyAccelerationStructureNV>(vkGetDeviceProcAddr(device, "vkDestroyAccelerationStructureNV"));
vkBindAccelerationStructureMemoryNV = reinterpret_cast<PFN_vkBindAccelerationStructureMemoryNV>(vkGetDeviceProcAddr(device, "vkBindAccelerationStructureMemoryNV"));
vkGetAccelerationStructureHandleNV = reinterpret_cast<PFN_vkGetAccelerationStructureHandleNV>(vkGetDeviceProcAddr(device, "vkGetAccelerationStructureHandleNV"));
vkGetAccelerationStructureMemoryRequirementsNV = reinterpret_cast<PFN_vkGetAccelerationStructureMemoryRequirementsNV>(vkGetDeviceProcAddr(device, "vkGetAccelerationStructureMemoryRequirementsNV"));
vkCmdBuildAccelerationStructureNV = reinterpret_cast<PFN_vkCmdBuildAccelerationStructureNV>(vkGetDeviceProcAddr(device, "vkCmdBuildAccelerationStructureNV"));
vkCreateRayTracingPipelinesNV = reinterpret_cast<PFN_vkCreateRayTracingPipelinesNV>(vkGetDeviceProcAddr(device, "vkCreateRayTracingPipelinesNV"));
vkGetRayTracingShaderGroupHandlesNV = reinterpret_cast<PFN_vkGetRayTracingShaderGroupHandlesNV>(vkGetDeviceProcAddr(device, "vkGetRayTracingShaderGroupHandlesNV"));
vkCmdTraceRaysNV = reinterpret_cast<PFN_vkCmdTraceRaysNV>(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()
You can’t perform that action at this time.