Mesh::Refit re-uploaded the entire vertex array every frame on the
in-place UPDATE path (full host write + flush + barrier on the direct
path; full re-stage + copy on the staged path), even when only a handful
of vertices moved.
Add VulkanBuffer::UploadDeviceLocalRange — a dirty-range counterpart to
UploadDeviceLocal that writes/flushes/stages/copies and barriers only the
half-open element range [offset, offset+count) of an already-allocated
device-local buffer. It picks direct-map vs staged-copy from the memory
type the buffer was actually allocated with (not the sub-range size, which
PreferDirectDeviceWrite would mis-route), and the direct path's ranged
flush is rounded to nonCoherentAtomSize and clamped to the allocation size
(mappedSize is now recorded for every buffer, not just mapped ones).
Add a dirty-range Refit overload taking the full vertex/index arrays plus
a (dirtyVertexOffset, dirtyVertexCount) window. The full-span Refit now
delegates to it with the whole array as the window. On the in-place UPDATE
path only the declared window is uploaded — the rest of the device buffer
retains last refit's positions; when an UPDATE isn't possible it falls
back to the full-span rebuild, which is why the full arrays are still
passed. The WebGPU/DOM backend keeps API symmetry: it has no hardware AS,
so it ignores the window and rebuilds the host BVH from the full geometry.
BLASBuildOptions exercises the dirty-range refit on the direct path, the
staged path, and the count-change rebuild fallback, asserting AS-handle /
blasAddr stability and zero Vulkan validation errors.
Resolves#119
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add zero-copy procedural BLAS overloads that take the AABB build input
straight from an existing device buffer instead of memcpy-ing a host span
through Mesh::aabbBuffer — the input-source companion to #36's refit work.
A GPU compute producer (e.g. a GPU-resident particle system) can now feed a
moving procedural BLAS that builds/refits each frame with no host round-trip.
Vulkan: new BuildProcedural(VkDeviceAddress, count, ...) and
RefitProcedural(VkDeviceAddress, count, ...) feed the device address directly
into VkAccelerationStructureGeometryAabbsDataKHR; the host-span path is
refactored to share RecordProceduralBuildFromAddress and is otherwise
unchanged.
WebGPU: device-buffer BuildProcedural/RefitProcedural copy the boxes GPU->GPU
into the mesh heap (new wgpuRegisterMeshBLASDeviceAabbs / wgpuRefitMeshBLAS-
DeviceAabbs bridge fns) and wrap them in a single root leaf bounded by a
caller-supplied worldBounds — no wasm round-trip, blasAddr stable across refit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mesh::Build / BuildProcedural now take an RTBuildOptions { preference,
allowUpdate }. `preference` maps to PREFER_FAST_TRACE (default) or
PREFER_FAST_BUILD; `allowUpdate` sets ALLOW_UPDATE so the BLAS can be
refit later.
Adds Mesh::Refit / RefitProcedural: when the original build opted into
allowUpdate and the topology is unchanged, they record an in-place
UPDATE-mode build (src == dst) — much cheaper than a rebuild, and the
AS handle + blasAddr are preserved so TLAS instances stay valid. They
fall back to a full rebuild otherwise. The shared build tail also now
destroys a stale AS handle on re-Build (previously leaked) and guards
the in-place update with an AS read→write barrier.
The portable RTBuildPreference/RTBuildOptions types and Refit methods
also exist on the WebGPU backend for API symmetry; the software BVH has
no hardware AS, so the preference is a no-op and a "refit" rebuilds the
BVH (re-registering the handle).
Also adds Device::validationErrorCount, bumped by the debug-messenger
callback on ERROR-severity messages, so tests can assert a Vulkan
operation produced no validation errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PipelineRTWebGPU emits a runIntersection mega-switch and the
RT_HAS_ANYHIT / RT_HAS_INTERSECTION consts (+ the @CRAFTER_RT_TRACE_USER
marker) that gate the library's new TRACE-stage user callbacks, so an
opaque triangle-only scene still const-folds them away. Mesh-WebGPU
builds a SAH BVH2 over AABB primitives and uploads them in primitive
order for the intersection shader to fetch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>