diff --git a/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm b/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm index 9da555e..527753d 100644 --- a/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm +++ b/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm @@ -103,7 +103,30 @@ export namespace Crafter { hitRegion.size = hitGroups.size() * sbtStride; 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* handleOffset = shaderHandles.data();