Added basic VK_NV_ray_tracing example (wip)
This commit is contained in:
parent
aa915d2c24
commit
b8b3fac9b4
8 changed files with 784 additions and 0 deletions
731
examples/nv_ray_tracing_basic/nv_ray_tracing_basic.cpp
Normal file
731
examples/nv_ray_tracing_basic/nv_ray_tracing_basic.cpp
Normal file
|
|
@ -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 <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 <gli/gli.hpp>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#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<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 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<uint8_t*>(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<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));
|
||||
|
||||
std::array<VkPipelineShaderStageCreateInfo, 3> 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<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[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<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, 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<PFN_vkCreateAccelerationStructureNV>(vkGetDeviceProcAddr(device, "vkCreateAccelerationStructureNV"));
|
||||
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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue