fix(rt): split Sponza's combined image sampler into a heap texture and sampler
examples/Sponza no longer built natively (#164). Its closest-hit declared a descriptor_heap sampler2D, which glslang has rejected since c8253623 ("Reject descriptor_heap combined image samplers"), and main.cpp wrote it through VkResourceDescriptorDataEXT::pCombinedImageSampler, which that union no longer has: the descriptor heap has no combined image samplers. Both went unnoticed because nothing builds the examples. Sponza - closesthit.glsl declares texture2D and sampler heap arrays and joins them where it samples, as ui-shared.glsl and HeapDynamicIndexHit do. The sampler slot is a second spec constant beside the image slot, so the shader assumes nothing about where the host put it, and a constant index keeps the example running on NVIDIA 610.43.02 (see its README). The comment no longer says the image slot is offset by bufferStartElement; only buffer slots are. - main.cpp gives the heap a sampler slot, writes the albedo as a SAMPLED_IMAGE from a view create-info, and writes the sampler into the sampler heap from a VkSamplerCreateInfo with vkWriteSamplerDescriptorsEXT, as UIRenderer::RegisterSampler does. SamplerVulkan's VkSampler had no use left. The sampler keeps its linear filtering and repeat addressing and drops anisotropy, which a hit shader cannot use: it samples at LOD 0, without derivatives. Docs - README.md and examples/Sponza/README.md quote the new declarations, and the Sponza README says why the texture and the sampler are two descriptors. Its texture2DArray materials pattern declared a descriptor_heap sampler2DArray, which glslang rejects as well; it now joins a texture2DArray and a sampler. - examples/SponzaBench/README.md drops its caveat that Sponza does not compile. Tests - ExampleShaders compiles every GLSL shader under examples/ with glslang as crafter-build does for the example (Vulkan rules, SPIR-V 1.4, shaders/ on the include path, and the descriptor-set build where the example registers one), so a glslang that starts rejecting one fails a test rather than an example build nobody runs. It fails on Sponza's old closest-hit and needs glslang only. Closes #164. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c97a3048fb
commit
6e94cc6209
7 changed files with 230 additions and 40 deletions
|
|
@ -14,9 +14,16 @@ it via ray tracing on both Vulkan (native) and WebGPU (wasm). Same
|
|||
at the barycentric attribs as UVs — proof-of-binding, not visually
|
||||
accurate. Per-vertex UV interpolation is the next step.
|
||||
|
||||
The closest-hit reads its texture through a **spec constant**
|
||||
(`albedo[albedoSlot]`), not a runtime index. That is deliberate — see
|
||||
below.
|
||||
The closest-hit reads its texture and its sampler through **spec
|
||||
constants** (`textures[albedoSlot]`, `samplers[samplerSlot]`), not a
|
||||
runtime index. That is deliberate — see below.
|
||||
|
||||
On Vulkan the texture and the sampler are separate descriptors: a sampled
|
||||
image in the resource heap and a sampler in the sampler heap, joined where
|
||||
the closest-hit samples them,
|
||||
`texture(sampler2D(textures[albedoSlot], samplers[samplerSlot]), uv)`.
|
||||
The descriptor heap has no combined image samplers, and glslang rejects a
|
||||
`descriptor_heap` `sampler2D` (#164).
|
||||
|
||||
## Native RT limitation: dynamic `descriptor_heap` indexing in hit shaders
|
||||
|
||||
|
|
@ -44,7 +51,8 @@ closest-hit:
|
|||
|
||||
- A closest-hit that reads only `lightHeap[lightSlot]` where `lightSlot`
|
||||
is a **spec constant** survives indefinitely. ✅ (This example's
|
||||
`albedo[albedoSlot]` is exactly this case.)
|
||||
`textures[albedoSlot]` and `samplers[samplerSlot]` are exactly this
|
||||
case.)
|
||||
- Reading `indexHeap[assetIndexStart + gl_InstanceCustomIndexEXT]` /
|
||||
`vertexHeap[...]` — a heap index offset by a **runtime** value —
|
||||
device-losts on the first geometry hit. ❌
|
||||
|
|
@ -116,11 +124,14 @@ indexing the heap:
|
|||
slot and index by **layer**:
|
||||
|
||||
```glsl
|
||||
// ✅ one array texture (spec-constant slot), dynamic layer index
|
||||
// ✅ one array texture (spec-constant slots), dynamic layer index
|
||||
layout(constant_id = 1) const uint16_t materialArraySlot = 0us;
|
||||
layout(descriptor_heap) uniform sampler2DArray materials[];
|
||||
layout(constant_id = 2) const uint16_t samplerSlot = 0us;
|
||||
layout(descriptor_heap) uniform texture2DArray materials[];
|
||||
layout(descriptor_heap) uniform sampler samplers[];
|
||||
uint layer = materialLayer[gl_InstanceCustomIndexEXT]; // from a bound SSBO
|
||||
vec3 albedo = texture(materials[materialArraySlot], vec3(uv, layer)).rgb;
|
||||
vec3 albedo = texture(sampler2DArray(materials[materialArraySlot], samplers[samplerSlot]),
|
||||
vec3(uv, layer)).rgb;
|
||||
```
|
||||
|
||||
This is precisely what the WebGPU path already does — bucketed texture
|
||||
|
|
|
|||
Loading…
Reference in a new issue