# VulkanTriangle

The minimal ray-traced example. Renders a single static triangle through
`vkCmdTraceRaysKHR`. No UI.

## What it shows

- `Device::Initialize()` + `Window` + swapchain bring-up.
- A `DescriptorHeapVulkan` sized for one image + one buffer slot, with
  slot ranges allocated via the bump-allocator API
  (`AllocateImageSlots`, `AllocateBufferSlots`).
- A `PipelineRTVulkan` built from raygen / miss / closesthit SPIR-V
  shaders compiled at build time.
- `Mesh::Build` constructing a BLAS and `RenderingElement3D::BuildTLAS`
  the per-frame TLAS.
- Direct descriptor writes via `vkWriteResourceDescriptorsEXT` for the
  swapchain views and TLAS device addresses.
- `RTPass{&pipeline}` plugged into `window.passes` — the canonical
  way to add ray tracing to a window in this library.

It's the smallest sensible test of the bindless `VK_EXT_descriptor_heap`
+ ray-tracing path.

## Run

```bash
cd examples/VulkanTriangle
crafter-build -r
```

You should see a 1280×720 window with an RGB-barycentric triangle filling
roughly the centre. On the NVIDIA driver this works through an engine-side
workaround for a driver fault — see below.

## Native status — NVIDIA driver fault, worked around

On NVIDIA driver `610.43.02` (Vulkan 1.4) reading the acceleration
structure through `VK_EXT_descriptor_heap` aborts the device with
`VK_ERROR_DEVICE_LOST` on the first frame. This is a **driver-side fault**
in the brand-new descriptor-heap acceleration-structure path, not an engine
bug (full investigation in #7, summarised below).

**The engine works around it transparently** (issue #15). On the NVIDIA
proprietary driver only, `VulkanShader` rewrites the compiled SPIR-V at
module-load time so that every `OpLoad` of an `accelerationStructureEXT`
out of the heap becomes a load of the TLAS *device address* (from a
push-constant block) followed by
`OpConvertUToAccelerationStructureKHR` — which reads no descriptor and so
never touches the faulting path. `RTPass` feeds the active frame's TLAS
address in as push data. SPIR-V allows only one push-constant block per
entry point, so when a shader already declares one the TLAS address is
appended to *that* block (rather than adding a second, which would fail
validation — issue #18); shaders without a push constant get a freshly
synthesized single-member block. `raygen.glsl` and the example code are
unchanged; acceleration structures still bind into the heap normally. On
every other driver the workaround is inert. It's gated on
`Device::workaroundDescriptorHeapAS` and confined to one fenced block in
`interfaces/Crafter.Graphics-ShaderVulkan.cppm` so it can be deleted wholesale
once a fixed NVIDIA driver ships.

### The underlying fault (#7)

The fault was traced to the **acceleration-structure read through
`VK_EXT_descriptor_heap`**, *not* to the engine's RT setup:

- The BLAS/TLAS build is correct and finishes before rendering
  (`Window::FinishInit` does `vkQueueWaitIdle`). The built TLAS instance
  has an identity transform, `mask = 0xFF`, and the correct BLAS device
  address.
- The AS descriptor is written correctly — `vkWriteResourceDescriptorsEXT`
  stores the TLAS device address at the expected heap byte offset (verified
  by dumping the raw heap bytes after the write).
- The Khronos validation layers (1.4.350, current) report **zero** errors
  for the whole frame, including the SBT regions handed to
  `vkCmdTraceRaysKHR`.
- Storage images and buffers bound through the **same** descriptor heap
  work — with `traceRayEXT` removed, the raygen shader's `imageStore`
  renders correctly, so the heap binding / image path is sound.
- Both the ray-tracing pipeline (`traceRayEXT`) **and** inline ray query
  (`rayQueryEXT`, which uses no shader binding table) fault identically
  when they read the acceleration structure from the heap. That isolates
  the fault to the AS-via-heap read, not the SBT or the RT pipeline.
- The fault reproduces even with the AS descriptor written at heap byte 0
  and read at shader index 0 (no descriptor offset/stride ambiguity), and
  is unaffected by the `pAddressRange` size.
- `VK_EXT_descriptor_heap` is brand new; on this machine NVIDIA is the only
  implementation that advertises it (llvmpipe does not), so there is no
  second conformant implementation to cross-check against.

**Conclusion:** this is a driver-side fault in NVIDIA's
`VK_EXT_descriptor_heap` acceleration-structure path, not an engine bug, and
it should be reported to NVIDIA. Until a fixed driver ships, the SPIR-V
rewrite above keeps the native RT path working; once it does, remove the
workaround and the heap AS read becomes the direct path again.
