Skip to main content

Module render_graph

Module render_graph 

Source
Expand description

Backend-agnostic render graph. Types, builder, and compile pass with unit tests; the per-backend executors live alongside each backend (metal/graph_exec.rs, vulkan/graph_exec.rs, directx/graph_exec.rs) and consume the CompiledGraph this module produces.

The graph deliberately stops short of allocating GPU resources; that stays backend-owned. The graph’s job is to:

  • give every pass a stable identity (PassId),
  • track read / write declarations so the compile pass can derive pass order, transient resource lifetimes, and per-pass barriers,
  • surface a CompiledGraph the per-backend executor consumes.

Structs§

BarrierOp
One barrier the executor must insert before a pass runs. Per-backend interpretation: Vulkan emits vkCmdPipelineBarrier; DirectX emits D3D12_RESOURCE_BARRIER; Metal mostly ignores them (implicit hazard tracking) but may translate from: Write, to: Read on the cull ICB path into an explicit useResource(.Write) declaration.
BufferUsage
Buffer-side counterpart to TextureUsage. Same bitset shape.
CompiledGraph
Frozen graph the per-backend executor consumes. Passes are in execution order; barriers are pre-derived; resource lifetimes are ready for a future aliaser.
CompiledPass
One pass in execution order. Carries the declared reads / writes, the barriers the executor must emit before running this pass, and the PassKind + PassId the backend dispatches on.
CompiledResource
One resource in the compiled graph. Carries the lifetime interval (in compiled-pass-index space) and the origin distinction the aliaser will care about.
FrameGraphInputs
Per-frame inputs that gate conditional passes. Built by draw_frame from the live MtlContext state and consumed by build_frame_graph so the conditional-inclusion decisions made here match what the executor will dispatch.
PoolGates
The feature gates a backend’s transient pool is built for, i.e. the ones it is rebuilt on. Everything else planning_inputs forces live.
ReadStages
Which shader stage(s) read a graph resource across a contiguous read-run (the passes that read one resource version before the next writer). Carried on a barrier whose Read side spans this run so a backend can satisfy it in a single transition: a write made visible to both a compute consumer and a fragment consumer needs one barrier covering both stages, not a per-consumer read-to-read barrier (which would not carry the producing write). Derived from each reading pass’s PassKind (a render pass samples in the fragment stage, a compute pass in the compute stage); empty on a barrier with no Read side (a write-only producer transition). Add bits as passes read in stages the two current ones do not model.
ResourceId
Dense resource identifier. u32::MAX reserved as the “invalid” sentinel; everything else is a valid index into the compiled graph’s resources Vec. The executor uses index() to look up a resource’s realised GPU object.
TextureDesc
Texture-shape description carried by both imported and transient resources. This is the authoritative shape: the aliaser sizes a resource from it and each backend’s transient pool translates it into a native descriptor, so a desc that disagrees with what the backend would have created is a defect rather than a documentation slip.
TextureHandle
One side of a PassBuilder::read_* / write_* declaration. The resource is a small dense index into the graph’s resource arena; the version increments on every write so a read-after-write chain (main → decals → fog writing the same hdr_resolve) is an unambiguous DAG.
TextureUsage
Bitset describing how a texture can be used. The graph doesn’t enforce these against declared reads / writes (executors do); the field exists so the aliaser can match transient resources to pool entries with the right usage flags.
TransientSlot
One slot: the members that share a backing allocation, in the order they reuse it (lifetime-start). A single-member slot is a plain pooled target; a multi-member slot is a realised alias, and the order is what each backend’s aliasing barriers are wired from.
TransientTexture
One pooled transient, resolved against a concrete drawable extent. The backend translates this into its native texture descriptor; nothing here is backend-specific.

Enums§

ClearValue
What a target’s clear resolves to. Split by kind rather than carried as four floats so a depth target cannot silently be given a colour.
GraphResourceClass
How a graph resource a backend drives from barriers_before is used, so the backend can translate the coarse ResourceState into a concrete native state: the same Write means a colour render target for one resource and a depth-stencil target for another, which map to different D3D12_RESOURCE_STATES / vk::ImageLayouts. The backend resolver assigns a class to each migrated resource; the backend’s barrier translator maps (class, state) to its native state. Extend as resources of new kinds (storage / compute targets, …) migrate.
PassId
One per-pass identity. Cast to usize to index PASS_NAMES or any [T; PASS_COUNT] companion array.
PassKind
What kind of work an executor encodes for a pass: render vs compute. The graph cares about this only enough to pick the right MTLRenderPassDescriptor / MTLComputePassDescriptor analogue per backend; the actual encoding stays in the per-backend encode_* methods. Blit passes are not yet in scope (today’s engine has none).
PixelFormat
Backend-agnostic pixel format. Maps to MTLPixelFormat / vk::Format / DXGI_FORMAT per executor. Only the formats the engine actually uses are enumerated; extend as new passes need new targets.
ResourceState
Coarse per-resource state used by the barrier deriver. The executor maps each to the backend’s concrete state: for Vulkan a VkImageLayout + VkAccessFlags pair, for DirectX a D3D12_RESOURCE_STATES, for Metal mostly a no-op except useResource on the ICB-driven cull pass.

Constants§

FOG_FROXEL_X
X/Y/Z dimensions of the volumetric-fog froxel volume. Sized to keep the per-frame compute cost modest (~230 k threads per dispatch) while preserving enough screen-space detail for shaft-of-light shadowing. Backends that implement the froxel path read these constants directly; the values also ride in FogFroxelParams.froxel_dims so shaders can map between absolute indices and normalised volume UVs without recompiling.
FOG_FROXEL_Y
Fog froxels down the screen. See FOG_FROXEL_X.
FOG_FROXEL_Z
Fog froxel depth slices. See FOG_FROXEL_X.
PASS_COUNT
Number of distinct passes the engine times. Sized to match PASS_NAMES; the per-pass timing array in crate::profile::RenderStats is sized to at least this many slots.
PASS_NAMES
Stable display name for each pass. Index = PassId as usize. Used by the WS profile.passes reply and the per-pass timing readback.

Functions§

apply_view
The frame’s effective graph inputs under the view state. Cleared show flags gate their passes off; the flat modes (Unlit, Wireframe) additionally drop the surface-effect stack; the G-buffer channel modes keep the prepass alive and, for the occlusion view, route ao_output into the composite.
assert_slot_aliasing_sound
Panic if any alias slot has two members live at once in graph. Members of a slot share bytes, so two live at once means one reads memory the other overwrote, and unlike a barrier gap there is no validation layer behind it on any backend. Every executor calls this per frame under debug_assertions, over the graph it is about to run; backend names the caller in the message.
barrier_coverage_gaps_for_driven
The same check restricted to the resources driven[resource_index] marks, i.e. the ones a backend executor resolves to a native target and emits transitions for. A backend calls this on the graphs a real session builds, so it covers the input combinations a headless sweep does not reach, and it asserts specifically that everything the backend claims to drive is fully covered. Resources outside the driven set keep whatever synchronisation their encoder owns and are skipped.
build_frame_graph
Compile the frame graph for inputs: the pass list above, gated down to the passes this frame actually runs.
final_states
The access each resource is left in once the graph’s last barrier for it has run, indexed by resource id: the state plus the stage union that barrier carried, since a Read’s native state can depend on its consuming stages. (Undefined, empty) for a resource no barrier touches.
plan_pool_slots
The alias-slot list a pool built for gates should allocate, taken straight from the graph: the grouping and each member’s extent, format and usage come from one planning graph, so init and resize cannot drift apart and neither can the graph and the resource it describes.
pooled
The transients a backend pool owns; everything else the graph declares transient stays backend-owned. One set for every backend, because which labels are pooled is policy rather than a per-backend capability: a set that differed per backend would make their footprints incomparable and would leave the soundness sweep below checking a grouping no backend builds.