perf(mesh): place RT geometry in device-local memory via #89 upload strategy (#73)

vertexBuffer/indexBuffer/aabbBuffer were HOST_VISIBLE (system RAM), so the
BLAS build, every refit, and any hit-shader geometry fetch read them over
PCIe. The usage flags (SHADER_DEVICE_ADDRESS, plus STORAGE on the index
buffer) exist precisely to expose geometry for hit-shader fetch, the dominant
RT shading pattern.

Add VulkanBuffer::UploadDeviceLocal, which places a CPU-written/GPU-read
buffer in device-local memory and picks the upload mechanism at runtime via
the #89 strategy (Device::PreferDirectDeviceWrite):
  - ReBAR/UMA: allocate HOST_VISIBLE|DEVICE_LOCAL (DEVICE_LOCAL preferred),
    map transiently, memcpy, flush-if-non-coherent. No staging buffer.
  - no/small BAR: allocate pure DEVICE_LOCAL + TRANSFER_DST, fill a transient
    HOST_VISIBLE staging buffer, vkCmdCopyBuffer, then DeferredClear the
    staging buffer onto the fence-keyed deletion queue (#101/#102) so it
    outlives the copy submit.

The geometry buffers become non-mapped so the destination is free to be
device-local-only. Same-size re-uploads reuse the allocation, so the device
address stays stable across an in-place AS UPDATE refit. The compressed Build
path now allocates vertex/index as pure DEVICE_LOCAL — the GPU decompressor
fills them directly, no host write.

Tests: BLASBuildOptions asserts device-local placement on the triangle,
procedural, and (budget-forced) staged paths, and exercises the staged copy +
deferred-deletion + in-place refit under GPU-assisted validation with zero
validation errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
catbot 2026-06-17 17:49:25 +00:00
commit 5f858509c8
4 changed files with 161 additions and 23 deletions

View file

@ -71,12 +71,18 @@ export namespace Crafter {
public:
VulkanBuffer<char, false> scratchBuffer;
VulkanBuffer<char, false> blasBuffer;
VulkanBuffer<Vector<float, 3, 3>, true> vertexBuffer;
VulkanBuffer<std::uint32_t, true> indexBuffer;
// Non-mapped (device-local) RT geometry: VulkanBuffer::UploadDeviceLocal
// places these in device memory and picks direct-map vs staged-copy per
// the #89 upload strategy, so they live in VRAM for hit-shader fetch and
// BLAS-build/refit reads instead of being read from system RAM over PCIe
// every trace (#73). The compressed Build path allocates them pure
// DEVICE_LOCAL and lets the GPU decompressor fill them directly.
VulkanBuffer<Vector<float, 3, 3>, false> vertexBuffer;
VulkanBuffer<std::uint32_t, false> indexBuffer;
// AABB build input for the procedural path (BuildProcedural).
// Lifetime contract matches vertexBuffer/indexBuffer: must stay
// alive until the build submitted on `cmd` completes.
VulkanBuffer<RTAabb, true> aabbBuffer;
VulkanBuffer<RTAabb, false> aabbBuffer;
// Lives until the cmd buffer issued by the compressed Build path
// completes execution. Kept as a member so the recorded
// vkCmdDecompressMemoryEXT references valid memory until the queue