perf(rt): place SBT in device-local memory via upload-strategy helper (#72) #107
1 changed files with 24 additions and 1 deletions
perf(rt): place SBT in device-local memory via upload-strategy helper (#72)
The shader binding table is written once at pipeline creation and read by the GPU on every vkCmdTraceRaysKHR thereafter, yet it was allocated plain HOST_VISIBLE (system RAM) — so every trace dispatch read raygen/miss/hit records over PCIe. Route the SBT allocation through Device::PreferDirectDeviceWrite (#89): when a DEVICE_LOCAL|HOST_VISIBLE type exists, prefer DEVICE_LOCAL on top of the required HOST_VISIBLE so the buffer lands in mappable device memory (GPU reads hit local VRAM; the one-time memcpy goes write-combined over PCIe). When no combined type exists, the preferred hint is dropped and GetMemoryType (#59) falls back to plain HOST_VISIBLE — current behaviour. The SBT is tiny so a small BAR window's budget is never the constraint. The buffer stays mapped and the existing FlushDevice gates on the chosen memory type's coherency flags (#60), so a direct-write type lacking HOST_COHERENT is still flushed correctly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit
b410f2d5aa
|
|
@ -103,7 +103,30 @@ export namespace Crafter {
|
||||||
hitRegion.size = hitGroups.size() * sbtStride;
|
hitRegion.size = hitGroups.size() * sbtStride;
|
||||||
|
|
||||||
std::size_t bufferSize = hitRegion.deviceAddress + hitRegion.size;
|
std::size_t bufferSize = hitRegion.deviceAddress + hitRegion.size;
|
||||||
sbtBuffer.Create(VK_BUFFER_USAGE_2_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferSize);
|
// The SBT is written once here (the memcpys below) and read by the
|
||||||
|
// GPU on every vkCmdTraceRaysKHR for the pipeline's lifetime — the
|
||||||
|
// textbook write-once/read-many buffer (issue #72). Get it into
|
||||||
|
// device-local memory so trace dispatches read raygen/miss/hit
|
||||||
|
// records out of VRAM instead of over PCIe from system RAM.
|
||||||
|
//
|
||||||
|
// Route the placement through #89: when a DEVICE_LOCAL|HOST_VISIBLE
|
||||||
|
// type exists (ReBAR/UMA, or a BAR window — the SBT is tiny so the
|
||||||
|
// window budget is never the constraint), prefer DEVICE_LOCAL on top
|
||||||
|
// of the required HOST_VISIBLE so the allocation lands in device
|
||||||
|
// memory we can still map. The one-time memcpy goes write-combined
|
||||||
|
// over PCIe (write-only, sequential — ideal); GPU reads then hit
|
||||||
|
// local VRAM. When no combined type exists at all (no spec
|
||||||
|
// guarantee), PreferDirectDeviceWrite returns false and the preferred
|
||||||
|
// hint is dropped, so GetMemoryType falls back to plain HOST_VISIBLE
|
||||||
|
// (current behaviour, the per-trace PCIe read) — the cheaper fallback
|
||||||
|
// the issue blesses while staging isn't wired here.
|
||||||
|
//
|
||||||
|
// Either way the buffer stays mapped, and the FlushDevice below gates
|
||||||
|
// its flush on the *chosen* memory type's flags (issue #60), so a
|
||||||
|
// direct-write type lacking HOST_COHERENT is still flushed correctly.
|
||||||
|
VkMemoryPropertyFlags sbtPreferred = Device::PreferDirectDeviceWrite(bufferSize)
|
||||||
|
? VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT : 0;
|
||||||
|
sbtBuffer.Create(VK_BUFFER_USAGE_2_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferSize, sbtPreferred);
|
||||||
|
|
||||||
std::uint8_t* offset = sbtBuffer.value;
|
std::uint8_t* offset = sbtBuffer.value;
|
||||||
std::uint8_t* handleOffset = shaderHandles.data();
|
std::uint8_t* handleOffset = shaderHandles.data();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue