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
CompiledGraphthe per-backend executor consumes.
Structs§
- Barrier
Op - One barrier the executor must insert before a pass runs. Per-backend
interpretation: Vulkan emits
vkCmdPipelineBarrier; DirectX emitsD3D12_RESOURCE_BARRIER; Metal mostly ignores them (implicit hazard tracking) but may translatefrom: Write, to: Readon the cull ICB path into an explicituseResource(.Write)declaration. - Buffer
Usage - Buffer-side counterpart to
TextureUsage. Same bitset shape. - Compiled
Graph - Frozen graph the per-backend executor consumes. Passes are in execution order; barriers are pre-derived; resource lifetimes are ready for a future aliaser.
- Compiled
Pass - One pass in execution order. Carries the declared reads / writes, the
barriers the executor must emit before running this pass, and the
PassKind+PassIdthe backend dispatches on. - Compiled
Resource - One resource in the compiled graph. Carries the lifetime interval (in compiled-pass-index space) and the origin distinction the aliaser will care about.
- Frame
Graph Inputs - Per-frame inputs that gate conditional passes. Built by
draw_framefrom the liveMtlContextstate and consumed bybuild_frame_graphso the conditional-inclusion decisions made here match what the executor will dispatch. - Pool
Gates - The feature gates a backend’s transient pool is built for, i.e. the ones it
is rebuilt on. Everything else
planning_inputsforces live. - Read
Stages - 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. - Resource
Id - Dense resource identifier.
u32::MAXreserved as the “invalid” sentinel; everything else is a valid index into the compiled graph’sresourcesVec. The executor usesindex()to look up a resource’s realised GPU object. - Texture
Desc - 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.
- Texture
Handle - One side of a
PassBuilder::read_*/write_*declaration. The resource is a small dense index into the graph’s resource arena; theversionincrements on every write so a read-after-write chain (main → decals → fogwriting the same hdr_resolve) is an unambiguous DAG. - Texture
Usage - 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.
- Transient
Slot - 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.
- Transient
Texture - One pooled transient, resolved against a concrete drawable extent. The backend translates this into its native texture descriptor; nothing here is backend-specific.
Enums§
- Clear
Value - 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.
- Graph
Resource Class - How a graph resource a backend drives from
barriers_beforeis used, so the backend can translate the coarseResourceStateinto a concrete native state: the sameWritemeans a colour render target for one resource and a depth-stencil target for another, which map to differentD3D12_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
usizeto indexPASS_NAMESor any[T; PASS_COUNT]companion array. - Pass
Kind - 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/MTLComputePassDescriptoranalogue per backend; the actual encoding stays in the per-backendencode_*methods. Blit passes are not yet in scope (today’s engine has none). - Pixel
Format - Backend-agnostic pixel format. Maps to
MTLPixelFormat/vk::Format/DXGI_FORMATper executor. Only the formats the engine actually uses are enumerated; extend as new passes need new targets. - Resource
State - Coarse per-resource state used by the barrier deriver. The
executor maps each to the backend’s concrete state: for Vulkan a
VkImageLayout+VkAccessFlagspair, for DirectX aD3D12_RESOURCE_STATES, for Metal mostly a no-op exceptuseResourceon 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_dimsso 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. The per-pass timing array
in
crate::gfx::profile::RenderStatsis sized to at least this many slots. - PASS_
NAMES - Stable display name for each pass. Index =
PassId as usize. Used by the WSprofile.passesreply 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_outputinto 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 underdebug_assertions, over the graph it is about to run;backendnames 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
gatesshould 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.