Updating the creation of the SBT

This commit is contained in:
mklefrancois 2021-09-08 15:59:03 +02:00
parent f476512440
commit 10637464fc
17 changed files with 477 additions and 444 deletions

View file

@ -840,39 +840,70 @@ void HelloVulkan::createRtPipeline()
// The Shader Binding Table (SBT)
// - getting all shader handles and write them in a SBT buffer
// - Besides exception, this could be always done like this
// See how the SBT buffer is used in run()
//
void HelloVulkan::createRtShaderBindingTable()
{
auto groupCount = static_cast<uint32_t>(m_rtShaderGroups.size()); // 4 shaders: raygen, 2 miss, chit
uint32_t groupHandleSize = m_rtProperties.shaderGroupHandleSize; // Size of a program identifier
// Compute the actual size needed per SBT entry (round-up to alignment needed).
uint32_t groupSizeAligned = nvh::align_up(groupHandleSize, m_rtProperties.shaderGroupBaseAlignment);
// Bytes needed for the SBT.
uint32_t sbtSize = groupCount * groupSizeAligned;
uint32_t missCount{2};
uint32_t hitCount{1};
auto handleCount = 1 + missCount + hitCount;
uint32_t handleSize = m_rtProperties.shaderGroupHandleSize;
// Fetch all the shader handles used in the pipeline. This is opaque data,/ so we store it in a vector of bytes.
// The order of handles follow the stage entry.
std::vector<uint8_t> shaderHandleStorage(sbtSize);
auto result = vkGetRayTracingShaderGroupHandlesKHR(m_device, m_rtPipeline, 0, groupCount, sbtSize, shaderHandleStorage.data());
// The SBT (buffer) need to have starting groups to be aligned and handles in the group to be aligned.
uint32_t handleSizeAligned = nvh::align_up(handleSize, m_rtProperties.shaderGroupHandleAlignment);
m_rgenRegion.stride = nvh::align_up(handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
m_rgenRegion.size = m_rgenRegion.stride; // The size member of pRayGenShaderBindingTable must be equal to its stride member
m_missRegion.stride = handleSizeAligned;
m_missRegion.size = nvh::align_up(missCount * handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
m_hitRegion.stride = handleSizeAligned;
m_hitRegion.size = nvh::align_up(hitCount * handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
// Get the shader group handles
uint32_t dataSize = handleCount * handleSize;
std::vector<uint8_t> handles(dataSize);
auto result = vkGetRayTracingShaderGroupHandlesKHR(m_device, m_rtPipeline, 0, handleCount, dataSize, handles.data());
assert(result == VK_SUCCESS);
// Allocate a buffer for storing the SBT. Give it a debug name for NSight.
// Allocate a buffer for storing the SBT.
VkDeviceSize sbtSize = m_rgenRegion.size + m_missRegion.size + m_hitRegion.size + m_callRegion.size;
m_rtSBTBuffer = m_alloc.createBuffer(sbtSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT
| VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
m_debug.setObjectName(m_rtSBTBuffer.buffer, std::string("SBT"));
m_debug.setObjectName(m_rtSBTBuffer.buffer, std::string("SBT")); // Give it a debug name for NSight.
// Find the SBT addresses of each group
VkBufferDeviceAddressInfo info{VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, nullptr, m_rtSBTBuffer.buffer};
VkDeviceAddress sbtAddress = vkGetBufferDeviceAddress(m_device, &info);
m_rgenRegion.deviceAddress = sbtAddress;
m_missRegion.deviceAddress = sbtAddress + m_rgenRegion.size;
m_hitRegion.deviceAddress = sbtAddress + m_rgenRegion.size + m_missRegion.size;
// Helper to retrieve the handle data
auto getHandle = [&](int i) { return handles.data() + i * handleSize; };
// Map the SBT buffer and write in the handles.
void* mapped = m_alloc.map(m_rtSBTBuffer);
auto* pData = reinterpret_cast<uint8_t*>(mapped);
for(uint32_t g = 0; g < groupCount; g++)
auto* pSBTBuffer = reinterpret_cast<uint8_t*>(m_alloc.map(m_rtSBTBuffer));
uint8_t* pData{nullptr};
uint32_t handleIdx{0};
// Raygen
pData = pSBTBuffer;
memcpy(pData, getHandle(handleIdx++), handleSize);
// Miss
pData = pSBTBuffer + m_rgenRegion.size;
for(uint32_t c = 0; c < missCount; c++)
{
memcpy(pData, shaderHandleStorage.data() + g * groupHandleSize, groupHandleSize);
pData += groupSizeAligned;
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += m_missRegion.stride;
}
// Hit
pData = pSBTBuffer + m_rgenRegion.size + m_missRegion.size;
for(uint32_t c = 0; c < hitCount; c++)
{
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += m_hitRegion.stride;
}
m_alloc.unmap(m_rtSBTBuffer);
m_alloc.finalizeAndReleaseStaging();
}
@ -898,22 +929,7 @@ void HelloVulkan::raytrace(const VkCommandBuffer& cmdBuf, const nvmath::vec4f& c
0, sizeof(PushConstantRay), &m_pcRay);
// Size of a program identifier
uint32_t groupSize = nvh::align_up(m_rtProperties.shaderGroupHandleSize, m_rtProperties.shaderGroupBaseAlignment);
uint32_t groupStride = groupSize;
VkBufferDeviceAddressInfo info{VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, nullptr, m_rtSBTBuffer.buffer};
VkDeviceAddress sbtAddress = vkGetBufferDeviceAddress(m_device, &info);
using Stride = VkStridedDeviceAddressRegionKHR;
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, groupStride, groupSize * 1}, // hit
Stride{0u, 0u, 0u}}; // callable
vkCmdTraceRaysKHR(cmdBuf, &strideAddresses[0], &strideAddresses[1], &strideAddresses[2], &strideAddresses[3],
m_size.width, m_size.height, 1);
vkCmdTraceRaysKHR(cmdBuf, &m_rgenRegion, &m_missRegion, &m_hitRegion, &m_callRegion, m_size.width, m_size.height, 1);
m_debug.endLabel(cmdBuf);

View file

@ -144,7 +144,12 @@ public:
std::vector<VkRayTracingShaderGroupCreateInfoKHR> m_rtShaderGroups;
VkPipelineLayout m_rtPipelineLayout;
VkPipeline m_rtPipeline;
nvvk::Buffer m_rtSBTBuffer;
VkStridedDeviceAddressRegionKHR m_rgenRegion{};
VkStridedDeviceAddressRegionKHR m_missRegion{};
VkStridedDeviceAddressRegionKHR m_hitRegion{};
VkStridedDeviceAddressRegionKHR m_callRegion{};
// Push constant for ray tracer
PushConstantRay m_pcRay{};

View file

@ -39,7 +39,6 @@
extern std::vector<std::string> defaultSearchPaths;
//--------------------------------------------------------------------------------------------------
// Keep the handle on the device
// Initialize the tool to do all our allocations: buffers, images
@ -685,8 +684,7 @@ void HelloVulkan::createTopLevelAS()
//
void HelloVulkan::createRtDescriptorSet()
{
// Top-level acceleration structure, usable by both the ray generation and the closest hit (to
// shoot shadow rays)
// Top-level acceleration structure, usable by both the ray generation and the closest hit (to shoot shadow rays)
m_rtDescSetLayoutBind.addBinding(RtxBindings::eTlas, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1,
VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR); // TLAS
m_rtDescSetLayoutBind.addBinding(RtxBindings::eOutImage, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1,
@ -857,39 +855,70 @@ void HelloVulkan::createRtPipeline()
// The Shader Binding Table (SBT)
// - getting all shader handles and write them in a SBT buffer
// - Besides exception, this could be always done like this
// See how the SBT buffer is used in run()
//
void HelloVulkan::createRtShaderBindingTable()
{
auto groupCount = static_cast<uint32_t>(m_rtShaderGroups.size()); // shaders: raygen, 2 miss, chit, 2 ahit
uint32_t groupHandleSize = m_rtProperties.shaderGroupHandleSize; // Size of a program identifier
// Compute the actual size needed per SBT entry (round-up to alignment needed).
uint32_t groupSizeAligned = nvh::align_up(groupHandleSize, m_rtProperties.shaderGroupBaseAlignment);
// Bytes needed for the SBT.
uint32_t sbtSize = groupCount * groupSizeAligned;
uint32_t missCount{2};
uint32_t hitCount{2};
auto handleCount = 1 + missCount + hitCount;
uint32_t handleSize = m_rtProperties.shaderGroupHandleSize;
// Fetch all the shader handles used in the pipeline. This is opaque data,
// so we store it in a vector of bytes.
std::vector<uint8_t> shaderHandleStorage(sbtSize);
auto result = vkGetRayTracingShaderGroupHandlesKHR(m_device, m_rtPipeline, 0, groupCount, sbtSize, shaderHandleStorage.data());
// The SBT (buffer) need to have starting groups to be aligned and handles in the group to be aligned.
uint32_t handleSizeAligned = nvh::align_up(handleSize, m_rtProperties.shaderGroupHandleAlignment);
m_rgenRegion.stride = nvh::align_up(handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
m_rgenRegion.size = m_rgenRegion.stride; // The size member of pRayGenShaderBindingTable must be equal to its stride member
m_missRegion.stride = handleSizeAligned;
m_missRegion.size = nvh::align_up(missCount * handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
m_hitRegion.stride = handleSizeAligned;
m_hitRegion.size = nvh::align_up(hitCount * handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
// Get the shader group handles
uint32_t dataSize = handleCount * handleSize;
std::vector<uint8_t> handles(dataSize);
auto result = vkGetRayTracingShaderGroupHandlesKHR(m_device, m_rtPipeline, 0, handleCount, dataSize, handles.data());
assert(result == VK_SUCCESS);
// Allocate a buffer for storing the SBT. Give it a debug name for NSight.
// Allocate a buffer for storing the SBT.
VkDeviceSize sbtSize = m_rgenRegion.size + m_missRegion.size + m_hitRegion.size + m_callRegion.size;
m_rtSBTBuffer = m_alloc.createBuffer(sbtSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT
| VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
m_debug.setObjectName(m_rtSBTBuffer.buffer, std::string("SBT").c_str());
m_debug.setObjectName(m_rtSBTBuffer.buffer, std::string("SBT")); // Give it a debug name for NSight.
// Find the SBT addresses of each group
VkBufferDeviceAddressInfo info{VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, nullptr, m_rtSBTBuffer.buffer};
VkDeviceAddress sbtAddress = vkGetBufferDeviceAddress(m_device, &info);
m_rgenRegion.deviceAddress = sbtAddress;
m_missRegion.deviceAddress = sbtAddress + m_rgenRegion.size;
m_hitRegion.deviceAddress = sbtAddress + m_rgenRegion.size + m_missRegion.size;
// Helper to retrieve the handle data
auto getHandle = [&](int i) { return handles.data() + i * handleSize; };
// Map the SBT buffer and write in the handles.
void* mapped = m_alloc.map(m_rtSBTBuffer);
auto* pData = reinterpret_cast<uint8_t*>(mapped);
for(uint32_t g = 0; g < groupCount; g++)
auto* pSBTBuffer = reinterpret_cast<uint8_t*>(m_alloc.map(m_rtSBTBuffer));
uint8_t* pData{nullptr};
uint32_t handleIdx{0};
// Raygen
pData = pSBTBuffer;
memcpy(pData, getHandle(handleIdx++), handleSize);
// Miss
pData = pSBTBuffer + m_rgenRegion.size;
for(uint32_t c = 0; c < missCount; c++)
{
memcpy(pData, shaderHandleStorage.data() + g * groupHandleSize, groupHandleSize);
pData += groupSizeAligned;
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += m_missRegion.stride;
}
// Hit
pData = pSBTBuffer + m_rgenRegion.size + m_missRegion.size;
for(uint32_t c = 0; c < hitCount; c++)
{
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += m_hitRegion.stride;
}
m_alloc.unmap(m_rtSBTBuffer);
m_alloc.finalizeAndReleaseStaging();
}
@ -910,7 +939,6 @@ void HelloVulkan::raytrace(const VkCommandBuffer& cmdBuf, const nvmath::vec4f& c
m_pcRay.lightIntensity = m_pcRaster.lightIntensity;
m_pcRay.lightType = m_pcRaster.lightType;
std::vector<VkDescriptorSet> descSets{m_rtDescSet, m_descSet};
vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, m_rtPipeline);
vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, m_rtPipelineLayout, 0,
@ -920,21 +948,7 @@ void HelloVulkan::raytrace(const VkCommandBuffer& cmdBuf, const nvmath::vec4f& c
0, sizeof(PushConstantRay), &m_pcRay);
// Size of a program identifier
uint32_t groupSize = nvh::align_up(m_rtProperties.shaderGroupHandleSize, m_rtProperties.shaderGroupBaseAlignment);
uint32_t groupStride = groupSize;
VkDeviceAddress sbtAddress = nvvk::getBufferDeviceAddress(m_device, m_rtSBTBuffer.buffer);
using Stride = VkStridedDeviceAddressRegionKHR;
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, groupStride, groupSize * 2}, // hit
Stride{0u, 0u, 0u}}; // callable
vkCmdTraceRaysKHR(cmdBuf, &strideAddresses[0], &strideAddresses[1], &strideAddresses[2], &strideAddresses[3],
m_size.width, m_size.height, 1);
vkCmdTraceRaysKHR(cmdBuf, &m_rgenRegion, &m_missRegion, &m_hitRegion, &m_callRegion, m_size.width, m_size.height, 1);
m_debug.endLabel(cmdBuf);

View file

@ -145,7 +145,13 @@ public:
std::vector<VkRayTracingShaderGroupCreateInfoKHR> m_rtShaderGroups;
VkPipelineLayout m_rtPipelineLayout;
VkPipeline m_rtPipeline;
nvvk::Buffer m_rtSBTBuffer;
VkStridedDeviceAddressRegionKHR m_rgenRegion{};
VkStridedDeviceAddressRegionKHR m_missRegion{};
VkStridedDeviceAddressRegionKHR m_hitRegion{};
VkStridedDeviceAddressRegionKHR m_callRegion{};
int m_maxFrames{10000};
// Push constant for ray tracer

View file

@ -1116,41 +1116,71 @@ void HelloVulkan::createRtPipeline()
// The Shader Binding Table (SBT)
// - getting all shader handles and write them in a SBT buffer
// - Besides exception, this could be always done like this
// See how the SBT buffer is used in run()
//
void HelloVulkan::createRtShaderBindingTable()
{
auto groupCount = static_cast<uint32_t>(m_rtShaderGroups.size());
assert(groupCount == 8 && "Update Comment"); // 8 shaders: raygen, 3 miss, 4 chit
uint32_t missCount{3};
uint32_t hitCount{4};
auto handleCount = 1 + missCount + hitCount;
uint32_t handleSize = m_rtProperties.shaderGroupHandleSize;
uint32_t groupHandleSize = m_rtProperties.shaderGroupHandleSize; // Size of a program identifier
// Compute the actual size needed per SBT entry (round-up to alignment needed).
uint32_t groupSizeAligned = nvh::align_up(groupHandleSize, m_rtProperties.shaderGroupBaseAlignment);
// Bytes needed for the SBT.
uint32_t sbtSize = groupCount * groupSizeAligned;
// The SBT (buffer) need to have starting groups to be aligned and handles in the group to be aligned.
uint32_t handleSizeAligned = nvh::align_up(handleSize, m_rtProperties.shaderGroupHandleAlignment);
// Fetch all the shader handles used in the pipeline. This is opaque data,
// so we store it in a vector of bytes.
std::vector<uint8_t> shaderHandleStorage(sbtSize);
auto result = vkGetRayTracingShaderGroupHandlesKHR(m_device, m_rtPipeline, 0, groupCount, sbtSize, shaderHandleStorage.data());
m_rgenRegion.stride = nvh::align_up(handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
m_rgenRegion.size = m_rgenRegion.stride; // The size member of pRayGenShaderBindingTable must be equal to its stride member
m_missRegion.stride = handleSizeAligned;
m_missRegion.size = nvh::align_up(missCount * handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
m_hitRegion.stride = handleSizeAligned;
m_hitRegion.size = nvh::align_up(hitCount * handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
// Get the shader group handles
uint32_t dataSize = handleCount * handleSize;
std::vector<uint8_t> handles(dataSize);
auto result = vkGetRayTracingShaderGroupHandlesKHR(m_device, m_rtPipeline, 0, handleCount, dataSize, handles.data());
assert(result == VK_SUCCESS);
// Allocate a buffer for storing the SBT. Give it a debug name for NSight.
// Allocate a buffer for storing the SBT.
VkDeviceSize sbtSize = m_rgenRegion.size + m_missRegion.size + m_hitRegion.size + m_callRegion.size;
m_rtSBTBuffer = m_alloc.createBuffer(sbtSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT
| VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
m_debug.setObjectName(m_rtSBTBuffer.buffer, std::string("SBT"));
m_debug.setObjectName(m_rtSBTBuffer.buffer, std::string("SBT")); // Give it a debug name for NSight.
// Find the SBT addresses of each group
VkBufferDeviceAddressInfo info{VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, nullptr, m_rtSBTBuffer.buffer};
VkDeviceAddress sbtAddress = vkGetBufferDeviceAddress(m_device, &info);
m_rgenRegion.deviceAddress = sbtAddress;
m_missRegion.deviceAddress = sbtAddress + m_rgenRegion.size;
m_hitRegion.deviceAddress = sbtAddress + m_rgenRegion.size + m_missRegion.size;
// Helper to retrieve the handle data
auto getHandle = [&](int i) { return handles.data() + i * handleSize; };
// Map the SBT buffer and write in the handles.
void* mapped = m_alloc.map(m_rtSBTBuffer);
auto* pData = reinterpret_cast<uint8_t*>(mapped);
for(uint32_t g = 0; g < groupCount; g++)
auto* pSBTBuffer = reinterpret_cast<uint8_t*>(m_alloc.map(m_rtSBTBuffer));
uint8_t* pData{nullptr};
uint32_t handleIdx{0};
// Raygen
pData = pSBTBuffer;
memcpy(pData, getHandle(handleIdx++), handleSize);
// Miss
pData = pSBTBuffer + m_rgenRegion.size;
for(uint32_t c = 0; c < missCount; c++)
{
memcpy(pData, shaderHandleStorage.data() + g * groupHandleSize, groupHandleSize);
pData += groupSizeAligned;
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += m_missRegion.stride;
}
// Hit
pData = pSBTBuffer + m_rgenRegion.size + m_missRegion.size;
for(uint32_t c = 0; c < hitCount; c++)
{
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += m_hitRegion.stride;
}
m_alloc.unmap(m_rtSBTBuffer);
m_alloc.finalizeAndReleaseStaging();
}
@ -1323,22 +1353,8 @@ void HelloVulkan::raytrace(const VkCommandBuffer& cmdBuf, const nvmath::vec4f& c
0, sizeof(PushConstantRay), &m_pcRay);
// Size of a program identifier
uint32_t groupSize = nvh::align_up(m_rtProperties.shaderGroupHandleSize, m_rtProperties.shaderGroupBaseAlignment);
uint32_t groupStride = groupSize;
vkCmdTraceRaysKHR(cmdBuf, &m_rgenRegion, &m_missRegion, &m_hitRegion, &m_callRegion, m_size.width, m_size.height, 1);
VkDeviceAddress sbtAddress = nvvk::getBufferDeviceAddress(m_device, m_rtSBTBuffer.buffer);
using Stride = VkStridedDeviceAddressRegionKHR;
std::array<Stride, 4> strideAddresses{Stride{sbtAddress + 0u * groupSize, groupStride, groupSize * 1}, // raygen
Stride{sbtAddress + 1u * groupSize, groupStride, groupSize * 3}, // miss
Stride{sbtAddress + 4u * groupSize, groupStride, groupSize * 4}, // hit
Stride{0u, 0u, 0u}}; // callable
// First pass, illuminate scene with global light.
vkCmdTraceRaysKHR(cmdBuf, &strideAddresses[0], &strideAddresses[1], &strideAddresses[2], &strideAddresses[3],
m_size.width, m_size.height, 1);
// Lantern passes, ensure previous pass completed, then add light contribution from each lantern.
for(int i = 0; i < static_cast<int>(m_lanternCount); ++i)
@ -1372,9 +1388,7 @@ void HelloVulkan::raytrace(const VkCommandBuffer& cmdBuf, const nvmath::vec4f& c
nvvk::getBufferDeviceAddress(m_device, m_lanternIndirectBuffer.buffer) + i * sizeof(LanternIndirectEntry);
// Execute lantern pass.
vkCmdTraceRaysIndirectKHR(cmdBuf, &strideAddresses[0], &strideAddresses[1], //
&strideAddresses[2], &strideAddresses[3], //
indirectDeviceAddress);
vkCmdTraceRaysIndirectKHR(cmdBuf, &m_rgenRegion, &m_missRegion, &m_hitRegion, &m_callRegion, indirectDeviceAddress);
}
m_debug.endLabel(cmdBuf);

View file

@ -206,7 +206,12 @@ public:
VkDescriptorSet m_lanternIndirectDescSet;
VkPipelineLayout m_lanternIndirectCompPipelineLayout;
VkPipeline m_lanternIndirectCompPipeline;
nvvk::Buffer m_rtSBTBuffer;
VkStridedDeviceAddressRegionKHR m_rgenRegion{};
VkStridedDeviceAddressRegionKHR m_missRegion{};
VkStridedDeviceAddressRegionKHR m_hitRegion{};
VkStridedDeviceAddressRegionKHR m_callRegion{};
// Buffer to source vkCmdTraceRaysIndirectKHR indirect parameters and lantern color,
// position, etc. from when doing lantern lighting passes.

View file

@ -423,7 +423,7 @@ void HelloVulkan::rasterize(const VkCommandBuffer& cmdBuf)
vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, &m_descSet, 0, nullptr);
uint32_t nbInst = static_cast<uint32_t>(m_instances.size() - 1); // Remove the implicit object
auto nbInst = static_cast<uint32_t>(m_instances.size() - 1); // Remove the implicit object
for(uint32_t i = 0; i < nbInst; ++i)
{
auto& inst = m_instances[i];
@ -998,39 +998,70 @@ void HelloVulkan::createRtPipeline()
// The Shader Binding Table (SBT)
// - getting all shader handles and write them in a SBT buffer
// - Besides exception, this could be always done like this
// See how the SBT buffer is used in run()
//
void HelloVulkan::createRtShaderBindingTable()
{
auto groupCount = static_cast<uint32_t>(m_rtShaderGroups.size()); // 4 shaders: raygen, 2 miss, chit
uint32_t groupHandleSize = m_rtProperties.shaderGroupHandleSize; // Size of a program identifier
// Compute the actual size needed per SBT entry (round-up to alignment needed).
uint32_t groupSizeAligned = nvh::align_up(groupHandleSize, m_rtProperties.shaderGroupBaseAlignment);
// Bytes needed for the SBT.
uint32_t sbtSize = groupCount * groupSizeAligned;
uint32_t missCount{2};
uint32_t hitCount{2};
auto handleCount = 1 + missCount + hitCount;
uint32_t handleSize = m_rtProperties.shaderGroupHandleSize;
// Fetch all the shader handles used in the pipeline. This is opaque data,
// so we store it in a vector of bytes.
std::vector<uint8_t> shaderHandleStorage(sbtSize);
auto result = vkGetRayTracingShaderGroupHandlesKHR(m_device, m_rtPipeline, 0, groupCount, sbtSize, shaderHandleStorage.data());
// The SBT (buffer) need to have starting groups to be aligned and handles in the group to be aligned.
uint32_t handleSizeAligned = nvh::align_up(handleSize, m_rtProperties.shaderGroupHandleAlignment);
m_rgenRegion.stride = nvh::align_up(handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
m_rgenRegion.size = m_rgenRegion.stride; // The size member of pRayGenShaderBindingTable must be equal to its stride member
m_missRegion.stride = handleSizeAligned;
m_missRegion.size = nvh::align_up(missCount * handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
m_hitRegion.stride = handleSizeAligned;
m_hitRegion.size = nvh::align_up(hitCount * handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
// Get the shader group handles
uint32_t dataSize = handleCount * handleSize;
std::vector<uint8_t> handles(dataSize);
auto result = vkGetRayTracingShaderGroupHandlesKHR(m_device, m_rtPipeline, 0, handleCount, dataSize, handles.data());
assert(result == VK_SUCCESS);
// Allocate a buffer for storing the SBT. Give it a debug name for NSight.
// Allocate a buffer for storing the SBT.
VkDeviceSize sbtSize = m_rgenRegion.size + m_missRegion.size + m_hitRegion.size + m_callRegion.size;
m_rtSBTBuffer = m_alloc.createBuffer(sbtSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT
| VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
m_debug.setObjectName(m_rtSBTBuffer.buffer, std::string("SBT"));
m_debug.setObjectName(m_rtSBTBuffer.buffer, std::string("SBT")); // Give it a debug name for NSight.
// Find the SBT addresses of each group
VkBufferDeviceAddressInfo info{VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, nullptr, m_rtSBTBuffer.buffer};
VkDeviceAddress sbtAddress = vkGetBufferDeviceAddress(m_device, &info);
m_rgenRegion.deviceAddress = sbtAddress;
m_missRegion.deviceAddress = sbtAddress + m_rgenRegion.size;
m_hitRegion.deviceAddress = sbtAddress + m_rgenRegion.size + m_missRegion.size;
// Helper to retrieve the handle data
auto getHandle = [&](int i) { return handles.data() + i * handleSize; };
// Map the SBT buffer and write in the handles.
void* mapped = m_alloc.map(m_rtSBTBuffer);
auto* pData = reinterpret_cast<uint8_t*>(mapped);
for(uint32_t g = 0; g < groupCount; g++)
auto* pSBTBuffer = reinterpret_cast<uint8_t*>(m_alloc.map(m_rtSBTBuffer));
uint8_t* pData{nullptr};
uint32_t handleIdx{0};
// Raygen
pData = pSBTBuffer;
memcpy(pData, getHandle(handleIdx++), handleSize);
// Miss
pData = pSBTBuffer + m_rgenRegion.size;
for(uint32_t c = 0; c < missCount; c++)
{
memcpy(pData, shaderHandleStorage.data() + g * groupHandleSize, groupHandleSize);
pData += groupSizeAligned;
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += m_missRegion.stride;
}
// Hit
pData = pSBTBuffer + m_rgenRegion.size + m_missRegion.size;
for(uint32_t c = 0; c < hitCount; c++)
{
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += m_hitRegion.stride;
}
m_alloc.unmap(m_rtSBTBuffer);
m_alloc.finalizeAndReleaseStaging();
}
@ -1047,7 +1078,6 @@ void HelloVulkan::raytrace(const VkCommandBuffer& cmdBuf, const nvmath::vec4f& c
m_pcRay.lightIntensity = m_pcRaster.lightIntensity;
m_pcRay.lightType = m_pcRaster.lightType;
std::vector<VkDescriptorSet> descSets{m_rtDescSet, m_descSet};
vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, m_rtPipeline);
vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, m_rtPipelineLayout, 0,
@ -1057,21 +1087,7 @@ void HelloVulkan::raytrace(const VkCommandBuffer& cmdBuf, const nvmath::vec4f& c
0, sizeof(PushConstantRay), &m_pcRay);
// Size of a program identifier
uint32_t groupSize = nvh::align_up(m_rtProperties.shaderGroupHandleSize, m_rtProperties.shaderGroupBaseAlignment);
uint32_t groupStride = groupSize;
VkDeviceAddress sbtAddress = nvvk::getBufferDeviceAddress(m_device, m_rtSBTBuffer.buffer);
using Stride = VkStridedDeviceAddressRegionKHR;
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, groupStride, groupSize * 2}, // hit
Stride{0u, 0u, 0u}}; // callable
vkCmdTraceRaysKHR(cmdBuf, &strideAddresses[0], &strideAddresses[1], &strideAddresses[2], &strideAddresses[3],
m_size.width, m_size.height, 1);
vkCmdTraceRaysKHR(cmdBuf, &m_rgenRegion, &m_missRegion, &m_hitRegion, &m_callRegion, m_size.width, m_size.height, 1);
m_debug.endLabel(cmdBuf);

View file

@ -144,7 +144,12 @@ public:
std::vector<VkRayTracingShaderGroupCreateInfoKHR> m_rtShaderGroups;
VkPipelineLayout m_rtPipelineLayout;
VkPipeline m_rtPipeline;
nvvk::Buffer m_rtSBTBuffer;
VkStridedDeviceAddressRegionKHR m_rgenRegion{};
VkStridedDeviceAddressRegionKHR m_missRegion{};
VkStridedDeviceAddressRegionKHR m_hitRegion{};
VkStridedDeviceAddressRegionKHR m_callRegion{};
// Push constant for ray tracer
PushConstantRay m_pcRay{};

View file

@ -843,39 +843,70 @@ void HelloVulkan::createRtPipeline()
// The Shader Binding Table (SBT)
// - getting all shader handles and write them in a SBT buffer
// - Besides exception, this could be always done like this
// See how the SBT buffer is used in run()
//
void HelloVulkan::createRtShaderBindingTable()
{
auto groupCount = static_cast<uint32_t>(m_rtShaderGroups.size()); // 4 shaders: raygen, 2 miss, chit
uint32_t groupHandleSize = m_rtProperties.shaderGroupHandleSize; // Size of a program identifier
// Compute the actual size needed per SBT entry (round-up to alignment needed).
uint32_t groupSizeAligned = nvh::align_up(groupHandleSize, m_rtProperties.shaderGroupBaseAlignment);
// Bytes needed for the SBT.
uint32_t sbtSize = groupCount * groupSizeAligned;
uint32_t missCount{2};
uint32_t hitCount{1};
auto handleCount = 1 + missCount + hitCount;
uint32_t handleSize = m_rtProperties.shaderGroupHandleSize;
// Fetch all the shader handles used in the pipeline. This is opaque data,/ so we store it in a vector of bytes.
// The order of handles follow the stage entry.
std::vector<uint8_t> shaderHandleStorage(sbtSize);
auto result = vkGetRayTracingShaderGroupHandlesKHR(m_device, m_rtPipeline, 0, groupCount, sbtSize, shaderHandleStorage.data());
// The SBT (buffer) need to have starting groups to be aligned and handles in the group to be aligned.
uint32_t handleSizeAligned = nvh::align_up(handleSize, m_rtProperties.shaderGroupHandleAlignment);
m_rgenRegion.stride = nvh::align_up(handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
m_rgenRegion.size = m_rgenRegion.stride; // The size member of pRayGenShaderBindingTable must be equal to its stride member
m_missRegion.stride = handleSizeAligned;
m_missRegion.size = nvh::align_up(missCount * handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
m_hitRegion.stride = handleSizeAligned;
m_hitRegion.size = nvh::align_up(hitCount * handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
// Get the shader group handles
uint32_t dataSize = handleCount * handleSize;
std::vector<uint8_t> handles(dataSize);
auto result = vkGetRayTracingShaderGroupHandlesKHR(m_device, m_rtPipeline, 0, handleCount, dataSize, handles.data());
assert(result == VK_SUCCESS);
// Allocate a buffer for storing the SBT. Give it a debug name for NSight.
// Allocate a buffer for storing the SBT.
VkDeviceSize sbtSize = m_rgenRegion.size + m_missRegion.size + m_hitRegion.size + m_callRegion.size;
m_rtSBTBuffer = m_alloc.createBuffer(sbtSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT
| VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
m_debug.setObjectName(m_rtSBTBuffer.buffer, std::string("SBT"));
m_debug.setObjectName(m_rtSBTBuffer.buffer, std::string("SBT")); // Give it a debug name for NSight.
// Find the SBT addresses of each group
VkBufferDeviceAddressInfo info{VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, nullptr, m_rtSBTBuffer.buffer};
VkDeviceAddress sbtAddress = vkGetBufferDeviceAddress(m_device, &info);
m_rgenRegion.deviceAddress = sbtAddress;
m_missRegion.deviceAddress = sbtAddress + m_rgenRegion.size;
m_hitRegion.deviceAddress = sbtAddress + m_rgenRegion.size + m_missRegion.size;
// Helper to retrieve the handle data
auto getHandle = [&](int i) { return handles.data() + i * handleSize; };
// Map the SBT buffer and write in the handles.
void* mapped = m_alloc.map(m_rtSBTBuffer);
auto* pData = reinterpret_cast<uint8_t*>(mapped);
for(uint32_t g = 0; g < groupCount; g++)
auto* pSBTBuffer = reinterpret_cast<uint8_t*>(m_alloc.map(m_rtSBTBuffer));
uint8_t* pData{nullptr};
uint32_t handleIdx{0};
// Raygen
pData = pSBTBuffer;
memcpy(pData, getHandle(handleIdx++), handleSize);
// Miss
pData = pSBTBuffer + m_rgenRegion.size;
for(uint32_t c = 0; c < missCount; c++)
{
memcpy(pData, shaderHandleStorage.data() + g * groupHandleSize, groupHandleSize);
pData += groupSizeAligned;
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += m_missRegion.stride;
}
// Hit
pData = pSBTBuffer + m_rgenRegion.size + m_missRegion.size;
for(uint32_t c = 0; c < hitCount; c++)
{
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += m_hitRegion.stride;
}
m_alloc.unmap(m_rtSBTBuffer);
m_alloc.finalizeAndReleaseStaging();
}
@ -906,21 +937,7 @@ void HelloVulkan::raytrace(const VkCommandBuffer& cmdBuf, const nvmath::vec4f& c
0, sizeof(PushConstantRay), &m_pcRay);
// Size of a program identifier
uint32_t groupSize = nvh::align_up(m_rtProperties.shaderGroupHandleSize, m_rtProperties.shaderGroupBaseAlignment);
uint32_t groupStride = groupSize;
VkDeviceAddress sbtAddress = nvvk::getBufferDeviceAddress(m_device, m_rtSBTBuffer.buffer);
using Stride = VkStridedDeviceAddressRegionKHR;
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, groupStride, groupSize * 1}, // hit
Stride{0u, 0u, 0u}}; // callable
vkCmdTraceRaysKHR(cmdBuf, &strideAddresses[0], &strideAddresses[1], &strideAddresses[2], &strideAddresses[3],
m_size.width, m_size.height, 1);
vkCmdTraceRaysKHR(cmdBuf, &m_rgenRegion, &m_missRegion, &m_hitRegion, &m_callRegion, m_size.width, m_size.height, 1);
m_debug.endLabel(cmdBuf);

View file

@ -145,7 +145,13 @@ public:
std::vector<VkRayTracingShaderGroupCreateInfoKHR> m_rtShaderGroups;
VkPipelineLayout m_rtPipelineLayout;
VkPipeline m_rtPipeline;
nvvk::Buffer m_rtSBTBuffer;
VkStridedDeviceAddressRegionKHR m_rgenRegion{};
VkStridedDeviceAddressRegionKHR m_missRegion{};
VkStridedDeviceAddressRegionKHR m_hitRegion{};
VkStridedDeviceAddressRegionKHR m_callRegion{};
int m_maxFrames{10};
// Push constant for ray tracer

View file

@ -133,16 +133,10 @@ This information can be used to pass extra information to a shader, for each ent
**:warning: Note:**
Since each entry in an SBT group must have the same size, each entry of the group has to have enough space to accommodate the largest element in the entire group.
The following diagram represents our current SBT, with the addition of some data to `HitGroup1`. As mentioned in the **note**, even if
`HitGroup0` doesn't have any shader record data, it still needs to have the same size as `HitGroup1`, the largest of the hit group.
The following diagram represents our current SBT, with some data added to `HitGroup1`. As mentioned in the **note**, even though `HitGroup0` has no shader record data, it still needs to be the same size as `HitGroup1`, the largest of the hit group and aligned to the Handle alignment.
![](images/sbt_0.png)
|Group|Handle|
| ------ | ------ |
| RayGen | Handle 0 |
| Miss0 | Handle 1 |
| Miss1 | Handle 2 |
| HitGroup0 | Handle 3 </br>-Empty- |
| HitGroup1 | Handle 4 </br> Data 0 |
## `hello_vulkan.h`
@ -200,16 +194,9 @@ data `m_hitShaderRecord` to the Hit group.
// Find handle indices and add data
m_sbtWrapper.addIndices(rayPipelineInfo);
m_sbtWrapper.addData(SBTWrapper::eHit, 1, m_hitShaderRecord[0]);
m_sbtWrapper.addData(SBTWrapper::eHit, 2, m_hitShaderRecord[1]);
m_sbtWrapper.create(m_rtPipeline);
````
The buffer for Hit will have the following layout
```
| handle | handle,data | handle,data |
```
The wrapper will make sure the stride covers the largest data and is aligned
based on the GPU properties.
@ -217,95 +204,26 @@ based on the GPU properties.
~~Since we are no longer compacting all handles in a continuous buffer, we need to fill the SBT as described above.~~
~~After retrieving the handles of all 5 groups (raygen, miss, miss shadow, hit0, and hit1)
using `getRayTracingShaderGroupHandlesKHR`, store the pointers to easily retrieve them.~~
~~~~ C++
// Retrieve the handle pointers
std::vector<uint8_t*> handles(groupCount);
for(uint32_t i = 0; i < groupCount; i++)
{
handles[i] = &shaderHandleStorage[i * groupHandleSize];
}
~~~~
~~The size of each group can be described as follows:~~
~~~~ C++
// Sizes
uint32_t rayGenSize = baseAlignment;
uint32_t missSize = baseAlignment;
uint32_t hitSize =
ROUND_UP(groupHandleSize + static_cast<int>(sizeof(HitRecordBuffer)), baseAlignment);
uint32_t newSbtSize = rayGenSize + 2 * missSize + 2 * hitSize;
m_hitRegion.stride = nvh::align_up(handleSize + sizeof(HitRecordBuffer), m_rtProperties.shaderGroupHandleAlignment);
~~~~
~~Then write the new SBT like this, where only Hit 1 has extra data.~~
~~~~ C++
std::vector<uint8_t> sbtBuffer(newSbtSize);
{
uint8_t* pBuffer = sbtBuffer.data();
// Hit
pData = pSBTBuffer + m_rgenRegion.size + m_missRegion.size;
memcpy(pData, getHandle(handleIdx++), handleSize);
memcpy(pBuffer, handles[0], groupHandleSize); // Raygen
pBuffer += rayGenSize;
memcpy(pBuffer, handles[1], groupHandleSize); // Miss 0
pBuffer += missSize;
memcpy(pBuffer, handles[2], groupHandleSize); // Miss 1
pBuffer += missSize;
uint8_t* pHitBuffer = pBuffer;
memcpy(pHitBuffer, handles[3], groupHandleSize); // Hit 0
// No data
pBuffer += hitSize;
pHitBuffer = pBuffer;
memcpy(pHitBuffer, handles[4], groupHandleSize); // Hit 1
pHitBuffer += groupHandleSize;
memcpy(pHitBuffer, &m_hitShaderRecord[0], sizeof(HitRecordBuffer)); // Hit 1 data
pBuffer += hitSize;
}
// hit 1
pData = pSBTBuffer + m_rgenRegion.size + m_missRegion.size + m_hitRegion.stride;
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += handleSize;
memcpy(pData, &m_hitShaderRecord[0], sizeof(HitRecordBuffer)); // Hit 1 data
~~~~
~~Then change the call to `m_alloc.createBuffer` to create the SBT buffer from `sbtBuffer`:~~
## Ray Trace
~~~~ C++
m_rtSBTBuffer = m_alloc.createBuffer(cmdBuf, sbtBuffer, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR);
~~~~
### `raytrace`
**:star:NEW:star:**
The mvvk::SBTWrapper gives use the information without having to compute the `VkStridedDeviceAddressRegionKHR`
``` C
auto& regions = m_sbtWrapper.getRegions();
vkCmdTraceRaysKHR(cmdBuf, &regions[0], &regions[1], &regions[2], &regions[3], m_size.width, m_size.height, 1);
```
**OLD**
~~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++
VkDeviceSize hitGroupSize =
nvh::align_up(m_rtProperties.shaderGroupHandleSize + sizeof(HitRecordBuffer),
m_rtProperties.shaderGroupBaseAlignment);
~~~~
~~The stride device address will be modified like this:~~
~~~~ C++
using Stride = VkStridedDeviceAddressRegionKHR;
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:**
The result should now show both `wuson` models with a yellow color.~~
![](images/manyhits4.png)
@ -316,14 +234,8 @@ The SBT can be larger than the number of shading models, which could then be use
The following modification will add another entry to the SBT with a different color per instance. The new SBT hit group (2) will use the same CHIT handle (4) as hit group 1.
|Group|Handle|
| ------ | ------ |
| RayGen | Handle 0 |
| Miss0 | Handle 1 |
| Miss1 | Handle 2 |
| HitGroup0 | Handle 3 </br>-Empty- |
| HitGroup1 | Handle 4 </br> Data 0 |
| HitGroup1 | Handle 4 </br> Data 1 |
![](images/sbt_1.png)
### `main.cpp`
@ -342,25 +254,24 @@ In the description of the scene in `main`, we will tell the `wuson` models to us
**:star:NEW:star:**
If you are using the SBT wrapper, this part is automatically handled.
If you are using the SBT wrapper, make sure to add the data to the third Hit (2).
~~~~ C
// Find handle indices and add data
m_sbtWrapper.addIndices(rayPipelineInfo);
m_sbtWrapper.addData(nvvk::SBTWrapper::eHit, 1, m_hitShaderRecord[0]);
m_sbtWrapper.addData(nvvk::SBTWrapper::eHit, 2, m_hitShaderRecord[1]);
m_sbtWrapper.create(m_rtPipeline);
~~~~
**OLD**
~~The size of the SBT will now account for its 3 hit groups:~~
~~~~ C++
uint32_t newSbtSize = rayGenSize + 2 * missSize + 3 * hitSize;
~~~~
~~Finally, we need to add the new entry as well at the end of the buffer, reusing the handle of the second Hit Group and setting a different color.~~
~~~~ C++
pHitBuffer = pBuffer;
memcpy(pHitBuffer, handles[4], groupHandleSize); // Hit 2
pHitBuffer += groupHandleSize;
memcpy(pHitBuffer, &m_hitShaderRecord[1], sizeof(HitRecordBuffer)); // Hit 2 data
pBuffer += hitSize;
// hit 2
pData = pSBTBuffer + m_rgenRegion.size + m_missRegion.size + (2 * m_hitRegion.stride);
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += handleSize;
memcpy(pData, &m_hitShaderRecord[1], sizeof(HitRecordBuffer)); // Hit 2 data
~~~~
~~**Note:**

View file

@ -384,13 +384,16 @@ void HelloVulkan::destroyResources()
// #VKRay
#ifdef USE_SBT_WRAPPER
m_sbtWrapper.destroy();
#else
m_alloc.destroy(m_rtSBTBuffer);
#endif
m_rtBuilder.destroy();
vkDestroyPipeline(m_device, m_rtPipeline, nullptr);
vkDestroyPipelineLayout(m_device, m_rtPipelineLayout, nullptr);
vkDestroyDescriptorPool(m_device, m_rtDescPool, nullptr);
vkDestroyDescriptorSetLayout(m_device, m_rtDescSetLayout, nullptr);
m_alloc.destroy(m_rtSBTBuffer);
m_alloc.deinit();
}
@ -595,7 +598,9 @@ void HelloVulkan::initRayTracing()
m_rtBuilder.setup(m_device, &m_alloc, m_graphicsQueueIndex);
#ifdef USE_SBT_WRAPPER
m_sbtWrapper.setup(m_device, m_graphicsQueueIndex, &m_alloc, m_rtProperties);
#endif
}
//--------------------------------------------------------------------------------------------------
@ -848,11 +853,15 @@ void HelloVulkan::createRtPipeline()
vkCreateRayTracingPipelinesKHR(m_device, {}, {}, 1, &rayPipelineInfo, nullptr, &m_rtPipeline);
#ifdef USE_SBT_WRAPPER
// Find handle indices and add data
m_sbtWrapper.addIndices(rayPipelineInfo);
m_sbtWrapper.addData(SBTWrapper::eHit, 1, m_hitShaderRecord[0]);
m_sbtWrapper.addData(SBTWrapper::eHit, 2, m_hitShaderRecord[1]);
m_sbtWrapper.addData(nvvk::SBTWrapper::eHit, 1, m_hitShaderRecord[0]);
m_sbtWrapper.addData(nvvk::SBTWrapper::eHit, 2, m_hitShaderRecord[1]);
m_sbtWrapper.create(m_rtPipeline);
#else
createRtShaderBindingTable();
#endif
for(auto& s : stages)
vkDestroyShaderModule(m_device, s.module, nullptr);
@ -864,78 +873,84 @@ void HelloVulkan::createRtPipeline()
// - Besides exception, this could be always done like this
// See how the SBT buffer is used in run()
//
#ifndef USE_SBT_WRAPPER
void HelloVulkan::createRtShaderBindingTable()
{
auto groupCount = static_cast<uint32_t>(m_rtShaderGroups.size()); // 4 shaders: raygen, 2 miss, chit
uint32_t groupHandleSize = m_rtProperties.shaderGroupHandleSize; // Size of a program identifier
// Compute the actual size needed per SBT entry (round-up to alignment needed).
uint32_t groupSizeAligned = nvh::align_up(groupHandleSize, m_rtProperties.shaderGroupBaseAlignment);
// Bytes needed for the SBT.
uint32_t sbtSize = groupCount * groupSizeAligned;
// Fetch all the shader handles used in the pipeline. This is opaque data,/ so we store it in a vector of bytes.
// The order of handles follow the stage entry.
std::vector<uint8_t> shaderHandleStorage(sbtSize);
auto result = vkGetRayTracingShaderGroupHandlesKHR(m_device, m_rtPipeline, 0, groupCount, sbtSize, shaderHandleStorage.data());
uint32_t missCount{2};
uint32_t hitCount{3};
auto handleCount = 1 + missCount + hitCount;
uint32_t handleSize = m_rtProperties.shaderGroupHandleSize;
// Get the shader group handles
uint32_t dataSize = handleCount * handleSize;
std::vector<uint8_t> handles(dataSize);
auto result = vkGetRayTracingShaderGroupHandlesKHR(m_device, m_rtPipeline, 0, handleCount, dataSize, handles.data());
assert(result == VK_SUCCESS);
// Retrieve the handle pointers
std::vector<uint8_t*> handles(groupCount);
for(uint32_t i = 0; i < groupCount; i++)
// The SBT (buffer) need to have starting groups to be aligned and handles in the group to be aligned.
uint32_t handleSizeAligned = nvh::align_up(handleSize, m_rtProperties.shaderGroupHandleAlignment);
m_rgenRegion.stride = nvh::align_up(handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
m_rgenRegion.size = m_rgenRegion.stride; // The size member of pRayGenShaderBindingTable must be equal to its stride member
m_missRegion.stride = handleSizeAligned;
m_missRegion.size = nvh::align_up(missCount * handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
m_hitRegion.stride = nvh::align_up(handleSize + sizeof(HitRecordBuffer), m_rtProperties.shaderGroupHandleAlignment);
m_hitRegion.size = nvh::align_up(hitCount * handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
// Allocate a buffer for storing the SBT.
VkDeviceSize sbtSize = m_rgenRegion.size + m_missRegion.size + m_hitRegion.size + m_callRegion.size;
m_rtSBTBuffer = m_alloc.createBuffer(sbtSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT
| VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
m_debug.setObjectName(m_rtSBTBuffer.buffer, std::string("SBT")); // Give it a debug name for NSight.
// Find the SBT addresses of each group
VkBufferDeviceAddressInfo info{VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, nullptr, m_rtSBTBuffer.buffer};
VkDeviceAddress sbtAddress = vkGetBufferDeviceAddress(m_device, &info);
m_rgenRegion.deviceAddress = sbtAddress;
m_missRegion.deviceAddress = sbtAddress + m_rgenRegion.size;
m_hitRegion.deviceAddress = sbtAddress + m_rgenRegion.size + m_missRegion.size;
// Helper to retrieve the handle data
auto getHandle = [&](int i) { return handles.data() + i * handleSize; };
// Map the SBT buffer and write in the handles.
auto* pSBTBuffer = reinterpret_cast<uint8_t*>(m_alloc.map(m_rtSBTBuffer));
uint8_t* pData{nullptr};
uint32_t handleIdx{0};
// Raygen
pData = pSBTBuffer;
memcpy(pData, getHandle(handleIdx++), handleSize);
// Miss
pData = pSBTBuffer + m_rgenRegion.size;
for(uint32_t c = 0; c < missCount; c++)
{
handles[i] = &shaderHandleStorage[i * groupHandleSize];
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += m_missRegion.stride;
}
// Hit
pData = pSBTBuffer + m_rgenRegion.size + m_missRegion.size;
memcpy(pData, getHandle(handleIdx++), handleSize);
// Sizes
uint32_t rayGenSize = groupSizeAligned;
uint32_t missSize = groupSizeAligned;
uint32_t hitSize = nvh::align_up(groupHandleSize + static_cast<int>(sizeof(HitRecordBuffer)), groupSizeAligned);
uint32_t newSbtSize = rayGenSize + 2 * missSize + 3 * hitSize;
// hit 1
pData = pSBTBuffer + m_rgenRegion.size + m_missRegion.size + m_hitRegion.stride;
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += handleSize;
memcpy(pData, &m_hitShaderRecord[0], sizeof(HitRecordBuffer)); // Hit 1 data
std::vector<uint8_t> sbtBuffer(newSbtSize);
{
uint8_t* pBuffer = sbtBuffer.data();
memcpy(pBuffer, handles[0], groupHandleSize); // Raygen
pBuffer += rayGenSize;
memcpy(pBuffer, handles[1], groupHandleSize); // Miss 0
pBuffer += missSize;
memcpy(pBuffer, handles[2], groupHandleSize); // Miss 1
pBuffer += missSize;
uint8_t* pHitBuffer = pBuffer;
memcpy(pHitBuffer, handles[3], groupHandleSize); // Hit 0
// No data
pBuffer += hitSize;
pHitBuffer = pBuffer;
memcpy(pHitBuffer, handles[4], groupHandleSize); // Hit 1
pHitBuffer += groupHandleSize;
memcpy(pHitBuffer, &m_hitShaderRecord[0], sizeof(HitRecordBuffer)); // Hit 1 data
pBuffer += hitSize;
pHitBuffer = pBuffer;
memcpy(pHitBuffer, handles[4], groupHandleSize); // Hit 2
pHitBuffer += groupHandleSize;
memcpy(pHitBuffer, &m_hitShaderRecord[1], sizeof(HitRecordBuffer)); // Hit 2 data
// pBuffer += hitSize;
}
// Write the handles in the SBT
nvvk::CommandPool genCmdBuf(m_device, m_graphicsQueueIndex);
VkCommandBuffer cmdBuf = genCmdBuf.createCommandBuffer();
m_rtSBTBuffer = m_alloc.createBuffer(cmdBuf, sbtBuffer,
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR);
m_debug.setObjectName(m_rtSBTBuffer.buffer, "SBT");
// hit 2
pData = pSBTBuffer + m_rgenRegion.size + m_missRegion.size + (2 * m_hitRegion.stride);
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += handleSize;
memcpy(pData, &m_hitShaderRecord[1], sizeof(HitRecordBuffer)); // Hit 2 data
genCmdBuf.submitAndWait(cmdBuf);
m_alloc.unmap(m_rtSBTBuffer);
m_alloc.finalizeAndReleaseStaging();
}
#endif
//--------------------------------------------------------------------------------------------------
// Ray Tracing the scene
@ -959,39 +974,12 @@ void HelloVulkan::raytrace(const VkCommandBuffer& cmdBuf, const nvmath::vec4f& c
0, sizeof(PushConstantRay), &m_pcRay);
//// 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 * 2}, // hit
// Stride{0u, 0u, 0u}}; // callable
//strideAddresses[0] = m_sbtWrapper.getRaygenRegion();
//strideAddresses[1] = m_sbtWrapper.getMissRegion();
//strideAddresses[2] = m_sbtWrapper.getHitRegion();
//cmdBuf.traceRaysKHR(&strideAddresses[0], &strideAddresses[1], &strideAddresses[2],
// &strideAddresses[3], //
// m_size.width, m_size.height, 1); //
//std::array<vk::StridedDeviceAddressRegionKHR, 4> regions;
//regions[0] = m_sbtWrapper.getRegion(SBTWrapper::eRaygen);
//regions[1] = m_sbtWrapper.getRegion(SBTWrapper::eMiss);
//regions[2] = m_sbtWrapper.getRegion(SBTWrapper::eHit);
//regions[3] = m_sbtWrapper.getRegion(SBTWrapper::eCallable);
#ifdef USE_SBT_WRAPPER
auto& regions = m_sbtWrapper.getRegions();
vkCmdTraceRaysKHR(cmdBuf, &regions[0], &regions[1], &regions[2], &regions[3], m_size.width, m_size.height, 1);
#else
vkCmdTraceRaysKHR(cmdBuf, &m_rgenRegion, &m_missRegion, &m_hitRegion, &m_callRegion, m_size.width, m_size.height, 1);
#endif
m_debug.endLabel(cmdBuf);
}

View file

@ -28,8 +28,12 @@
// #VKRay
#include "nvvk/raytraceKHR_vk.hpp"
#define USE_SBT_WRAPPER
#ifdef USE_SBT_WRAPPER
#include "nvvk/sbtwrapper_vk.hpp"
using nvvk::SBTWrapper;
#endif
//--------------------------------------------------------------------------------------------------
// Simple rasterizer of OBJ objects
@ -147,7 +151,16 @@ public:
std::vector<VkRayTracingShaderGroupCreateInfoKHR> m_rtShaderGroups;
VkPipelineLayout m_rtPipelineLayout;
VkPipeline m_rtPipeline;
#ifdef USE_SBT_WRAPPER
nvvk::SBTWrapper m_sbtWrapper;
#else
nvvk::Buffer m_rtSBTBuffer;
VkStridedDeviceAddressRegionKHR m_rgenRegion{};
VkStridedDeviceAddressRegionKHR m_missRegion{};
VkStridedDeviceAddressRegionKHR m_hitRegion{};
VkStridedDeviceAddressRegionKHR m_callRegion{};
#endif
// Push constant for ray tracer
PushConstantRay m_pcRay{};
@ -157,6 +170,4 @@ public:
nvmath::vec4f color;
};
std::vector<HitRecordBuffer> m_hitShaderRecord;
nvvk::SBTWrapper m_sbtWrapper;
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -39,7 +39,6 @@
extern std::vector<std::string> defaultSearchPaths;
//--------------------------------------------------------------------------------------------------
// Keep the handle on the device
// Initialize the tool to do all our allocations: buffers, images
@ -683,8 +682,7 @@ void HelloVulkan::createTopLevelAS()
//
void HelloVulkan::createRtDescriptorSet()
{
// Top-level acceleration structure, usable by both the ray generation and the closest hit (to
// shoot shadow rays)
// Top-level acceleration structure, usable by both the ray generation and the closest hit (to shoot shadow rays)
m_rtDescSetLayoutBind.addBinding(RtxBindings::eTlas, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1,
VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR); // TLAS
m_rtDescSetLayoutBind.addBinding(RtxBindings::eOutImage, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1,
@ -836,39 +834,70 @@ void HelloVulkan::createRtPipeline()
// The Shader Binding Table (SBT)
// - getting all shader handles and write them in a SBT buffer
// - Besides exception, this could be always done like this
// See how the SBT buffer is used in run()
//
void HelloVulkan::createRtShaderBindingTable()
{
auto groupCount = static_cast<uint32_t>(m_rtShaderGroups.size()); // 4 shaders: raygen, 2 miss, chit
uint32_t groupHandleSize = m_rtProperties.shaderGroupHandleSize; // Size of a program identifier
// Compute the actual size needed per SBT entry (round-up to alignment needed).
uint32_t groupSizeAligned = nvh::align_up(groupHandleSize, m_rtProperties.shaderGroupBaseAlignment);
// Bytes needed for the SBT.
uint32_t sbtSize = groupCount * groupSizeAligned;
uint32_t missCount{2};
uint32_t hitCount{1};
auto handleCount = 1 + missCount + hitCount;
uint32_t handleSize = m_rtProperties.shaderGroupHandleSize;
// Fetch all the shader handles used in the pipeline. This is opaque data,/ so we store it in a vector of bytes.
// The order of handles follow the stage entry.
std::vector<uint8_t> shaderHandleStorage(sbtSize);
auto result = vkGetRayTracingShaderGroupHandlesKHR(m_device, m_rtPipeline, 0, groupCount, sbtSize, shaderHandleStorage.data());
// The SBT (buffer) need to have starting groups to be aligned and handles in the group to be aligned.
uint32_t handleSizeAligned = nvh::align_up(handleSize, m_rtProperties.shaderGroupHandleAlignment);
m_rgenRegion.stride = nvh::align_up(handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
m_rgenRegion.size = m_rgenRegion.stride; // The size member of pRayGenShaderBindingTable must be equal to its stride member
m_missRegion.stride = handleSizeAligned;
m_missRegion.size = nvh::align_up(missCount * handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
m_hitRegion.stride = handleSizeAligned;
m_hitRegion.size = nvh::align_up(hitCount * handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
// Get the shader group handles
uint32_t dataSize = handleCount * handleSize;
std::vector<uint8_t> handles(dataSize);
auto result = vkGetRayTracingShaderGroupHandlesKHR(m_device, m_rtPipeline, 0, handleCount, dataSize, handles.data());
assert(result == VK_SUCCESS);
// Allocate a buffer for storing the SBT. Give it a debug name for NSight.
// Allocate a buffer for storing the SBT.
VkDeviceSize sbtSize = m_rgenRegion.size + m_missRegion.size + m_hitRegion.size + m_callRegion.size;
m_rtSBTBuffer = m_alloc.createBuffer(sbtSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT
| VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
m_debug.setObjectName(m_rtSBTBuffer.buffer, std::string("SBT"));
m_debug.setObjectName(m_rtSBTBuffer.buffer, std::string("SBT")); // Give it a debug name for NSight.
// Find the SBT addresses of each group
VkBufferDeviceAddressInfo info{VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, nullptr, m_rtSBTBuffer.buffer};
VkDeviceAddress sbtAddress = vkGetBufferDeviceAddress(m_device, &info);
m_rgenRegion.deviceAddress = sbtAddress;
m_missRegion.deviceAddress = sbtAddress + m_rgenRegion.size;
m_hitRegion.deviceAddress = sbtAddress + m_rgenRegion.size + m_missRegion.size;
// Helper to retrieve the handle data
auto getHandle = [&](int i) { return handles.data() + i * handleSize; };
// Map the SBT buffer and write in the handles.
void* mapped = m_alloc.map(m_rtSBTBuffer);
auto* pData = reinterpret_cast<uint8_t*>(mapped);
for(uint32_t g = 0; g < groupCount; g++)
auto* pSBTBuffer = reinterpret_cast<uint8_t*>(m_alloc.map(m_rtSBTBuffer));
uint8_t* pData{nullptr};
uint32_t handleIdx{0};
// Raygen
pData = pSBTBuffer;
memcpy(pData, getHandle(handleIdx++), handleSize);
// Miss
pData = pSBTBuffer + m_rgenRegion.size;
for(uint32_t c = 0; c < missCount; c++)
{
memcpy(pData, shaderHandleStorage.data() + g * groupHandleSize, groupHandleSize);
pData += groupSizeAligned;
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += m_missRegion.stride;
}
// Hit
pData = pSBTBuffer + m_rgenRegion.size + m_missRegion.size;
for(uint32_t c = 0; c < hitCount; c++)
{
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += m_hitRegion.stride;
}
m_alloc.unmap(m_rtSBTBuffer);
m_alloc.finalizeAndReleaseStaging();
}
@ -885,7 +914,6 @@ void HelloVulkan::raytrace(const VkCommandBuffer& cmdBuf, const nvmath::vec4f& c
m_pcRay.lightIntensity = m_pcRaster.lightIntensity;
m_pcRay.lightType = m_pcRaster.lightType;
std::vector<VkDescriptorSet> descSets{m_rtDescSet, m_descSet};
vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, m_rtPipeline);
vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, m_rtPipelineLayout, 0,
@ -895,21 +923,7 @@ void HelloVulkan::raytrace(const VkCommandBuffer& cmdBuf, const nvmath::vec4f& c
0, sizeof(PushConstantRay), &m_pcRay);
// Size of a program identifier
uint32_t groupSize = nvh::align_up(m_rtProperties.shaderGroupHandleSize, m_rtProperties.shaderGroupBaseAlignment);
uint32_t groupStride = groupSize;
VkDeviceAddress sbtAddress = nvvk::getBufferDeviceAddress(m_device, m_rtSBTBuffer.buffer);
using Stride = VkStridedDeviceAddressRegionKHR;
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, groupStride, groupSize * 1}, // hit
Stride{0u, 0u, 0u}}; // callable
vkCmdTraceRaysKHR(cmdBuf, &strideAddresses[0], &strideAddresses[1], &strideAddresses[2], &strideAddresses[3],
m_size.width, m_size.height, 1);
vkCmdTraceRaysKHR(cmdBuf, &m_rgenRegion, &m_missRegion, &m_hitRegion, &m_callRegion, m_size.width, m_size.height, 1);
m_debug.endLabel(cmdBuf);

View file

@ -144,7 +144,12 @@ public:
std::vector<VkRayTracingShaderGroupCreateInfoKHR> m_rtShaderGroups;
VkPipelineLayout m_rtPipelineLayout;
VkPipeline m_rtPipeline;
nvvk::Buffer m_rtSBTBuffer;
VkStridedDeviceAddressRegionKHR m_rgenRegion{};
VkStridedDeviceAddressRegionKHR m_missRegion{};
VkStridedDeviceAddressRegionKHR m_hitRegion{};
VkStridedDeviceAddressRegionKHR m_callRegion{};
// Push constant for ray tracer
PushConstantRay m_pcRay{{}, {}, 0, 0, 10};