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:
parent
39b882a9cb
commit
5f858509c8
4 changed files with 161 additions and 23 deletions
|
|
@ -132,6 +132,15 @@ int main() {
|
|||
// refit below can reuse it (build scratch ≥ update scratch, no resize).
|
||||
Check(tri.scratchBuffer.buffer != VK_NULL_HANDLE,
|
||||
"allowUpdate=true → scratch retained for refit (issue #66)");
|
||||
// Geometry is placed in device-local memory regardless of the upload
|
||||
// strategy taken (issue #73): the direct ReBAR/UMA path lands a
|
||||
// HOST_VISIBLE | DEVICE_LOCAL type, the staged path a pure DEVICE_LOCAL
|
||||
// one — either way the DEVICE_LOCAL bit is set, so the BLAS build and any
|
||||
// hit-shader fetch read from VRAM, not system RAM.
|
||||
Check((tri.vertexBuffer.memoryPropertyFlagsChosen & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0,
|
||||
"triangle vertexBuffer placed in device-local memory (#73)");
|
||||
Check((tri.indexBuffer.memoryPropertyFlagsChosen & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0,
|
||||
"triangle indexBuffer placed in device-local memory (#73)");
|
||||
|
||||
const VkDeviceAddress triAddrBefore = tri.blasAddr;
|
||||
const VkAccelerationStructureKHR triHandleBefore = tri.accelerationStructure;
|
||||
|
|
@ -198,6 +207,8 @@ int main() {
|
|||
Check(proc.builtPrimitiveCount == 2, "procedural BLAS reports 2 primitives");
|
||||
Check(proc.scratchBuffer.buffer != VK_NULL_HANDLE,
|
||||
"procedural allowUpdate=true → scratch retained for refit (issue #66)");
|
||||
Check((proc.aabbBuffer.memoryPropertyFlagsChosen & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0,
|
||||
"procedural aabbBuffer placed in device-local memory (#73)");
|
||||
|
||||
const VkDeviceAddress procAddrBefore = proc.blasAddr;
|
||||
const VkAccelerationStructureKHR procHandleBefore = proc.accelerationStructure;
|
||||
|
|
@ -293,6 +304,45 @@ int main() {
|
|||
Check(devProc.aabbBuffer.buffer == VK_NULL_HANDLE,
|
||||
"device-buffer refit still never touched the host aabbBuffer");
|
||||
|
||||
// ── Force the STAGED upload path (issue #73). On this ReBAR dev host the
|
||||
// direct map path is normally taken; temporarily zero the cached upload
|
||||
// budget so PreferDirectDeviceWrite returns false, exercising Mesh's
|
||||
// pure-DEVICE_LOCAL + transient-staging-buffer + vkCmdCopyBuffer path
|
||||
// (and its deferred-deletion of the staging buffer). The validation
|
||||
// layer check below is the real assertion that the staged copy and its
|
||||
// barriers are spec-correct on a fresh build and an in-place refit. ───
|
||||
{
|
||||
const VkDeviceSize savedBudget = Device::directWriteBudget;
|
||||
Device::directWriteBudget = 0; // 0 → PreferDirectDeviceWrite is always false
|
||||
|
||||
Mesh staged;
|
||||
auto verts = CubeVerts(1.0f);
|
||||
auto idx = CubeIndices();
|
||||
{
|
||||
VkCommandBuffer cmd = BeginCmd();
|
||||
staged.Build(verts, idx, cmd, RTBuildOptions{
|
||||
.preference = RTBuildPreference::FastTrace, .allowUpdate = true });
|
||||
SubmitWait(cmd);
|
||||
}
|
||||
Check(staged.blasAddr != 0, "staged-upload Build produced a non-zero blasAddr (#73)");
|
||||
// Staged geometry lives in pure DEVICE_LOCAL — no HOST_VISIBLE bit.
|
||||
Check((staged.vertexBuffer.memoryPropertyFlagsChosen & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0,
|
||||
"staged vertexBuffer is device-local-only, not host-visible (#73)");
|
||||
|
||||
const VkAccelerationStructureKHR stagedHandle = staged.accelerationStructure;
|
||||
{
|
||||
// Same topology, deformed positions → in-place UPDATE, re-staged.
|
||||
auto verts2 = CubeVerts(1.5f);
|
||||
VkCommandBuffer cmd = BeginCmd();
|
||||
staged.Refit(verts2, idx, cmd);
|
||||
SubmitWait(cmd);
|
||||
}
|
||||
Check(staged.accelerationStructure == stagedHandle,
|
||||
"staged-upload Refit kept the same AS handle (in-place UPDATE, #73)");
|
||||
|
||||
Device::directWriteBudget = savedBudget;
|
||||
}
|
||||
|
||||
Check(Device::validationErrorCount == 0,
|
||||
std::format("no Vulkan validation errors ({} seen)", Device::validationErrorCount));
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue