perf(rt): allocate TLAS metadata buffer in BAR/VRAM, not system RAM (#75) #111

Merged
catbot merged 1 commit from claude/issue-75 into master 2026-06-17 19:50:55 +02:00
Member

What

The TLAS metadataBuffer was allocated HOST_VISIBLE | HOST_COHERENT (system RAM). It is CPU-written every frame (one userMetadata word per element in BuildTLAS's copy loop) and read by the ray shaders as a STORAGE_BUFFER via device address every frame — so every per-frame shader read traversed PCIe, the same cost the sibling instance buffer paid before #65.

This upgrades the request to HOST_VISIBLE with a best-effort DEVICE_LOCAL preference (BAR/VRAM), keeping the single persistently-mapped buffer. CPU writes go to write-combined BAR (write-only, sequential, never-read-back — the ideal load); shader reads now hit local VRAM.

Correctness

  • DEVICE_LOCAL is a preference only (depends on #59): GetMemoryType falls back to plain HOST_VISIBLE (the previous behaviour) on GPUs without resizable BAR.
  • No staging (caveat 2): the buffer is rewritten by the CPU every frame, so a per-frame stage+copy+barrier would defeat the purpose. The direct upgrade is unconditional — unlike the instance buffer there is no GPU co-write (caveat 4).
  • HOST_COHERENT dropped (caveat 3): the combined type may not be coherent. A FlushDevice() is added after the copy loop to make host writes available on a non-coherent type; it self-gates to a no-op on a coherent type (#60), preserving the previous behaviour wherever the type ends up coherent. The metadata buffer is not an AS build input and has no GPU co-write, so it needs neither the build-stage barrier the instance buffer takes nor a HOST→shader command barrier — the queue submit already orders host writes (made available before it) against every command in the submission, exactly as it did when the buffer was HOST_COHERENT.

Testing

TLASHighWaterMark gains a metadata-buffer assertion block mirroring #65's instance-buffer check: the chosen memory type must equal what GetMemoryType resolves for the HOST_VISIBLE | prefer-DEVICE_LOCAL request, and must be DEVICE_LOCAL wherever a host-visible device-local type is reachable. The zero-validation-errors check covers the dropped HOST_COHERENT path. This test passes on the RTX 4090.

crafter-build test: 19 of 20 green, including this PR's TLASHighWaterMark. The one red — MeshDecompressStagingRelease — is a pre-existing failure on master (reproduced via git stash on a clean tree), unrelated to this change (it tests Mesh decompress staging, this PR touches only RenderingElement3D). Filed as #110.

Resolves #75.

## What The TLAS `metadataBuffer` was allocated `HOST_VISIBLE | HOST_COHERENT` (system RAM). It is CPU-written every frame (one `userMetadata` word per element in `BuildTLAS`'s copy loop) and read by the ray shaders as a `STORAGE_BUFFER` via device address every frame — so every per-frame shader read traversed PCIe, the same cost the sibling instance buffer paid before #65. This upgrades the request to `HOST_VISIBLE` with a best-effort `DEVICE_LOCAL` preference (BAR/VRAM), keeping the single persistently-mapped buffer. CPU writes go to write-combined BAR (write-only, sequential, never-read-back — the ideal load); shader reads now hit local VRAM. ## Correctness - **`DEVICE_LOCAL` is a preference only** (depends on #59): `GetMemoryType` falls back to plain `HOST_VISIBLE` (the previous behaviour) on GPUs without resizable BAR. - **No staging** (caveat 2): the buffer is rewritten by the CPU *every frame*, so a per-frame stage+copy+barrier would defeat the purpose. The direct upgrade is unconditional — unlike the instance buffer there is no GPU co-write (caveat 4). - **`HOST_COHERENT` dropped** (caveat 3): the combined type may not be coherent. A `FlushDevice()` is added after the copy loop to make host writes available on a non-coherent type; it self-gates to a no-op on a coherent type (#60), preserving the previous behaviour wherever the type ends up coherent. The metadata buffer is not an AS build input and has no GPU co-write, so it needs neither the build-stage barrier the instance buffer takes nor a HOST→shader command barrier — the queue submit already orders host writes (made available before it) against every command in the submission, exactly as it did when the buffer was `HOST_COHERENT`. ## Testing `TLASHighWaterMark` gains a metadata-buffer assertion block mirroring #65's instance-buffer check: the chosen memory type must equal what `GetMemoryType` resolves for the `HOST_VISIBLE | prefer-DEVICE_LOCAL` request, and must be `DEVICE_LOCAL` wherever a host-visible device-local type is reachable. The zero-validation-errors check covers the dropped `HOST_COHERENT` path. This test passes on the RTX 4090. `crafter-build test`: **19 of 20 green, including this PR's `TLASHighWaterMark`.** The one red — `MeshDecompressStagingRelease` — is a **pre-existing failure on `master`** (reproduced via `git stash` on a clean tree), unrelated to this change (it tests `Mesh` decompress staging, this PR touches only `RenderingElement3D`). Filed as #110. Resolves #75.
The TLAS metadata buffer is CPU-written every frame (one userMetadata word
per element in BuildTLAS's copy loop) and read by the ray shaders as a
STORAGE_BUFFER via device address every frame. Allocated
HOST_VISIBLE | HOST_COHERENT (system RAM), every per-frame shader read
traversed PCIe — the same cost the sibling instance buffer paid before #65.

Upgrade the request to HOST_VISIBLE with a best-effort DEVICE_LOCAL preference
(BAR/VRAM), keeping the single persistently-mapped buffer. The CPU's writes are
write-only, sequential, never-read-back — the ideal write-combined BAR load —
and the shader reads now hit local VRAM.

Unlike the instance buffer there is no GPU co-write, so the direct upgrade is
unconditional. Staging is still deliberately avoided: this buffer is rewritten
by the CPU every frame, so a per-frame stage+copy+barrier would defeat the
purpose (#75 caveat 2).

Correctness:
- DEVICE_LOCAL is a preference only (depends on #59): GetMemoryType falls back
  to plain HOST_VISIBLE (the previous behaviour) on GPUs without resizable BAR.
- HOST_COHERENT is dropped from the required set — the combined type may not be
  coherent. A FlushDevice() is added after the copy loop to make the host writes
  available on a non-coherent type; it self-gates to a no-op on a coherent type
  (#60), so the previous behaviour is preserved wherever the type ends up
  coherent. The metadata buffer is not an AS build input and has no GPU
  co-write, so it needs neither the build-stage barrier the instance buffer
  takes nor a HOST->shader command barrier: the queue submit already orders host
  writes made available before it against every command in the submission,
  exactly as it did when this buffer was HOST_COHERENT.

Extends the TLASHighWaterMark hardware test with a metadata-buffer assertion
block mirroring #65's instance-buffer check: the chosen memory type must equal
what GetMemoryType resolves for the HOST_VISIBLE | prefer-DEVICE_LOCAL request,
and must be DEVICE_LOCAL wherever a host-visible device-local type is reachable.
The zero-validation-errors check covers the dropped HOST_COHERENT path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
catbot merged commit 1582b6ceb5 into master 2026-06-17 19:50:55 +02:00
catbot deleted branch claude/issue-75 2026-06-17 19:50:56 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Catcrafts/Crafter.Graphics!111
No description provided.