Using final KHR ray tracing extension: VK_KHR_acceleration_structure, VK_KHR_ray_tracing_pipeline and VK_KHR_ray_query
This commit is contained in:
parent
7179569ec3
commit
b26ff92473
80 changed files with 2446 additions and 2351 deletions
Binary file not shown.
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 37 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 497 KiB After Width: | Height: | Size: 336 KiB |
|
|
@ -9,6 +9,14 @@ Besides the current repository, you will also need to clone or download the foll
|
|||
* [shared_sources](https://github.com/nvpro-samples/shared_sources): The primary framework that all samples depend on.
|
||||
* [shared_external](https://github.com/nvpro-samples/shared_external): Third party libraries that are provided pre-compiled, mostly for Windows x64 / MSVC.
|
||||
|
||||
Cloning all repositories
|
||||
|
||||
~~~~~
|
||||
git clone https://github.com/nvpro-samples/shared_sources.git
|
||||
git clone https://github.com/nvpro-samples/shared_external.git
|
||||
git clone https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR.git
|
||||
~~~~~
|
||||
|
||||
The directory structure should be looking like this:
|
||||
|
||||
~~~~
|
||||
|
|
@ -44,7 +52,7 @@ Vulkan driver.
|
|||
The CMakefile will use other makefiles from `shared_sources` and look for Vulkan environment variables for the installation of the SDK. Therefore, it is important to have all the above installed before running Cmake in the
|
||||
`vk_raytracing_tutorial_KHR` directory.
|
||||
|
||||
:warning: **Note:** If you are using your own Vulkan header files, it is possible to overide the default search path.
|
||||
**Note:** If you are using your own Vulkan header files, it is possible to overide the default search path.
|
||||
Modify `VULKAN > VULKAN_HEADERS_OVERRIDE_INCLUDE_DIR` to the path to beta vulkan headers.
|
||||
|
||||
## Starting From Extra Tutorial
|
||||
|
|
|
|||
|
|
@ -218,11 +218,11 @@ The size of each group can be described as follows:
|
|||
|
||||
~~~~ C++
|
||||
// Sizes
|
||||
uint32_t rayGenSize = baseAlignment;
|
||||
uint32_t missSize = baseAlignment;
|
||||
uint32_t rayGenSize = groupSizeAligned;
|
||||
uint32_t missSize = groupSizeAligned;
|
||||
uint32_t hitSize =
|
||||
ROUND_UP(groupHandleSize + static_cast<int>(sizeof(HitRecordBuffer)), baseAlignment);
|
||||
uint32_t newSbtSize = rayGenSize + 2 * missSize + 2 * hitSize;
|
||||
nvh::align_up(groupHandleSize + static_cast<int>(sizeof(HitRecordBuffer)), groupSizeAligned);
|
||||
uint32_t newSbtSize = rayGenSize + 2 * missSize + 3 * hitSize;
|
||||
~~~~
|
||||
|
||||
Then write the new SBT like this, where only Hit 1 has extra data.
|
||||
|
|
@ -258,21 +258,23 @@ Then change the call to `m_alloc.createBuffer` to create the SBT buffer from `sb
|
|||
m_rtSBTBuffer = m_alloc.createBuffer(cmdBuf, sbtBuffer, vk::BufferUsageFlagBits::eRayTracingKHR);
|
||||
~~~~
|
||||
|
||||
Note: we are using this `define` for rounding up to the correct alignment
|
||||
~~~~ C++
|
||||
#ifndef ROUND_UP
|
||||
#define ROUND_UP(v, powerOf2Alignment) (((v) + (powerOf2Alignment)-1) & ~((powerOf2Alignment)-1))
|
||||
#endif
|
||||
~~~~
|
||||
|
||||
|
||||
## `raytrace`
|
||||
|
||||
Finally, since the size of the hit group is now larger than just the handle, we need to set the new value of the hit group stride in `HelloVulkan::raytrace`.
|
||||
Finally, since the size of the hit group is now larger than just the handle,
|
||||
we need to set the new value of the hit group stride in `HelloVulkan::raytrace`.
|
||||
|
||||
~~~~ C++
|
||||
vk::DeviceSize hitGroupStride =
|
||||
ROUND_UP(m_rtProperties.shaderGroupHandleSize + sizeof(HitRecordBuffer), progOffset);
|
||||
vk::DeviceSize hitGroupSize =
|
||||
nvh::align_up(m_rtProperties.shaderGroupHandleSize + sizeof(HitRecordBuffer),
|
||||
m_rtProperties.shaderGroupBaseAlignment);
|
||||
|
||||
|
||||
std::array<Stride, 4> strideAddresses{
|
||||
Stride{sbtAddress + 0u * groupSize, groupStride, groupSize * 1}, // raygen
|
||||
Stride{sbtAddress + 1u * groupSize, groupStride, groupSize * 2}, // miss
|
||||
Stride{sbtAddress + 3u * groupSize, hitGroupSize, hitGroupSize * 3}, // hit
|
||||
Stride{0u, 0u, 0u}}; // callable
|
||||
~~~~
|
||||
|
||||
!!! Note:
|
||||
|
|
|
|||
|
|
@ -36,78 +36,90 @@ verbosity and its potential for errors.
|
|||
|
||||
# Environment Setup
|
||||
|
||||
**The preferred way** to download the project (including NVVK) is to use the
|
||||
nvpro-samples `build_all` script.
|
||||
|
||||
In a command line, clone the `nvpro-samples/build_all` repository from
|
||||
https://github.com/nvpro-samples/build_all:
|
||||
|
||||
~~~~~
|
||||
git clone https://github.com/nvpro-samples/build_all.git
|
||||
~~~~~
|
||||
|
||||
Then open the `build_all` folder and run either `clone_all.bat` (Windows) or
|
||||
`clone_all.sh` (Linux).
|
||||
|
||||
**If you want to clone as few repositories as possible**, open a command line,
|
||||
and run the following commands to clone the repositories you need:
|
||||
~~~~~
|
||||
git clone https://github.com/nvpro-samples/shared_sources.git
|
||||
git clone https://github.com/nvpro-samples/shared_external.git
|
||||
git clone https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR.git
|
||||
~~~~~
|
||||
|
||||
## Generating the Solution
|
||||
|
||||
One typical way to store the build system is to create a `build` directory below the
|
||||
main project. You can use CMake-GUI or do the following steps.
|
||||
|
||||
~~~~~
|
||||
cd vk_raytracing_tutorial_KHR
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
~~~~~
|
||||
|
||||
## Beta Installation
|
||||
|
||||
The SDK 1.2.135 and up which can be found under https://vulkan.lunarg.com/sdk/home will work with this project.
|
||||
The SDK 1.2.161 and up which can be found under https://vulkan.lunarg.com/sdk/home will work with this project.
|
||||
|
||||
Nevertheless, if you are in the Beta period, it is suggested to install and compile all of the following and replace
|
||||
with the current environment.
|
||||
|
||||
* Latest driver: https://developer.nvidia.com/vulkan-driver
|
||||
* Latest Vulkan headers: https://github.com/KhronosGroup/Vulkan-Headers
|
||||
* Latest glslangValidator: https://github.com/KhronosGroup/glslang
|
||||
|
||||
## Structure
|
||||
|
||||
This tutorial is a modification of [`ray_tracing__before`](https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/tree/master/ray_tracing__before), which loads are render OBJ scenes with Vulkan rasterizer.
|
||||
All following instructions are based on the modification of this project.
|
||||
The directory `ray_tracing__simple` is the end result of this tutorial.
|
||||
|
||||
Besides the current repository, you will also need to clone or download the following repositories:
|
||||
|
||||
* [shared_sources](https://github.com/nvpro-samples/shared_sources): The primary framework that all samples depend on.
|
||||
* [shared_external](https://github.com/nvpro-samples/shared_external): Third party libraries that are provided pre-compiled, mostly for Windows x64 / MSVC.
|
||||
|
||||
The directory structure should be looking like:
|
||||
|
||||
**********************************************************
|
||||
* \
|
||||
* |
|
||||
* +-- 📂 shared_external
|
||||
* |
|
||||
* +-- 📂 shared_sources
|
||||
* |
|
||||
* +-- 📂 vk_raytracing_tutorial_KHR
|
||||
* | |
|
||||
* | +-- 📂 ray_tracing__before
|
||||
* | |
|
||||
* | ⋮
|
||||
* |
|
||||
* ⋮
|
||||
**********************************************************
|
||||
|
||||
|
||||
!!! Warning
|
||||
**Run CMake** in vk_raytracing_tutorial_KHR.
|
||||
|
||||
!!! Warning Beta
|
||||
Modify `VULKAN > VULKAN_HEADERS_OVERRIDE_INCLUDE_DIR` to the path to beta vulkan headers.
|
||||
* Vulkan headers: https://github.com/KhronosGroup/Vulkan-Headers
|
||||
* Validator: https://github.com/KhronosGroup/Vulkan-ValidationLayers
|
||||
* Vulkan-Hpp: https://github.com/KhronosGroup/Vulkan-Hpp
|
||||
|
||||
!!! Tip Visual Assist
|
||||
To get auto-completion, edit vulkan.hpp and change two places from:<br>
|
||||
`namespace VULKAN_HPP_NAMESPACE` to `namespace vk`
|
||||
|
||||
The starting project is a simple framework allowing us to load OBJ files and rasterize them
|
||||
# Compiling & Running
|
||||
|
||||
Open the solution located in the build directory, then compile and run `vk_ray_tracing__before_KHR`.
|
||||
|
||||
This will be the starting point of the tutorial. This project is a simple framework allowing us to load OBJ files and rasterize them
|
||||
using Vulkan.
|
||||
|
||||

|
||||
|
||||
|
||||
The following steps in the tutorial will be modifying this project
|
||||
`vk_ray_tracing__before_KHR` and will add support for ray tracing. The
|
||||
end result of the tutorial is the project `vk_ray_tracing__simple_KHR`.
|
||||
It is possible to look in that project if something went wrong.
|
||||
|
||||
The project `vk_ray_tracing__simple_KHR` will be the starting point for the
|
||||
extra tutorials.
|
||||
|
||||
|
||||
# Ray Tracing Setup
|
||||
|
||||
Go to the `main` function of the `main.cpp` file, and find where we request Vulkan extensions with
|
||||
`nvvk::ContextCreateInfo`.
|
||||
To request ray tracing capabilities, we need to explicitly
|
||||
add the
|
||||
[VK_KHR_ray_tracing](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_KHR_ray_tracing.html)
|
||||
extension as well as its various dependencies.
|
||||
To be able to use ray tracing, we will need VK_KHR_ACCELERATION_STRUCTURE and VK_KHR_RAY_TRACING_PIPELINE.
|
||||
Those extensions have also dependencies on other extension, therefore all the following
|
||||
extensions will need to be added.
|
||||
|
||||
```` C
|
||||
// #VKRay: Activate the ray tracing extension
|
||||
vk::PhysicalDeviceRayTracingFeaturesKHR raytracingFeature;
|
||||
contextInfo.addDeviceExtension(VK_KHR_RAY_TRACING_EXTENSION_NAME, false, &raytracingFeature);
|
||||
vk::PhysicalDeviceAccelerationStructureFeaturesKHR accelFeature;
|
||||
contextInfo.addDeviceExtension(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME, false,
|
||||
&accelFeature);
|
||||
vk::PhysicalDeviceRayTracingPipelineFeaturesKHR rtPipelineFeature;
|
||||
contextInfo.addDeviceExtension(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME, false,
|
||||
&rtPipelineFeature);
|
||||
contextInfo.addDeviceExtension(VK_KHR_MAINTENANCE3_EXTENSION_NAME);
|
||||
contextInfo.addDeviceExtension(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME);
|
||||
contextInfo.addDeviceExtension(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME);
|
||||
|
|
@ -115,20 +127,20 @@ contextInfo.addDeviceExtension(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
|
|||
|
||||
````
|
||||
|
||||
Before creating the device, a linked structure of features must past. Not all extensions requires a set of features, but
|
||||
ray tracing features must be enabled before the creation of the device. By providing
|
||||
`raytracingFeature`, the context creation will query the capable features for ray tracing and will use the
|
||||
filled structure to create the device.
|
||||
|
||||
Before creating the device, a linked structure of features must past. Not all extensions
|
||||
requires a set of features, but ray tracing features must be enabled before the creation of the device.
|
||||
By providing `accelFeature`, and `rtPipelineFeature`, the context creation will query the capable features
|
||||
for ray tracing and will use the filled structure to create the device.
|
||||
|
||||
In the `HelloVulkan` class in `hello_vulkan.h`, add an initialization function and a member storing the capabilities of
|
||||
the GPU for ray tracing:
|
||||
|
||||
```` C
|
||||
// #VKRay
|
||||
void initRayTracing();
|
||||
vk::PhysicalDeviceRayTracingPropertiesKHR m_rtProperties;
|
||||
void initRayTracing();
|
||||
vk::PhysicalDeviceRayTracingPipelinePropertiesKHR m_rtProperties;
|
||||
````
|
||||
|
||||
At the end of `hello_vulkan.cpp`, add the body of `initRayTracing()`, which will query the ray tracing capabilities
|
||||
of the GPU using this extension. In particular, it will obtain the maximum recursion depth,
|
||||
ie. the number of nested ray tracing calls that can be performed from a single ray. This can be seen as the number
|
||||
|
|
@ -144,15 +156,17 @@ creating the shader binding table in a later section.
|
|||
void HelloVulkan::initRayTracing()
|
||||
{
|
||||
// Requesting ray tracing properties
|
||||
auto properties = m_physicalDevice.getProperties2<vk::PhysicalDeviceProperties2,
|
||||
vk::PhysicalDeviceRayTracingPropertiesKHR>();
|
||||
m_rtProperties = properties.get<vk::PhysicalDeviceRayTracingPropertiesKHR>();
|
||||
auto properties =
|
||||
m_physicalDevice.getProperties2<vk::PhysicalDeviceProperties2,
|
||||
vk::PhysicalDeviceRayTracingPipelinePropertiesKHR>();
|
||||
m_rtProperties = properties.get<vk::PhysicalDeviceRayTracingPipelinePropertiesKHR>();
|
||||
}
|
||||
````
|
||||
|
||||
## main
|
||||
|
||||
In `main.cpp`, in the `main()` function, we call the initialization method right after `helloVk.updateDescriptorSet();`
|
||||
In `main.cpp`, in the `main()` function, we call the initialization method right after
|
||||
`helloVk.updateDescriptorSet();`
|
||||
|
||||
```` C
|
||||
// #VKRay
|
||||
|
|
@ -188,12 +202,11 @@ This sample loads an OBJ file and stores its indices, vertices and material data
|
|||
model is referenced by an `ObjInstance` structure which also contains the transformation matrix of that particular
|
||||
instance. For ray tracing the `ObjModel` and `ObjInstance` will then naturally fit the BLAS and TLAS, respectively.
|
||||
|
||||
To simplify the ray tracing setup we use a helper class containing utility functions for acceleration structure builds.
|
||||
In the header file, include the`raytrace_vkpp` helper
|
||||
To simplify the ray tracing setup we use a helper class containing utility functions for
|
||||
acceleration structure builds. In the header file, include the`raytrace_vkpp` helper
|
||||
|
||||
```` C
|
||||
// #VKRay
|
||||
#define ALLOC_DEDICATED
|
||||
#include "nvvk/raytrace_vk.hpp"
|
||||
````
|
||||
|
||||
|
|
@ -224,11 +237,12 @@ nvvk::RaytracingBuilderKHR::Blas objectToVkGeometryKHR(const ObjModel& model);
|
|||
|
||||
Its implementation will fill three structures
|
||||
|
||||
* vk::AccelerationStructureCreateGeometryTypeInfoKHR: that defines how the AS will be constructed.
|
||||
* vk::AccelerationStructureGeometryKHR: the geometry for build the AS, in this case, from triangles.
|
||||
* vk::AccelerationStructureBuildOffsetInfoKHR: the offset, which correspond to the actual wanted geometry when building.
|
||||
* vk::AccelerationStructureGeometryTrianglesDataKHR: defines the data from which the AS will be constructed.
|
||||
* vk::AccelerationStructureGeometryKHR: the geometry type for building the AS, in this case, from triangles.
|
||||
* vk::AccelerationStructureBuildRangeInfoKHR: the offset, which correspond to the actual wanted geometry when building.
|
||||
|
||||
Multiple of the above structure can be combined to create a single blas. In this example, the array will always be a length of one.
|
||||
Multiple of the above structure can be combined to create a single blas. In this example,
|
||||
the array will always be a length of one.
|
||||
|
||||
Note that we consider all objects opaque for now, and indicate this to the builder for
|
||||
potential optimization.
|
||||
|
|
@ -239,44 +253,37 @@ potential optimization.
|
|||
//
|
||||
nvvk::RaytracingBuilderKHR::Blas HelloVulkan::objectToVkGeometryKHR(const ObjModel& model)
|
||||
{
|
||||
// Setting up the creation info of acceleration structure
|
||||
vk::AccelerationStructureCreateGeometryTypeInfoKHR asCreate;
|
||||
asCreate.setGeometryType(vk::GeometryTypeKHR::eTriangles);
|
||||
asCreate.setIndexType(vk::IndexType::eUint32);
|
||||
asCreate.setVertexFormat(vk::Format::eR32G32B32Sfloat);
|
||||
asCreate.setMaxPrimitiveCount(model.nbIndices / 3); // Nb triangles
|
||||
asCreate.setMaxVertexCount(model.nbVertices);
|
||||
asCreate.setAllowsTransforms(VK_FALSE); // No adding transformation matrices
|
||||
|
||||
// Building part
|
||||
vk::DeviceAddress vertexAddress = m_device.getBufferAddress({model.vertexBuffer.buffer});
|
||||
vk::DeviceAddress indexAddress = m_device.getBufferAddress({model.indexBuffer.buffer});
|
||||
|
||||
uint32_t maxPrimitiveCount = model.nbIndices / 3;
|
||||
|
||||
vk::AccelerationStructureGeometryTrianglesDataKHR triangles;
|
||||
triangles.setVertexFormat(asCreate.vertexFormat);
|
||||
triangles.setVertexFormat(vk::Format::eR32G32B32Sfloat);
|
||||
triangles.setVertexData(vertexAddress);
|
||||
triangles.setVertexStride(sizeof(VertexObj));
|
||||
triangles.setIndexType(asCreate.indexType);
|
||||
triangles.setIndexType(vk::IndexType::eUint32);
|
||||
triangles.setIndexData(indexAddress);
|
||||
triangles.setTransformData({});
|
||||
triangles.setMaxVertex(model.nbVertices);
|
||||
|
||||
// Setting up the build info of the acceleration
|
||||
vk::AccelerationStructureGeometryKHR asGeom;
|
||||
asGeom.setGeometryType(asCreate.geometryType);
|
||||
asGeom.setGeometryType(vk::GeometryTypeKHR::eTriangles);
|
||||
asGeom.setFlags(vk::GeometryFlagBitsKHR::eOpaque);
|
||||
asGeom.geometry.setTriangles(triangles);
|
||||
|
||||
// The primitive itself
|
||||
vk::AccelerationStructureBuildOffsetInfoKHR offset;
|
||||
vk::AccelerationStructureBuildRangeInfoKHR offset;
|
||||
offset.setFirstVertex(0);
|
||||
offset.setPrimitiveCount(asCreate.maxPrimitiveCount);
|
||||
offset.setPrimitiveCount(maxPrimitiveCount);
|
||||
offset.setPrimitiveOffset(0);
|
||||
offset.setTransformOffset(0);
|
||||
|
||||
// Our blas is only one geometry, but could be made of many geometries
|
||||
nvvk::RaytracingBuilderKHR::Blas blas;
|
||||
blas.asGeometry.emplace_back(asGeom);
|
||||
blas.asCreateGeometryInfo.emplace_back(asCreate);
|
||||
blas.asBuildOffsetInfo.emplace_back(offset);
|
||||
|
||||
return blas;
|
||||
|
|
@ -817,7 +824,7 @@ void HelloVulkan::createRtDescriptorSet()
|
|||
m_rtDescSetLayout = m_rtDescSetLayoutBind.createLayout(m_device);
|
||||
m_rtDescSet = m_device.allocateDescriptorSets({m_rtDescPool, 1, &m_rtDescSetLayout})[0];
|
||||
|
||||
vk::AccelerationStructureKHR tlas = m_rtBuilder.getAccelerationStructure();
|
||||
vk::AccelerationStructureKHR tlas = m_rtBuilder.getAccelerationStructure();
|
||||
vk::WriteDescriptorSetAccelerationStructureKHR descASInfo;
|
||||
descASInfo.setAccelerationStructureCount(1);
|
||||
descASInfo.setPAccelerationStructures(&tlas);
|
||||
|
|
@ -825,8 +832,8 @@ void HelloVulkan::createRtDescriptorSet()
|
|||
{}, m_offscreenColor.descriptor.imageView, vk::ImageLayout::eGeneral};
|
||||
|
||||
std::vector<vk::WriteDescriptorSet> writes;
|
||||
writes.emplace_back(m_rtDescSetLayoutBind.makeWrite(m_rtDescSet, 0, descASInfo));
|
||||
writes.emplace_back(m_rtDescSetLayoutBind.makeWrite(m_rtDescSet, 1, imageInfo));
|
||||
writes.emplace_back(m_rtDescSetLayoutBind.makeWrite(m_rtDescSet, 0, &descASInfo));
|
||||
writes.emplace_back(m_rtDescSetLayoutBind.makeWrite(m_rtDescSet, 1, &imageInfo));
|
||||
m_device.updateDescriptorSets(static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
|
||||
}
|
||||
````
|
||||
|
|
@ -840,27 +847,27 @@ index buffers. Even though the vertex and index buffers will only be used by the
|
|||
descriptor set as they semantically fit the Scene descriptor set.
|
||||
|
||||
```` C
|
||||
// Camera matrices (binding = 0)
|
||||
m_descSetLayoutBind.addBinding(
|
||||
vkDS(0, vkDT::eUniformBuffer, 1, vkSS::eVertex | vkSS::eRaygenKHR));
|
||||
// Materials (binding = 1)
|
||||
m_descSetLayoutBind.addBinding(
|
||||
vkDS(1, vkDT::eStorageBuffer, nbObj, vkSS::eVertex | vkSS::eFragment | vkSS::eClosestHitKHR));
|
||||
// Scene description (binding = 2)
|
||||
m_descSetLayoutBind.addBinding( //
|
||||
vkDS(2, vkDT::eStorageBuffer, 1, vkSS::eVertex | vkSS::eFragment | vkSS::eClosestHitKHR));
|
||||
// Textures (binding = 3)
|
||||
m_descSetLayoutBind.addBinding(
|
||||
vkDS(3, vkDT::eCombinedImageSampler, nbTxt, vkSS::eFragment | vkSS::eClosestHitKHR));
|
||||
// Materials (binding = 4)
|
||||
m_descSetLayoutBind.addBinding(
|
||||
vkDS(4, vkDT::eStorageBuffer, nbObj, vkSS::eFragment | vkSS::eClosestHitKHR));
|
||||
// Storing vertices (binding = 5)
|
||||
m_descSetLayoutBind.addBinding( //
|
||||
vkDS(5, vkDT::eStorageBuffer, nbObj, vkSS::eClosestHitKHR));
|
||||
// Storing indices (binding = 6)
|
||||
m_descSetLayoutBind.addBinding( //
|
||||
vkDS(6, vkDT::eStorageBuffer, nbObj, vkSS::eClosestHitKHR));
|
||||
// Camera matrices (binding = 0)
|
||||
m_descSetLayoutBind.addBinding(
|
||||
vkDS(0, vkDT::eUniformBuffer, 1, vkSS::eVertex | vkSS::eRaygenKHR));
|
||||
// Materials (binding = 1)
|
||||
m_descSetLayoutBind.addBinding(
|
||||
vkDS(1, vkDT::eStorageBuffer, nbObj, vkSS::eVertex | vkSS::eFragment | vkSS::eClosestHitKHR));
|
||||
// Scene description (binding = 2)
|
||||
m_descSetLayoutBind.addBinding( //
|
||||
vkDS(2, vkDT::eStorageBuffer, 1, vkSS::eVertex | vkSS::eFragment | vkSS::eClosestHitKHR));
|
||||
// Textures (binding = 3)
|
||||
m_descSetLayoutBind.addBinding(
|
||||
vkDS(3, vkDT::eCombinedImageSampler, nbTxt, vkSS::eFragment | vkSS::eClosestHitKHR));
|
||||
// Materials (binding = 4)
|
||||
m_descSetLayoutBind.addBinding(
|
||||
vkDS(4, vkDT::eStorageBuffer, nbObj, vkSS::eFragment | vkSS::eClosestHitKHR));
|
||||
// Storing vertices (binding = 5)
|
||||
m_descSetLayoutBind.addBinding( //
|
||||
vkDS(5, vkDT::eStorageBuffer, nbObj, vkSS::eClosestHitKHR));
|
||||
// Storing indices (binding = 6)
|
||||
m_descSetLayoutBind.addBinding( //
|
||||
vkDS(6, vkDT::eStorageBuffer, nbObj, vkSS::eClosestHitKHR));
|
||||
````
|
||||
|
||||
We set the actual contents of the descriptor set by adding those buffers in `updateDescriptorSet()`:
|
||||
|
|
@ -884,14 +891,23 @@ We set the actual contents of the descriptor set by adding those buffers in `upd
|
|||
writes.emplace_back(m_descSetLayoutBind.makeWriteArray(m_descSet, 6, dbiIdx.data()));
|
||||
````
|
||||
|
||||
Originally the buffers containing the vertices and indices were only used by the rasterization pipeline. The ray tracing
|
||||
will need to use those buffers as storage buffers. We update the usage of the buffers in `loadModel`:
|
||||
Originally the buffers containing the vertices and indices were only used by the rasterization pipeline.
|
||||
The ray tracing will need to use those buffers as storage buffers (`VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT`),
|
||||
the address to those buffers are needed to fill the `VkAccelerationStructureGeometryTrianglesDataKHR` structure,
|
||||
and because they are use for constructing the acceleration structure, they also need
|
||||
the `VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR` flag.
|
||||
|
||||
We update the usage of the buffers in `loadModel`:
|
||||
|
||||
```` C
|
||||
model.vertexBuffer =
|
||||
m_alloc.createBuffer(cmdBuf, loader.m_vertices, vkBU::eVertexBuffer | vkBU::eStorageBuffer);
|
||||
model.indexBuffer =
|
||||
m_alloc.createBuffer(cmdBuf, loader.m_indices, vkBU::eIndexBuffer | vkBU::eStorageBuffer);
|
||||
model.vertexBuffer =
|
||||
m_alloc.createBuffer(cmdBuf, loader.m_vertices,
|
||||
vkBU::eVertexBuffer | vkBU::eStorageBuffer | vkBU::eShaderDeviceAddress
|
||||
| vkBU::eAccelerationStructureBuildInputReadOnlyKHR);
|
||||
model.indexBuffer =
|
||||
m_alloc.createBuffer(cmdBuf, loader.m_indices,
|
||||
vkBU::eIndexBuffer | vkBU::eStorageBuffer | vkBU::eShaderDeviceAddress
|
||||
| vkBU::eAccelerationStructureBuildInputReadOnlyKHR);
|
||||
````
|
||||
|
||||
!!! Note: Array of Buffers
|
||||
|
|
@ -1076,10 +1092,10 @@ void HelloVulkan::createRtPipeline()
|
|||
|
||||
vk::ShaderModule raygenSM =
|
||||
nvvk::createShaderModule(m_device, //
|
||||
nvh::loadFile("shaders/raytrace.rgen.spv", true, paths));
|
||||
nvh::loadFile("shaders/raytrace.rgen.spv", true, paths, true));
|
||||
vk::ShaderModule missSM =
|
||||
nvvk::createShaderModule(m_device, //
|
||||
nvh::loadFile("shaders/raytrace.rmiss.spv", true, paths));
|
||||
nvh::loadFile("shaders/raytrace.rmiss.spv", true, paths, true));
|
||||
|
||||
std::vector<vk::PipelineShaderStageCreateInfo> stages;
|
||||
|
||||
|
|
@ -1114,7 +1130,7 @@ shaders.
|
|||
// Hit Group - Closest Hit + AnyHit
|
||||
vk::ShaderModule chitSM =
|
||||
nvvk::createShaderModule(m_device, //
|
||||
nvh::loadFile("shaders/raytrace.rchit.spv", true, paths));
|
||||
nvh::loadFile("shaders/raytrace.rchit.spv", true, paths, true));
|
||||
|
||||
vk::RayTracingShaderGroupCreateInfoKHR hg{vk::RayTracingShaderGroupTypeKHR::eTrianglesHitGroup,
|
||||
VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR,
|
||||
|
|
@ -1192,9 +1208,10 @@ call as a miss. Note that it is preferable to keep the recursion level as low as
|
|||
formulation instead.
|
||||
|
||||
```` C
|
||||
rayPipelineInfo.setMaxRecursionDepth(1); // Ray depth
|
||||
rayPipelineInfo.setMaxPipelineRayRecursionDepth(1); // Ray depth
|
||||
rayPipelineInfo.setLayout(m_rtPipelineLayout);
|
||||
m_rtPipeline = m_device.createRayTracingPipelineKHR({}, rayPipelineInfo);
|
||||
m_rtPipeline = static_cast<const vk::Pipeline&>(
|
||||
m_device.createRayTracingPipelineKHR({}, {}, rayPipelineInfo));
|
||||
````
|
||||
|
||||
Once the pipeline has been created we discard the shader modules:
|
||||
|
|
@ -1279,23 +1296,29 @@ void HelloVulkan::createRtShaderBindingTable()
|
|||
auto groupCount =
|
||||
static_cast<uint32_t>(m_rtShaderGroups.size()); // 3 shaders: raygen, miss, chit
|
||||
uint32_t groupHandleSize = m_rtProperties.shaderGroupHandleSize; // Size of a program identifier
|
||||
uint32_t baseAlignment = m_rtProperties.shaderGroupBaseAlignment; // Size of shader alignment
|
||||
uint32_t groupSizeAligned =
|
||||
nvh::align_up(groupHandleSize, m_rtProperties.shaderGroupBaseAlignment);
|
||||
|
||||
// Fetch all the shader handles used in the pipeline, so that they can be written in the SBT
|
||||
uint32_t sbtSize = groupCount * baseAlignment;
|
||||
uint32_t sbtSize = groupCount * groupSizeAligned;
|
||||
````
|
||||
|
||||
We then fetch the handles to the shader groups of the pipeline, and let the allocator allocate the device memory and
|
||||
copy the handles into the SBT:
|
||||
We then fetch the handles to the shader groups of the pipeline, and let the allocator
|
||||
allocate the device memory and copy the handles into the SBT. Note that SBT buffer need the
|
||||
`VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR` flag and since we will need the address
|
||||
of SBT buffer, therefore the buffer need also the `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT` flag.
|
||||
|
||||
```` C
|
||||
std::vector<uint8_t> shaderHandleStorage(sbtSize);
|
||||
m_device.getRayTracingShaderGroupHandlesKHR(m_rtPipeline, 0, groupCount, sbtSize,
|
||||
shaderHandleStorage.data());
|
||||
// Write the handles in the SBT
|
||||
m_rtSBTBuffer = m_alloc.createBuffer(sbtSize, vk::BufferUsageFlagBits::eTransferSrc,
|
||||
vk::MemoryPropertyFlagBits::eHostVisible
|
||||
| vk::MemoryPropertyFlagBits::eHostCoherent);
|
||||
|
||||
// Write the handles in the SBT
|
||||
m_rtSBTBuffer = m_alloc.createBuffer(
|
||||
sbtSize,
|
||||
vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eShaderDeviceAddress
|
||||
| vk::BufferUsageFlagBits::eShaderBindingTableKHR,
|
||||
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
|
||||
m_debug.setObjectName(m_rtSBTBuffer.buffer, std::string("SBT").c_str());
|
||||
|
||||
// Write the handles in the SBT
|
||||
|
|
@ -1304,7 +1327,7 @@ copy the handles into the SBT:
|
|||
for(uint32_t g = 0; g < groupCount; g++)
|
||||
{
|
||||
memcpy(pData, shaderHandleStorage.data() + g * groupHandleSize, groupHandleSize); // raygen
|
||||
pData += baseAlignment;
|
||||
pData += groupSizeAligned;
|
||||
}
|
||||
m_alloc.unmap(m_rtSBTBuffer);
|
||||
|
||||
|
|
@ -1366,12 +1389,21 @@ each shader. In our case the stride is simply the size of a shader group handle,
|
|||
shader-group-specific data within the SBT, resulting in a larger stride.
|
||||
|
||||
```` C
|
||||
vk::DeviceSize progSize = m_rtProperties.shaderGroupBaseAlignment; // Size of a program identifier
|
||||
vk::DeviceSize rayGenOffset = 0u * progSize; // Start at the beginning of m_sbtBuffer
|
||||
vk::DeviceSize missOffset = 1u * progSize; // Jump over raygen
|
||||
vk::DeviceSize missStride = progSize;
|
||||
vk::DeviceSize hitGroupOffset = 2u * progSize; // Jump over the previous shaders
|
||||
vk::DeviceSize hitGroupStride = progSize;
|
||||
// Size of a program identifier
|
||||
uint32_t groupSize =
|
||||
nvh::align_up(m_rtProperties.shaderGroupHandleSize, m_rtProperties.shaderGroupBaseAlignment);
|
||||
uint32_t groupStride = groupSize;
|
||||
vk::DeviceSize hitGroupSize =
|
||||
nvh::align_up(m_rtProperties.shaderGroupHandleSize + sizeof(HitRecordBuffer),
|
||||
m_rtProperties.shaderGroupBaseAlignment);
|
||||
vk::DeviceAddress sbtAddress = m_device.getBufferAddress({m_rtSBTBuffer.buffer});
|
||||
|
||||
using Stride = vk::StridedDeviceAddressRegionKHR;
|
||||
std::array<Stride, 4> strideAddresses{
|
||||
Stride{sbtAddress + 0u * groupSize, groupStride, groupSize * 1}, // raygen
|
||||
Stride{sbtAddress + 1u * groupSize, groupStride, groupSize * 2}, // miss
|
||||
Stride{sbtAddress + 3u * groupSize, hitGroupSize, hitGroupSize * 3}, // hit
|
||||
Stride{0u, 0u, 0u}}; // callable
|
||||
|
||||
````
|
||||
|
||||
|
|
@ -1382,19 +1414,9 @@ three parameters are equivalent to the grid size of a compute launch, and repres
|
|||
we want to trace one ray per pixel, the grid size has the width and height of the output image, and a depth of 1.
|
||||
|
||||
```` C
|
||||
vk::DeviceSize sbtSize = progSize * (vk::DeviceSize)m_rtShaderGroups.size();
|
||||
|
||||
const vk::StridedBufferRegionKHR raygenShaderBindingTable = {m_rtSBTBuffer.buffer, rayGenOffset,
|
||||
progSize, sbtSize};
|
||||
const vk::StridedBufferRegionKHR missShaderBindingTable = {m_rtSBTBuffer.buffer, missOffset,
|
||||
progSize, sbtSize};
|
||||
const vk::StridedBufferRegionKHR hitShaderBindingTable = {m_rtSBTBuffer.buffer, hitGroupOffset,
|
||||
progSize, sbtSize};
|
||||
const vk::StridedBufferRegionKHR callableShaderBindingTable;
|
||||
|
||||
cmdBuf.traceRaysKHR(&raygenShaderBindingTable, &missShaderBindingTable, &hitShaderBindingTable,
|
||||
&callableShaderBindingTable, //
|
||||
m_size.width, m_size.height, 1); //
|
||||
cmdBuf.traceRaysKHR(&strideAddresses[0], &strideAddresses[1], &strideAddresses[2],
|
||||
&strideAddresses[3], //
|
||||
m_size.width, m_size.height, 1); //
|
||||
|
||||
m_debug.endLabel(cmdBuf);
|
||||
}
|
||||
|
|
@ -1823,8 +1845,8 @@ The OBJ model is loaded in `main.cpp` by calling `helloVk.loadModel`. Let's load
|
|||
|
||||
```` C
|
||||
// Creation of the example
|
||||
helloVk.loadModel(nvh::findFile("media/scenes/Medieval_building.obj", defaultSearchPaths));
|
||||
helloVk.loadModel(nvh::findFile("media/scenes/plane.obj", defaultSearchPaths));
|
||||
helloVk.loadModel(nvh::findFile("media/scenes/Medieval_building.obj", defaultSearchPaths, true));
|
||||
helloVk.loadModel(nvh::findFile("media/scenes/plane.obj", defaultSearchPaths, true));
|
||||
````
|
||||
|
||||
Since that model is larger, we can change the `CameraManip.setLookat` call to
|
||||
|
|
@ -1860,7 +1882,7 @@ In the body of `createRtPipeline`, we need to define the new miss shader right a
|
|||
// simply indicates that no occlusion has been found
|
||||
vk::ShaderModule shadowmissSM =
|
||||
nvvk::createShaderModule(m_device,
|
||||
nvh::loadFile("shaders/raytraceShadow.rmiss.spv", true, paths));
|
||||
nvh::loadFile("shaders/raytraceShadow.rmiss.spv", true, paths, true));
|
||||
|
||||
````
|
||||
|
||||
|
|
@ -1880,7 +1902,7 @@ The pipeline now has to allow shooting rays from the closest hit program, which
|
|||
// hit points of the camera rays, hence a recursion level of 2. This number should be kept as low
|
||||
// as possible for performance reasons. Even recursive ray tracing should be flattened into a loop
|
||||
// in the ray generation to avoid deep recursion.
|
||||
rayPipelineInfo.setMaxRecursionDepth(2); // Ray depth
|
||||
rayPipelineInfo.setMaxPipelineRayRecursionDepth(2); // Ray depth
|
||||
````
|
||||
|
||||
At the end of the method, we destroy the shader module for the shadow miss shader:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue