Skip to content

[EPIC]: Bring-up and test coverage for PSO-based raytracing #1268

Description

@MarijnS95

Companion to the inline-RT epic in #1258. That issue is scoped to RayQuery / TraceRayInline inside a regular compute shader; the test framework already treats it as a Compute pipeline that happens to bind an acceleration structure. PSO-based raytracing is a different beast — it introduces an entirely new pipeline kind, new shader stages, a shader binding table, and the DispatchRays command. The framework has none of that today, so this issue tracks both the bring-up work and the shader-visible test surface that becomes unlockable as bring-up progresses.

Reuses the AS YAML / abstraction layers from the inline-RT bring-up (#1214, #1232, #1245), so this work starts above that line.

Legend:

  • Not started
  • In progress 🏗️
  • In review 👀
  • Finished (merged in main)
  • 👍 testable on top of the bring-up below
  • 🏗️ requires additional framework work first

Bring-up PRs (foundation for everything below)

Framework bring-up

Pipeline / stage model

  • Add ShaderPipelineKind::RayTracing alongside Compute / TraditionalRaster / MeshShaderRaster in Support/Pipeline.h 👀
  • Add new Stages: RayGeneration, Miss, ClosestHit, AnyHit, Intersection, Callable 👀
  • Extend validatePipelineKind to require ≥1 raygen entry and at least one of {miss, hit-group} when the pipeline is RT 👀
  • YAML mapping for the new stages and pipeline kind 👀

Shader compilation

  • Add a lib_6_3 (or lib_6_5) target so clang-dxc / dxc emits a DXIL library with multiple entry points
  • %dxc_target_lib substitution analogous to %dxc_target 👀
  • Lit feature raytracing-pipeline (mirrors acceleration-structure) gated on the device feature + library-target support 👀

Hit groups, payloads, attributes, recursion config

  • YAML HitGroups: list (Name, Type: Triangles|Procedural, ClosestHit, optional AnyHit, optional Intersection) 👀
  • YAML RayTracingPipelineConfig: block (MaxTraceRecursionDepth, MaxPayloadSizeInBytes, MaxAttributeSizeInBytes, optional PipelineFlags) 👀
  • Local root signatures (per shader-record root args) — at minimum a schema that lets a test record SBT-local constants 🏗️
  • Validate payload/attr sizes against shader-declared sizes at YAML parse time

State Object / RT pipeline creation

  • D3D12 backend: build a ID3D12StateObject from the YAML (DXIL library subobject, hit-group subobjects, global/local root signatures, raytracing pipeline + shader config subobjects)
  • Vulkan backend: VkRayTracingPipelineKHR + vkGetRayTracingShaderGroupHandlesKHR, pipeline-library plumbing if needed; VK_KHR_ray_tracing_pipeline feature gate
  • Metal backend: route through metal_irconverter for DXIL→AIR — it lowers DispatchRays into a compute dispatch backed by MTLVisibleFunctionTable / MTLIntersectionFunctionTable standing in for hit-groups, miss shaders, and callables. The backend work covers emitting the converter-expected compute dispatch and building those function tables. 👀 ([Metal] Add ray tracing pipeline, SBT, and DispatchRays bring-up #1281)

Shader binding table

  • Abstract ShaderBindingTable resource owned by the RT pipeline
  • YAML ShaderBindingTable: block describing raygen / miss / hit-group / callable records, each with ShaderName, optional local-root data 👀
  • Per-backend SBT builders:
    • D3D12: identifier from ID3D12StateObjectProperties::GetShaderIdentifier, 32-byte aligned records, programmer-managed stride
    • Vulkan: VkStridedDeviceAddressRegionKHR, alignment from rayTracingPipelineProperties
    • Metal: visible function tables / intersection function tables
  • Compute SBT record stride from declared local-root data size

DispatchRays command

  • New RayTracingEncoder (or extend ComputeEncoder) with dispatchRays(width, height, depth, sbt)
  • YAML dispatch parameters: RayTracingDispatch: { Width, Height, Depth } — currently reusing DispatchParameters.DispatchGroupCount as {Width, Height, Depth} for RT pipelines per Add RayTracing pipeline kind, shader stages, and YAML schema #1270; a dedicated RayTracingDispatch block can come later if useful.
  • Resource-state / barrier handling for SBT and AS reads

Resource binding parity

  • AS binding from raygen / miss / hit / callable stages (the inline-RT work only wires it up for compute)
  • UAV / SRV / CBV / sampler tables from each new stage
  • Verify cross-API root-signature layout matches between D3D12/Vulkan so the same DXIL works unmodified

Shader-observable spec surface

Each test asserts behavior visible from a raygen / miss / hit / callable shader. Coverage parallels the inline-RT epic where the same concept appears (e.g. ray flags), then adds the PSO-only surface (payloads, recursion, etc.).

TraceRay (host-side intrinsic)

  • RayFlags parameter behaves the same as in RayQuery<> — re-run the same flag matrix from [EPIC]: Improve inline raytracing test coverage #1258 — 👍 once bring-up lands
  • InstanceInclusionMask filtering — 👍
  • RayContributionToHitGroupIndex selects the right hit-group record — 👍
  • MultiplierForGeometryContributionToHitGroupIndex (per-geometry hit-group striding) — 🏗️ (multi-geometry BLAS)
  • MissShaderIndex selects the right miss record — 👍
  • RayDesc corners: TMin > 0, TMax clip, NaN/Inf direction — 👍
  • Recursive TraceRay up to MaxTraceRecursionDepth — 👍
  • Recursion beyond declared MaxTraceRecursionDepth is undefined; verify that the in-spec maximum value works — 👍

Raygen shader ([shader("raygeneration")])

  • DispatchRaysIndex() matches the dispatch x/y/z lane — 👍
  • DispatchRaysDimensions() echoes the host-side width/height/depth — 👍
  • Multiple raygen entries, one selected per pipeline — 👍
  • Writes to UAV indexed by DispatchRaysIndex — 👍
  • Calls TraceRay and consumes the returned payload — 👍

Miss shader ([shader("miss")])

  • Miss runs when no geometry is hit and payload is observable in raygen — 👍
  • Multiple miss shaders, MissShaderIndex selects between them — 👍
  • Miss-side ray-system-values: WorldRayOrigin, WorldRayDirection, RayTMin, RayTCurrent, RayFlags, DispatchRaysIndex — 👍

Closest-hit shader ([shader("closesthit")])

  • Runs exactly once per accepted hit; payload mutation observable in raygen — 👍
  • Hit-side ray-system-values: all of the miss-side ones plus InstanceIndex, InstanceID, PrimitiveIndex, GeometryIndex, HitKind, ObjectRayOrigin/Direction, ObjectToWorld3x4/4x3, WorldToObject3x4/4x3 — 👍
  • BuiltInTriangleIntersectionAttributes.barycentrics at known points — 👍
  • Hit-group routing: distinct closest-hit per hit-group, selected by RayContributionToHitGroupIndex + instance contribution — 👍
  • Closest-hit calls TraceRay to spawn a secondary ray (recursion) — 👍

Any-hit shader ([shader("anyhit")])

  • Any-hit fires for non-opaque geometry only — 🏗️ (non-opaque YAML)
  • IgnoreHit() skips a candidate; closest-hit sees next hit — 🏗️
  • AcceptHitAndEndSearch() ends traversal immediately — 🏗️
  • Any-hit can mutate payload before IgnoreHit (and the mutation must be discarded per spec) — 🏗️
  • Per-geometry NO_DUPLICATE_ANYHIT_INVOCATION — 🏗️ (geometry flag YAML)

Intersection shader ([shader("intersection")])

  • ReportHit(t, kind, attrs) with custom hit-attribute struct — 🏗️ (procedural geometry)
  • Custom HitKind value flows through to closest-hit / any-hit — 🏗️
  • ReportHit returns true/false based on TMin/TMax + any-hit decision — 🏗️
  • Intersection-side ray-system-values match the rest — 🏗️

Callable shader ([shader("callable")])

  • CallShader(index, params) runs the right callable record — 👍
  • Callable can be invoked from raygen, miss, closest-hit, callable — 👍
  • DispatchRaysIndex / Dimensions visible inside callable — 👍

Payload + attribute mechanics

  • Trivial payload (single uint) round-trip — 👍
  • Large payload near MaxPayloadSizeInBytes — 👍
  • [payload] qualifier in / out / inout semantics — 👍
  • Custom hit attribute struct from an intersection shader — 🏗️
  • Payload struct with mixed types (float/int/half) — 👍

Hit-group / SBT selection (shader-observable)

  • InstanceContributionToHitGroupIndex selects per-instance hit group — 🏗️ (YAML field shared with [EPIC]: Improve inline raytracing test coverage #1258)
  • RayContributionToHitGroupIndex per TraceRay call — 👍
  • MultiplierForGeometryContributionToHitGroupIndex across multi-geom BLAS — 🏗️ (multi-geom YAML)
  • Local-root constants in an SBT record are visible to the shader — 👍

Shader control flow / vendor edges

  • Divergent rays per lane (different hit-groups invoked) — 👍
  • Conditional TraceRay (lane mask) — 👍
  • TraceRay with RAY_FLAG_SKIP_CLOSEST_HIT_SHADER (PSO-only flag) — 👍
  • Mix of opaque and non-opaque BLAS in one pipeline — 🏗️
  • Same ray flag matrix as [EPIC]: Improve inline raytracing test coverage #1258 for parity — 👍

Out of scope (intentionally)

  • AS build flags / compaction / refit / clone / serialize — not shader-visible; same reasoning as [EPIC]: Improve inline raytracing test coverage #1258.
  • DXR 1.2 / SER (ShaderExecutionReordering) — future epic once base PSO RT works on all three backends.
  • Work-graph dispatched RT — separate feature.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions