| Filename | Latest commit message | Latest commit date |
|---|---|---|
DescriptorHeapVulkan - The buffer region started at images * imageDescriptorSize / bufferDescriptorSize, rounded down. A descriptor_heap array indexes the heap from byte 0 in strides of its descriptor size, so buffer slot 0 has to sit on a whole buffer descriptor past the images. Where the image descriptors were not a whole number of buffer descriptors, the first buffer descriptors overwrote the last image descriptors. Where they were less than one, the start was bumped to descriptor 1, but the heap was still sized as if the buffers began right after the images, so the last buffer descriptors went into the implementation's reserved range. Every descriptor size is a power of two, so both cases need image descriptors smaller than buffer descriptors, which NVIDIA (32 and 16 bytes) and llvmpipe (64 and 16) do not have. The start now rounds up and the heap is sized from it (#163). - The reserved range sat at a multiple of imageDescriptorAlignment only, where vkCmdBindResourceHeapEXT also wants bufferDescriptorAlignment (VUID-vkCmdBindResourceHeapEXT-pBindInfo-11435 and -11436). It now starts on a multiple of the larger. The buffer descriptors need no more than the rounding: each alignment is at most its descriptor size, so a whole number of descriptors is aligned. - bufferStartElement is 16 bits and took the division unchecked, so an image region past 65535 buffer descriptors (16384 images on llvmpipe) wrapped the buffer region around onto the images. Slot counts whose buffer indices do not fit 16 bits, 0xFFFF being the empty slot, now throw, naming the counts. A heap without buffer slots has no buffer region, which starts at 0 as in descriptor-set mode, so image-only heaps have no index to fit. - On a driver that reserves nothing (llvmpipe), a heap without sampler slots was a zero-size buffer, which every Initialize(1, 1, 0) made, and Window::Render bound its address each frame. Each heap now holds at least one descriptor, as each binding does in descriptor-set mode. That grows NVIDIA's sampler heap by one descriptor when no sampler slots are asked for; the resource heaps of the slot counts in the tree keep their layouts byte for byte. - ComputeLayout holds the arithmetic, pure over the descriptor-heap properties, and Initialize goes through it. GetBufferOffset and GetBufferOffsetElement, which had their own copies of the old arithmetic and never had a caller, are gone. - Bind(cmd, frame) binds a frame's heaps with the reserved ranges Initialize stored, or in descriptor-set mode the frame's descriptor set. Window::Render and the RTVolume example worked the reserved-range offset out from the heap's size with the image-alignment mask, and call Bind now. The window's per-heap cache of bind structs goes with it: keyed on the heap pointer, it kept binding the old buffers after a re-Initialize that reallocated them. The heap still ends with its reserved range, so code that derives the offset from the size keeps getting the right one. Tests - DescriptorHeapLayout: the issue's two layouts, the reserved range where buffer descriptors are aligned more strictly than image ones, every legal combination of descriptor sizes and alignments up to 256 bytes, no heap empty, the NVIDIA and llvmpipe layouts, the 16-bit limit and image-only heaps. Pure CPU, no device. Against the old arithmetic it fails 12 of its 18 checks. Docs - README.md's heap bullet and AGENTS.consumer.md say how to bind the heap outside the frame loop, and the consumer notes that Initialize throws when buffer indices would pass 16 bits. AGENTS.md lists ComputeLayout among the functions the pure-logic tests drive. Closes #163. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
| .. | ||
| aabbs.comp.glsl | ||
| aabbs.comp.wgsl | ||
| anyhit.glsl | ||
| anyhit.wgsl | ||
| closesthit.glsl | ||
| closesthit.wgsl | ||
| intersection.glsl | ||
| intersection.wgsl | ||
| main.cpp | ||
| miss.glsl | ||
| miss.wgsl | ||
| project.cpp | ||
| raygen.glsl | ||
| raygen.wgsl | ||
| README.md | ||
| resolve.wgsl | ||
RTVolume
Ray tracing of procedural (AABB) geometry with an any-hit cut-out on both backends — software WebGPU (issue #13) and native Vulkan hardware RT (issue #33).
A 3×3×3 grid of unit boxes is registered as an AABB BLAS
(Mesh::BuildProcedural — VK_GEOMETRY_TYPE_AABBS_KHR on Vulkan, the
software AABB-leaf path on WebGPU). The hit group is procedural
(RTShaderGroupType::ProceduralHitGroup /
VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_KHR) carrying:
intersection.wgsl/intersection.glsl— analytic ray–sphere test that turns each box into a radius-1 sphere (runs once per box the ray enters);anyhit.wgsl/anyhit.glsl— ignores the intersection for half the cells of a spherical checkerboard, so the ray passes through and the background / spheres behind show through (the visible proof any-hit runs);closesthit.wgsl/closesthit.glsl— normal-based Lambert shading, tinted per instance.
The geometry is registered non-opaque and the instances clear their
force-opaque flag, which is what lets the any-hit shader run. Flip the
instance flag to kRTGeometryInstanceForceOpaque (or build the mesh with
opaque = true) to skip any-hit and see solid spheres.
WebGPU/DOM (free camera, WASD + mouse):
crafter-build --target=wasm32-wasip1 -r
Native Vulkan (fixed camera in raygen.glsl):
crafter-build -r