| name | pass-variant-addition |
| description | When adding a new Pass variant to the Titan frame graph, update all 10+ synchronized sites in lockstep — enum, ALL_PASSES, manifest, frame_graph_build, bench tools (3), RON configs, forward-only gates, scaffold dispatch, stratum_hook, render_hook |
| source | auto-skill |
| extracted_at | 2026-06-15T19:00:47.920Z |
Pass variant multi-site addition
Adding a new Pass variant to the Titan frame graph requires
updating ~10+ sites in lockstep. Missing any site causes compile
errors or (worse) silent test failures in the bench pin tests.
This skill lists every site in dependency order.
Step 1: Add variant to the enum
In crates/titan-rendering-frame-graph/src/lib.rs:
pub enum Pass {
NewPassVariant,
}
Place after the logically-preceding pass in the rendering pipeline.
Step 2: Validate pass ordering
In the same file, find validate_pass_ordering(). Add an empty {}
arm for the new variant (ordering constraints are defined by RON
configs, not by this function, unless the new pass has a hard
dependency).
Step 3: ALL_PASSES constant
In crates/titan-rendering-3d/src/manifest.rs, add to
ALL_PASSES_IN_CANONICAL_ORDER array.
Step 4: Manifest entry
In the same file, manifest_entry() function. Add an arm with
Provenance::Unaudited (for scaffold) or Provenance::Audited
with citation for real passes. Use DispatchKind::StratumHook for
stratum passes or the appropriate dispatch kind.
Step 5: Frame graph build guard
In crates/titan-rendering-3d/src/frame_graph_build.rs, if the
pass needs cascade textures or other frame-level resources, add
it to the needs_cascades or equivalent matches!() check.
Step 6: Forward-only gates
In crates/titan-rendering-3d/src/lib.rs, search for has_stratum
and add to the boot-panic guard (true = present). Also add to the
forward-only unreachable catch-arm in the dispatch match.
Step 7: Scaffold dispatch arms
In the same lib.rs, add #[cfg(not(feature = "forward-only"))]
dispatch arms using warn_once_unimplemented_pass for scaffold
passes:
#[cfg(not(feature = "forward-only"))]
Pass::NewPassVariant => {
if !self.gbuffer_ready { continue; }
if self.stratum_hook.is_none() {
Self::warn_once_unimplemented_pass(pass, &mut self.warned_passes);
continue;
}
let hook = self.stratum_hook.as_mut().unwrap();
let ctx = PassCtx { };
hook.record_new_pass(ctx);
}
Step 8: StratumRenderHook trait method
In crates/titan-rendering-3d/src/stratum_hook.rs, add a default
no-op method to the StratumRenderHook trait:
fn record_new_pass(&mut self, _ctx: PassCtx<'_>) {}
Step 9: Render hook dispatch
In crates/titan-stratum-lighting/src/render_hook.rs, implement
the trait method with real dispatch logic (or a scaffold returning
early with a TODO: comment).
For compute passes: follow the existing pattern
(record_stratum_shadow_importance is the canonical reference).
For raster passes: set up a render pass with appropriate
attachments.
Step 10: Bench-tool pass lists (3 sites)
Update ALL THREE bench tools — each has two functions
(forward_stratum and deferred_stratum):
tools/titan-render-bench/src/pipelines.rs
tools/titan-render-bench-probe/src/pipelines.rs
tools/titan-render-bench-shadows/src/pipelines.rs
The KB entry stratum-pass-list-duplicated-in-bench-tools.md tracks
this requirement. Pin tests compare bench pass lists against
engine-default RON configs — mismatch = test failure.
Step 11: RON config files
Add the new pass to the appropriate tier RON configs under
crates/titan-rendering-frame-graph/configs/. The four stratum
configs are:
pipeline_deferred_stratum_high.ron
pipeline_deferred_stratum_mid.ron
pipeline_forward_stratum_high.ron
pipeline_forward_stratum_mid.ron
Place the pass in pipeline order appropriate for its dependency
requirements.
Step 12: Test fixture pass lists
In crates/titan-rendering-frame-graph/src/lib.rs, update the
test fixtures that list canonical pass orders for each tier:
deferred_stratum_mid_passes_are_in_canonical_order
deferred_stratum_high_passes_are_in_canonical_order
forward_stratum_mid_passes_are_in_canonical_order
forward_stratum_high_passes_are_in_canonical_order
Verification
cargo check --workspace
cargo test -p titan-render-bench -p titan-render-bench-probe -p titan-render-bench-shadows
Common pitfalls
- Forgetting bench tools: The compiler doesn't catch this. The
pin tests do — run them explicitly.
- Forgetting forward-only gates: The forward-only feature gate
doesn't compile in CI unless explicitly enabled. Always add both
the
has_stratum check and the catch-arm.
- Forgetting RON configs: Passes without RON entries compile
but never dispatch. The probe pipeline won't exercise them.
- Forgetting
frame_graph_build.rs: If the pass needs cascade
textures and isn't in needs_cascades, it gets a zero-init
dummy texture — silently wrong, no compile error.
- Tier-gated passes can require constructor signature changes.
When a pass is gated on
DenoiserTier (e.g. High-only VSM), the
StratumDispatch::new constructor may need a new denoiser_tier
parameter. Every external call site (editor + 3 bench tools) must
be updated — the compiler catches these but the blast radius is
wider than a typical pass addition.
- PassCtx field propagation. If a new pass needs a resource not
yet in
PassCtx (e.g. tlas or gbuffer_world_pos_view for RT
shadows), add the field as Option and set it to None at all
construction sites. See auto-skill-passctx-field-propagation.