Vulkan: Mesh::BuildProcedural — AABB (procedural) BLAS build path to match WebGPU #33

Closed
opened 2026-06-12 13:39:21 +02:00 by jorijnvdgraaf · 0 comments

3DForts grass (3DForts/3DForts#161) is switching from any-hit alpha cards to per-patch procedural AABBs with intersection-shader-marched volumetric blades. The WebGPU backend already ships everything this needs; the native Vulkan backend is missing exactly one piece: an AABB BLAS build path.

Current state

  • RTShaderGroupType::ProceduralHitGroup exists and mirrors VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_KHR, with RTShaderGroup::intersectionShader documented as "only consulted for ProceduralHitGroup" (interfaces/Crafter.Graphics-RT.cppm).
  • Vulkan pipeline/SBT side needs nothing: apps pass raw VkRayTracingShaderGroupCreateInfoKHR spans to PipelineRT::Init (interfaces/Crafter.Graphics-PipelineRTVulkan.cppm:57), so a procedural group + intersection shader stage already flows through today.
  • WebGPU is complete: Mesh::BuildProcedural(std::span<const RTAabb>, bool opaque, cmd) (interfaces/Crafter.Graphics-Mesh.cppm:143-146, impl in Crafter.Graphics-Mesh-WebGPU.cpp), the WebGPURTStage::Intersection stage with contract fn(ray: RayDesc, aabbMin: vec3<f32>, aabbMax: vec3<f32>, primitiveId: u32) -> IntersectionResult (interfaces/Crafter.Graphics-ShaderBindingTableWebGPU.cppm:26-33), and the traversal's geomType==1 AABB-leaf branch in additional/dom-webgpu.js (~1830).
  • The gap: the Vulkan Mesh::Build hardcodes VK_GEOMETRY_TYPE_TRIANGLES_KHR (implementations/Crafter.Graphics-Mesh.cpp:62) and there is no AABB overload — a procedural BLAS cannot be built natively.

Requested

A Vulkan Mesh::BuildProcedural, API-symmetric with the WebGPU declaration:

void BuildProcedural(std::span<const RTAabb> aabbs,
                     bool                    opaque = false,
                     VkCommandBuffer         cmd);
  • Upload the boxes as a 24-byte-stride (VkAabbPositionsKHR-compatible — RTAabb already matches) device buffer with VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | SHADER_DEVICE_ADDRESS.
  • Build the BLAS from one VkAccelerationStructureGeometryAabbsDataKHR geometry (VK_GEOMETRY_TYPE_AABBS_KHR); opaque maps to VK_GEOMETRY_OPAQUE_BIT_KHR (default false so any-hit/non-opaque semantics match the WebGPU path).
  • Same scratch/lifetime handling as the existing triangle build; no TLAS/instance changes needed (VkAccelerationStructureInstanceKHR is geometry-type-agnostic).

Acceptance

An instance of a procedural mesh bound to a PROCEDURAL_HIT_GROUP_KHR group invokes its GLSL intersection shader (reportIntersectionEXT + hitAttributeEXT) once per entered box, and the committed hit shades through the group's closest-hit — i.e. the WebGPU procedural example behavior reproduced natively. Porting that example to the Vulkan backend would make a good regression test.

3DForts grass (3DForts/3DForts#161) is switching from any-hit alpha cards to **per-patch procedural AABBs with intersection-shader-marched volumetric blades**. The WebGPU backend already ships everything this needs; the native Vulkan backend is missing exactly one piece: an AABB BLAS build path. ## Current state - `RTShaderGroupType::ProceduralHitGroup` exists and mirrors `VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_KHR`, with `RTShaderGroup::intersectionShader` documented as "only consulted for ProceduralHitGroup" (`interfaces/Crafter.Graphics-RT.cppm`). - **Vulkan pipeline/SBT side needs nothing**: apps pass raw `VkRayTracingShaderGroupCreateInfoKHR` spans to `PipelineRT::Init` (`interfaces/Crafter.Graphics-PipelineRTVulkan.cppm:57`), so a procedural group + intersection shader stage already flows through today. - **WebGPU is complete**: `Mesh::BuildProcedural(std::span<const RTAabb>, bool opaque, cmd)` (`interfaces/Crafter.Graphics-Mesh.cppm:143-146`, impl in `Crafter.Graphics-Mesh-WebGPU.cpp`), the `WebGPURTStage::Intersection` stage with contract `fn(ray: RayDesc, aabbMin: vec3<f32>, aabbMax: vec3<f32>, primitiveId: u32) -> IntersectionResult` (`interfaces/Crafter.Graphics-ShaderBindingTableWebGPU.cppm:26-33`), and the traversal's `geomType==1` AABB-leaf branch in `additional/dom-webgpu.js` (~1830). - **The gap**: the Vulkan `Mesh::Build` hardcodes `VK_GEOMETRY_TYPE_TRIANGLES_KHR` (`implementations/Crafter.Graphics-Mesh.cpp:62`) and there is no AABB overload — a procedural BLAS cannot be built natively. ## Requested A Vulkan `Mesh::BuildProcedural`, API-symmetric with the WebGPU declaration: ```cpp void BuildProcedural(std::span<const RTAabb> aabbs, bool opaque = false, VkCommandBuffer cmd); ``` - Upload the boxes as a 24-byte-stride (`VkAabbPositionsKHR`-compatible — `RTAabb` already matches) device buffer with `VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | SHADER_DEVICE_ADDRESS`. - Build the BLAS from one `VkAccelerationStructureGeometryAabbsDataKHR` geometry (`VK_GEOMETRY_TYPE_AABBS_KHR`); `opaque` maps to `VK_GEOMETRY_OPAQUE_BIT_KHR` (default false so any-hit/non-opaque semantics match the WebGPU path). - Same scratch/lifetime handling as the existing triangle build; no TLAS/instance changes needed (`VkAccelerationStructureInstanceKHR` is geometry-type-agnostic). ## Acceptance An instance of a procedural mesh bound to a `PROCEDURAL_HIT_GROUP_KHR` group invokes its GLSL intersection shader (`reportIntersectionEXT` + `hitAttributeEXT`) once per entered box, and the committed hit shades through the group's closest-hit — i.e. the WebGPU procedural example behavior reproduced natively. Porting that example to the Vulkan backend would make a good regression test.
Sign in to join this conversation.
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#33
No description provided.