procedural-3d-engine/examples/raytracingreflections/raytracingreflections.cpp
SRSaunders bdfd4709ff
macOS/iOS fixes plus other generic fixes for clang and validation warnings (#1117)
* Fix clang Objective-C++ flags for macOS command line builds

* Fix getAssetPath() and getShaderBasePath() for macOS command line builds

* Protect debugUtilsMessageCallback() from failing when pMessageIdName is NULL

* Fix a few clang function override and mismatched type warnings

* Fix validation layer warnings on exit for computeraytracing example

* Fix regression in text visibility toggle for textOverlay example

* Support VK_USE_PLATFORM_METAL_EXT vs. deprecated VK_USE_PLATFORM_MACOS_MVK / DVK_USE_PLATFORM_IOS_MVK

* Check dynamic state features before enabling capabilities in dynamicstate example

* Fix vkCmdDraw() vertexCount argument (PARTICLE_COUNT) in particlesystem example

* Update examples list and restore benchmarking script (to top level)

* Fix validation warning in descriptorindexing example

* Fix device max recursion depth validation warnings in ray tracing examples

* Fix OpenMP build settings for texture3d example on all platforms

* Update and simplify build instructions for macOS

* Update CI script with correct library path for libomp on macOS x86_64

* Update CI scipt to install libomp prior to macOS builds

* Trying one more time to get the CI script working for macOS libomp

* Fix vertexCount argument using calculated size in particlesystem example

* Fix combined image descriptor offset calculation in descriptorbuffer example

* macOS: Support non-system level Vulkan SDK installs, with fallback to MoltenVK library
2024-05-04 13:53:08 +02:00

592 lines
27 KiB
C++

/*
* Vulkan Example - Hardware accelerated ray tracing example for doing reflections
*
* Renders a complex scene doing recursion inside the shaders for creating reflections
*
* Copyright (C) 2019-2023 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/
#include "VulkanRaytracingSample.h"
#include "VulkanglTFModel.h"
class VulkanExample : public VulkanRaytracingSample
{
public:
AccelerationStructure bottomLevelAS{};
AccelerationStructure topLevelAS{};
std::vector<VkRayTracingShaderGroupCreateInfoKHR> shaderGroups{};
struct ShaderBindingTables {
ShaderBindingTable raygen;
ShaderBindingTable miss;
ShaderBindingTable hit;
} shaderBindingTables;
struct UniformData {
glm::mat4 viewInverse;
glm::mat4 projInverse;
glm::vec4 lightPos;
int32_t vertexSize;
} uniformData;
vks::Buffer ubo;
VkPipeline pipeline;
VkPipelineLayout pipelineLayout;
VkDescriptorSet descriptorSet;
VkDescriptorSetLayout descriptorSetLayout;
vkglTF::Model scene;
// This sample is derived from an extended base class that saves most of the ray tracing setup boiler plate
VulkanExample() : VulkanRaytracingSample()
{
title = "Ray tracing reflections";
timerSpeed *= 0.5f;
camera.rotationSpeed *= 0.25f;
camera.type = Camera::CameraType::firstperson;
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.5f, -2.0f));
enableExtensions();
}
~VulkanExample()
{
vkDestroyPipeline(device, pipeline, nullptr);
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
deleteStorageImage();
deleteAccelerationStructure(bottomLevelAS);
deleteAccelerationStructure(topLevelAS);
shaderBindingTables.raygen.destroy();
shaderBindingTables.miss.destroy();
shaderBindingTables.hit.destroy();
ubo.destroy();
}
/*
Create the bottom level acceleration structure contains the scene's actual geometry (vertices, triangles)
*/
void createBottomLevelAccelerationStructure()
{
// Instead of a simple triangle, we'll be loading a more complex scene for this example
// The shaders are accessing the vertex and index buffers of the scene, so the proper usage flag has to be set on the vertex and index buffers for the scene
vkglTF::memoryPropertyFlags = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
const uint32_t glTFLoadingFlags = vkglTF::FileLoadingFlags::PreTransformVertices | vkglTF::FileLoadingFlags::PreMultiplyVertexColors | vkglTF::FileLoadingFlags::FlipY;
scene.loadFromFile(getAssetPath() + "models/reflection_scene.gltf", vulkanDevice, queue, glTFLoadingFlags);
VkDeviceOrHostAddressConstKHR vertexBufferDeviceAddress{};
VkDeviceOrHostAddressConstKHR indexBufferDeviceAddress{};
vertexBufferDeviceAddress.deviceAddress = getBufferDeviceAddress(scene.vertices.buffer);
indexBufferDeviceAddress.deviceAddress = getBufferDeviceAddress(scene.indices.buffer);
uint32_t numTriangles = static_cast<uint32_t>(scene.indices.count) / 3;
// Build
VkAccelerationStructureGeometryKHR accelerationStructureGeometry = vks::initializers::accelerationStructureGeometryKHR();
accelerationStructureGeometry.flags = VK_GEOMETRY_OPAQUE_BIT_KHR;
accelerationStructureGeometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;
accelerationStructureGeometry.geometry.triangles.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR;
accelerationStructureGeometry.geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT;
accelerationStructureGeometry.geometry.triangles.vertexData = vertexBufferDeviceAddress;
accelerationStructureGeometry.geometry.triangles.maxVertex = scene.vertices.count - 1;
accelerationStructureGeometry.geometry.triangles.vertexStride = sizeof(vkglTF::Vertex);
accelerationStructureGeometry.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32;
accelerationStructureGeometry.geometry.triangles.indexData = indexBufferDeviceAddress;
accelerationStructureGeometry.geometry.triangles.transformData.deviceAddress = 0;
accelerationStructureGeometry.geometry.triangles.transformData.hostAddress = nullptr;
// Get size info
VkAccelerationStructureBuildGeometryInfoKHR accelerationStructureBuildGeometryInfo = vks::initializers::accelerationStructureBuildGeometryInfoKHR();
accelerationStructureBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
accelerationStructureBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
accelerationStructureBuildGeometryInfo.geometryCount = 1;
accelerationStructureBuildGeometryInfo.pGeometries = &accelerationStructureGeometry;
VkAccelerationStructureBuildSizesInfoKHR accelerationStructureBuildSizesInfo = vks::initializers::accelerationStructureBuildSizesInfoKHR();
vkGetAccelerationStructureBuildSizesKHR(
device,
VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR,
&accelerationStructureBuildGeometryInfo,
&numTriangles,
&accelerationStructureBuildSizesInfo);
createAccelerationStructure(bottomLevelAS, VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR, accelerationStructureBuildSizesInfo);
// Create a small scratch buffer used during build of the bottom level acceleration structure
ScratchBuffer scratchBuffer = createScratchBuffer(accelerationStructureBuildSizesInfo.buildScratchSize);
VkAccelerationStructureBuildGeometryInfoKHR accelerationBuildGeometryInfo = vks::initializers::accelerationStructureBuildGeometryInfoKHR();
accelerationBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
accelerationBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
accelerationBuildGeometryInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR;
accelerationBuildGeometryInfo.dstAccelerationStructure = bottomLevelAS.handle;
accelerationBuildGeometryInfo.geometryCount = 1;
accelerationBuildGeometryInfo.pGeometries = &accelerationStructureGeometry;
accelerationBuildGeometryInfo.scratchData.deviceAddress = scratchBuffer.deviceAddress;
VkAccelerationStructureBuildRangeInfoKHR accelerationStructureBuildRangeInfo{};
accelerationStructureBuildRangeInfo.primitiveCount = numTriangles;
accelerationStructureBuildRangeInfo.primitiveOffset = 0;
accelerationStructureBuildRangeInfo.firstVertex = 0;
accelerationStructureBuildRangeInfo.transformOffset = 0;
std::vector<VkAccelerationStructureBuildRangeInfoKHR*> accelerationBuildStructureRangeInfos = { &accelerationStructureBuildRangeInfo };
// Build the acceleration structure on the device via a one-time command buffer submission
// Some implementations may support acceleration structure building on the host (VkPhysicalDeviceAccelerationStructureFeaturesKHR->accelerationStructureHostCommands), but we prefer device builds
VkCommandBuffer commandBuffer = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
vkCmdBuildAccelerationStructuresKHR(
commandBuffer,
1,
&accelerationBuildGeometryInfo,
accelerationBuildStructureRangeInfos.data());
vulkanDevice->flushCommandBuffer(commandBuffer, queue);
deleteScratchBuffer(scratchBuffer);
}
/*
The top level acceleration structure contains the scene's object instances
*/
void createTopLevelAccelerationStructure()
{
VkTransformMatrixKHR transformMatrix = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f };
VkAccelerationStructureInstanceKHR instance{};
instance.transform = transformMatrix;
instance.instanceCustomIndex = 0;
instance.mask = 0xFF;
instance.instanceShaderBindingTableRecordOffset = 0;
instance.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
instance.accelerationStructureReference = bottomLevelAS.deviceAddress;
// Buffer for instance data
vks::Buffer instancesBuffer;
VK_CHECK_RESULT(vulkanDevice->createBuffer(
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&instancesBuffer,
sizeof(VkAccelerationStructureInstanceKHR),
&instance));
VkDeviceOrHostAddressConstKHR instanceDataDeviceAddress{};
instanceDataDeviceAddress.deviceAddress = getBufferDeviceAddress(instancesBuffer.buffer);
VkAccelerationStructureGeometryKHR accelerationStructureGeometry = vks::initializers::accelerationStructureGeometryKHR();
accelerationStructureGeometry.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR;
accelerationStructureGeometry.flags = VK_GEOMETRY_OPAQUE_BIT_KHR;
accelerationStructureGeometry.geometry.instances.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR;
accelerationStructureGeometry.geometry.instances.arrayOfPointers = VK_FALSE;
accelerationStructureGeometry.geometry.instances.data = instanceDataDeviceAddress;
// Get size info
VkAccelerationStructureBuildGeometryInfoKHR accelerationStructureBuildGeometryInfo = vks::initializers::accelerationStructureBuildGeometryInfoKHR();
accelerationStructureBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR;
accelerationStructureBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
accelerationStructureBuildGeometryInfo.geometryCount = 1;
accelerationStructureBuildGeometryInfo.pGeometries = &accelerationStructureGeometry;
uint32_t primitive_count = 1;
VkAccelerationStructureBuildSizesInfoKHR accelerationStructureBuildSizesInfo = vks::initializers::accelerationStructureBuildSizesInfoKHR();
vkGetAccelerationStructureBuildSizesKHR(
device,
VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR,
&accelerationStructureBuildGeometryInfo,
&primitive_count,
&accelerationStructureBuildSizesInfo);
createAccelerationStructure(topLevelAS, VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR, accelerationStructureBuildSizesInfo);
// Create a small scratch buffer used during build of the top level acceleration structure
ScratchBuffer scratchBuffer = createScratchBuffer(accelerationStructureBuildSizesInfo.buildScratchSize);
VkAccelerationStructureBuildGeometryInfoKHR accelerationBuildGeometryInfo = vks::initializers::accelerationStructureBuildGeometryInfoKHR();
accelerationBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR;
accelerationBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
accelerationBuildGeometryInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR;
accelerationBuildGeometryInfo.dstAccelerationStructure = topLevelAS.handle;
accelerationBuildGeometryInfo.geometryCount = 1;
accelerationBuildGeometryInfo.pGeometries = &accelerationStructureGeometry;
accelerationBuildGeometryInfo.scratchData.deviceAddress = scratchBuffer.deviceAddress;
VkAccelerationStructureBuildRangeInfoKHR accelerationStructureBuildRangeInfo{};
accelerationStructureBuildRangeInfo.primitiveCount = 1;
accelerationStructureBuildRangeInfo.primitiveOffset = 0;
accelerationStructureBuildRangeInfo.firstVertex = 0;
accelerationStructureBuildRangeInfo.transformOffset = 0;
std::vector<VkAccelerationStructureBuildRangeInfoKHR*> accelerationBuildStructureRangeInfos = { &accelerationStructureBuildRangeInfo };
// Build the acceleration structure on the device via a one-time command buffer submission
// Some implementations may support acceleration structure building on the host (VkPhysicalDeviceAccelerationStructureFeaturesKHR->accelerationStructureHostCommands), but we prefer device builds
VkCommandBuffer commandBuffer = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
vkCmdBuildAccelerationStructuresKHR(
commandBuffer,
1,
&accelerationBuildGeometryInfo,
accelerationBuildStructureRangeInfos.data());
vulkanDevice->flushCommandBuffer(commandBuffer, queue);
deleteScratchBuffer(scratchBuffer);
instancesBuffer.destroy();
}
/*
Create the Shader Binding Tables that binds the programs and top-level acceleration structure
SBT Layout used in this sample:
/-----------\
| raygen |
|-----------|
| miss |
|-----------|
| hit |
\-----------/
*/
void createShaderBindingTables() {
const uint32_t handleSize = rayTracingPipelineProperties.shaderGroupHandleSize;
const uint32_t handleSizeAligned = vks::tools::alignedSize(rayTracingPipelineProperties.shaderGroupHandleSize, rayTracingPipelineProperties.shaderGroupHandleAlignment);
const uint32_t groupCount = static_cast<uint32_t>(shaderGroups.size());
const uint32_t sbtSize = groupCount * handleSizeAligned;
std::vector<uint8_t> shaderHandleStorage(sbtSize);
VK_CHECK_RESULT(vkGetRayTracingShaderGroupHandlesKHR(device, pipeline, 0, groupCount, sbtSize, shaderHandleStorage.data()));
createShaderBindingTable(shaderBindingTables.raygen, 1);
createShaderBindingTable(shaderBindingTables.miss, 1);
createShaderBindingTable(shaderBindingTables.hit, 1);
// Copy handles
memcpy(shaderBindingTables.raygen.mapped, shaderHandleStorage.data(), handleSize);
memcpy(shaderBindingTables.miss.mapped, shaderHandleStorage.data() + handleSizeAligned, handleSize);
memcpy(shaderBindingTables.hit.mapped, shaderHandleStorage.data() + handleSizeAligned * 2, handleSize);
}
/*
Create the descriptor sets used for the ray tracing dispatch
*/
void createDescriptorSets()
{
std::vector<VkDescriptorPoolSize> poolSizes = {
{ VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1 },
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2 }
};
VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 1);
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCreateInfo, nullptr, &descriptorPool));
VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocateInfo, &descriptorSet));
VkWriteDescriptorSetAccelerationStructureKHR descriptorAccelerationStructureInfo = vks::initializers::writeDescriptorSetAccelerationStructureKHR();
descriptorAccelerationStructureInfo.accelerationStructureCount = 1;
descriptorAccelerationStructureInfo.pAccelerationStructures = &topLevelAS.handle;
VkWriteDescriptorSet accelerationStructureWrite{};
accelerationStructureWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
// The specialized acceleration structure descriptor has to be chained
accelerationStructureWrite.pNext = &descriptorAccelerationStructureInfo;
accelerationStructureWrite.dstSet = descriptorSet;
accelerationStructureWrite.dstBinding = 0;
accelerationStructureWrite.descriptorCount = 1;
accelerationStructureWrite.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
VkDescriptorImageInfo storageImageDescriptor{ VK_NULL_HANDLE, storageImage.view, VK_IMAGE_LAYOUT_GENERAL };
VkDescriptorBufferInfo vertexBufferDescriptor{ scene.vertices.buffer, 0, VK_WHOLE_SIZE };
VkDescriptorBufferInfo indexBufferDescriptor{ scene.indices.buffer, 0, VK_WHOLE_SIZE };
std::vector<VkWriteDescriptorSet> writeDescriptorSets = {
// Binding 0: Top level acceleration structure
accelerationStructureWrite,
// Binding 1: Ray tracing result image
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, &storageImageDescriptor),
// Binding 2: Uniform data
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2, &ubo.descriptor),
// Binding 3: Scene vertex buffer
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3, &vertexBufferDescriptor),
// Binding 4: Scene index buffer
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 4, &indexBufferDescriptor),
};
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, VK_NULL_HANDLE);
}
/*
Create our ray tracing pipeline
*/
void createRayTracingPipeline()
{
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
// Binding 0: Acceleration structure
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, 0),
// Binding 1: Storage image
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_RAYGEN_BIT_KHR, 1),
// Binding 2: Uniform buffer
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR, 2),
// Binding 3: Vertex buffer
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, 3),
// Binding 4: Index buffer
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, 4),
};
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayout));
VkPipelineLayoutCreateInfo pPipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCI, nullptr, &pipelineLayout));
/*
Setup ray tracing shader groups
*/
std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
VkSpecializationMapEntry specializationMapEntry = vks::initializers::specializationMapEntry(0, 0, sizeof(uint32_t));
uint32_t maxRecursion = 4;
VkSpecializationInfo specializationInfo = vks::initializers::specializationInfo(1, &specializationMapEntry, sizeof(maxRecursion), &maxRecursion);
// Ray generation group
{
shaderStages.push_back(loadShader(getShadersPath() + "raytracingreflections/raygen.rgen.spv", VK_SHADER_STAGE_RAYGEN_BIT_KHR));
// Pass recursion depth for reflections to ray generation shader via specialization constant
shaderStages.back().pSpecializationInfo = &specializationInfo;
VkRayTracingShaderGroupCreateInfoKHR shaderGroup{};
shaderGroup.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR;
shaderGroup.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
shaderGroup.generalShader = static_cast<uint32_t>(shaderStages.size()) - 1;
shaderGroup.closestHitShader = VK_SHADER_UNUSED_KHR;
shaderGroup.anyHitShader = VK_SHADER_UNUSED_KHR;
shaderGroup.intersectionShader = VK_SHADER_UNUSED_KHR;
shaderGroups.push_back(shaderGroup);
}
// Miss group
{
shaderStages.push_back(loadShader(getShadersPath() + "raytracingreflections/miss.rmiss.spv", VK_SHADER_STAGE_MISS_BIT_KHR));
VkRayTracingShaderGroupCreateInfoKHR shaderGroup{};
shaderGroup.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR;
shaderGroup.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
shaderGroup.generalShader = static_cast<uint32_t>(shaderStages.size()) - 1;
shaderGroup.closestHitShader = VK_SHADER_UNUSED_KHR;
shaderGroup.anyHitShader = VK_SHADER_UNUSED_KHR;
shaderGroup.intersectionShader = VK_SHADER_UNUSED_KHR;
shaderGroups.push_back(shaderGroup);
}
// Closest hit group
{
shaderStages.push_back(loadShader(getShadersPath() + "raytracingreflections/closesthit.rchit.spv", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR));
VkRayTracingShaderGroupCreateInfoKHR shaderGroup{};
shaderGroup.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR;
shaderGroup.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR;
shaderGroup.generalShader = VK_SHADER_UNUSED_KHR;
shaderGroup.closestHitShader = static_cast<uint32_t>(shaderStages.size()) - 1;
shaderGroup.anyHitShader = VK_SHADER_UNUSED_KHR;
shaderGroup.intersectionShader = VK_SHADER_UNUSED_KHR;
shaderGroups.push_back(shaderGroup);
}
// Get max pipeline ray tracing recursion depth for physical device
VkPhysicalDeviceRayTracingPipelinePropertiesKHR physicalDeviceRayTracingPipelinePropertiesKHR {};
physicalDeviceRayTracingPipelinePropertiesKHR.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR;
VkPhysicalDeviceProperties2 physicalDeviceProperties2;
physicalDeviceProperties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
physicalDeviceProperties2.pNext = &physicalDeviceRayTracingPipelinePropertiesKHR;
vkGetPhysicalDeviceProperties2(physicalDevice, &physicalDeviceProperties2);
VkRayTracingPipelineCreateInfoKHR rayTracingPipelineCI = vks::initializers::rayTracingPipelineCreateInfoKHR();
rayTracingPipelineCI.stageCount = static_cast<uint32_t>(shaderStages.size());
rayTracingPipelineCI.pStages = shaderStages.data();
rayTracingPipelineCI.groupCount = static_cast<uint32_t>(shaderGroups.size());
rayTracingPipelineCI.pGroups = shaderGroups.data();
rayTracingPipelineCI.maxPipelineRayRecursionDepth = std::min(uint32_t(4), physicalDeviceRayTracingPipelinePropertiesKHR.maxRayRecursionDepth);
rayTracingPipelineCI.layout = pipelineLayout;
VK_CHECK_RESULT(vkCreateRayTracingPipelinesKHR(device, VK_NULL_HANDLE, VK_NULL_HANDLE, 1, &rayTracingPipelineCI, nullptr, &pipeline));
}
/*
Create the uniform buffer used to pass matrices to the ray tracing ray generation shader
*/
void createUniformBuffer()
{
VK_CHECK_RESULT(vulkanDevice->createBuffer(
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&ubo,
sizeof(uniformData),
&uniformData));
VK_CHECK_RESULT(ubo.map());
updateUniformBuffers();
}
/*
If the window has been resized, we need to recreate the storage image and it's descriptor
*/
void handleResize()
{
// Recreate image
createStorageImage(swapChain.colorFormat, { width, height, 1 });
// Update descriptor
VkDescriptorImageInfo storageImageDescriptor{ VK_NULL_HANDLE, storageImage.view, VK_IMAGE_LAYOUT_GENERAL };
VkWriteDescriptorSet resultImageWrite = vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, &storageImageDescriptor);
vkUpdateDescriptorSets(device, 1, &resultImageWrite, 0, VK_NULL_HANDLE);
resized = false;
}
/*
Command buffer generation
*/
void buildCommandBuffers()
{
if (resized)
{
handleResize();
}
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
VkImageSubresourceRange subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
for (int32_t i = 0; i < drawCmdBuffers.size(); ++i)
{
VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo));
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipelineLayout, 0, 1, &descriptorSet, 0, 0);
/*
Dispatch the ray tracing commands
*/
VkStridedDeviceAddressRegionKHR emptySbtEntry = {};
vkCmdTraceRaysKHR(
drawCmdBuffers[i],
&shaderBindingTables.raygen.stridedDeviceAddressRegion,
&shaderBindingTables.miss.stridedDeviceAddressRegion,
&shaderBindingTables.hit.stridedDeviceAddressRegion,
&emptySbtEntry,
width,
height,
1);
/*
Copy ray tracing output to swap chain image
*/
// Prepare current swap chain image as transfer destination
vks::tools::setImageLayout(
drawCmdBuffers[i],
swapChain.images[i],
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
subresourceRange);
// Prepare ray tracing output image as transfer source
vks::tools::setImageLayout(
drawCmdBuffers[i],
storageImage.image,
VK_IMAGE_LAYOUT_GENERAL,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
subresourceRange);
VkImageCopy copyRegion{};
copyRegion.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
copyRegion.srcOffset = { 0, 0, 0 };
copyRegion.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
copyRegion.dstOffset = { 0, 0, 0 };
copyRegion.extent = { width, height, 1 };
vkCmdCopyImage(drawCmdBuffers[i], storageImage.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, swapChain.images[i], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &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);
drawUI(drawCmdBuffers[i], frameBuffers[i]);
VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i]));
}
}
void updateUniformBuffers()
{
uniformData.projInverse = glm::inverse(camera.matrices.perspective);
uniformData.viewInverse = glm::inverse(camera.matrices.view);
uniformData.lightPos = glm::vec4(cos(glm::radians(timer * 360.0f)) * 40.0f, -20.0f + sin(glm::radians(timer * 360.0f)) * 20.0f, 25.0f + sin(glm::radians(timer * 360.0f)) * 5.0f, 0.0f);
// Pass the vertex size to the shader for unpacking vertices
uniformData.vertexSize = sizeof(vkglTF::Vertex);
memcpy(ubo.mapped, &uniformData, sizeof(uniformData));
}
void getEnabledFeatures()
{
// Enable features required for ray tracing using feature chaining via pNext
enabledBufferDeviceAddresFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES;
enabledBufferDeviceAddresFeatures.bufferDeviceAddress = VK_TRUE;
enabledRayTracingPipelineFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR;
enabledRayTracingPipelineFeatures.rayTracingPipeline = VK_TRUE;
enabledRayTracingPipelineFeatures.pNext = &enabledBufferDeviceAddresFeatures;
enabledAccelerationStructureFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR;
enabledAccelerationStructureFeatures.accelerationStructure = VK_TRUE;
enabledAccelerationStructureFeatures.pNext = &enabledRayTracingPipelineFeatures;
deviceCreatepNextChain = &enabledAccelerationStructureFeatures;
}
void prepare()
{
VulkanRaytracingSample::prepare();
// Create the acceleration structures used to render the ray traced scene
createBottomLevelAccelerationStructure();
createTopLevelAccelerationStructure();
createStorageImage(swapChain.colorFormat, { width, height, 1 });
createUniformBuffer();
createRayTracingPipeline();
createShaderBindingTables();
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 (!paused || camera.updated)
updateUniformBuffers();
}
};
VULKAN_EXAMPLE_MAIN()