Expand description
§zenfg
zenfg is a renderer-agnostic FrameGraph compiler and transient-resource
executor for wgpu. It records logical resources and accesses, validates content
flow, builds dependencies, culls dead work, derives usage, plans transient
aliasing, and optionally materializes retained work on a caller-owned device and
queue.
ZenFG does not own scenes, pipelines, bind groups, samplers, surfaces, presentation, or device-loss policy. This is a public beta crate; pin the exact prerelease version while integrating.
§Installation
cargo add zenfg@=0.1.0§Features
| Feature | Default | Adds |
|---|---|---|
default | Yes | Empty feature set; the core compiler and device-backed executor are always available |
serde | No | Serde support for internal compilation report types |
snapshot | No | zenfg-snapshot, wire-type re-exports, and portable report export |
FrameGraph::new() is CPU-only. FrameGraph::with_device() stores a cloned
wgpu::Device, owns a transient pool, and enables execution. The queue and all
imported resources remain caller-owned.
§Quick start
Use a Cargo application with the toolchain in Compatibility.
Place this code inside fn main() -> Result<(), zenfg::FrameGraphError> and finish
with Ok(()). Run cargo run; the assertion succeeds without requiring a GPU.
Lines prefixed with # in this Markdown are rustdoc test scaffolding, not lines to paste.
This complete CPU-only example records one transient output, retains it, and compiles a diagnostic plan:
use zenfg::{
BufferDesc, BufferRange, CompileOptions, FrameGraph, RootReason,
UsagePolicy, WriteContents,
};
let mut graph = FrameGraph::new();
let mut frame = graph.begin_frame();
let output = frame.create_buffer(BufferDesc {
label: "output".into(),
size: 1024,
usage: UsagePolicy::Infer,
})?;
let mut pass = frame.compute_pass("produce");
let _output = pass.storage_buffer_write(
output,
BufferRange::whole(),
WriteContents::Overwrite,
)?;
pass.finish()?;
frame.mark_buffer_root(output, BufferRange::whole(), RootReason::Output)?;
let compiled = frame.compile(CompileOptions::full_report())?;
assert_eq!(compiled.report().unwrap().summary.retained_node_count, 1);§Lifecycle
FrameGraph -> Frame<'frame> -> CompiledFrame<'frame> -> execute(queue)
runtime recording retained CPU plan optional, one-shotbegin_frame()exclusively borrows the runtime and creates one recording.Frame::compile()consumes the recording. Handles and access tokens carry runtime and recording identities enforced by the API and validation.CompiledFrame::execute()is one-shot. Native bindings and callbacks needed only by culled work are released after compilation.- Surface acquisition and presentation remain caller-owned; import and bind a fresh current surface texture for each presentation frame.
- Dropping
FrameGraphreleases retained pool and profiler resources, but not caller-owned imported resources.
§Common tasks
| Task | Public API |
|---|---|
| Create a CPU-only compiler | FrameGraph::new() |
| Create a device-backed runtime | FrameGraph::with_device() |
| Start a recording | begin_frame() |
| Create transient storage | create_texture(), create_buffer() |
| Register imported storage | import_texture(), import_surface_texture(), import_buffer() |
| Bind imported native objects | bind_imported_texture(), bind_imported_buffer() |
| Select texture subresources | create_texture_view() |
| Record render or compute work | render_pass() / finish_render(), compute_pass() / finish_compute() |
| Record copies or clears | copy_pass() with typed copy methods, clear_buffer(), clear_buffers() |
| Encode custom graph-owned commands | command_pass() / finish_command() |
| Call a renderer that submits itself | external_submission() / finish_external() |
| Retain observable values | mark_present(), mark_buffer_root(), mark_texture_root(), mark_readback() |
| Compile compact or full diagnostics | compile(CompileOptions::default()), compile(CompileOptions::full_report()) |
| Execute retained work | execute(), execute_with_options() |
| Request CPU/GPU timing | execute_with_timing() |
| Inspect or clear retained allocations | resource_pool_stats(), clear_resource_pool() |
| Export Snapshot 1.2 | snapshot::create_frame_graph_snapshot() with feature snapshot |
Exact signatures, fields, defaults, and structured FGxxxx errors are
documented on docs.rs.
§Key pattern: bind and resolve typed access
Imported storage is declared logically, then bound to a caller-owned native object. Transient and imported storage resolve through the same typed pass tokens:
use zenfg::{
BufferDesc, BufferRange, CompileOptions, FrameGraph, ImportBufferOptions,
InitialContents,
};
let mut graph = FrameGraph::with_device(device);
let mut frame = graph.begin_frame();
let buffer = frame.import_buffer(
BufferDesc::new("input", native.size()),
ImportBufferOptions::new(InitialContents::Defined),
)?;
frame.bind_imported_buffer(buffer, native)?;
let mut pass = frame.compute_pass("consume");
let input = pass.storage_buffer_read(buffer, BufferRange::whole())?;
pass.finish_compute(move |ctx| {
let _native = ctx.resources.buffer(input)?;
// Set a compute pipeline and dispatch through ctx.pass.
Ok(())
})?;
frame.compile(CompileOptions::default())?.execute(queue)?;Resolved transient objects are valid only inside their execution callback. Imported objects remain caller-owned, but every graph-visible access still needs a matching declaration.
§Resource and integration choices
Declaration granularity is optional: complex workloads can keep private weights, parameters, and scratch internally bound, while teaching or diagnostic use can expose more resources. Graph-visible dependencies and access correctness still apply. See Choosing resource declaration granularity.
For resources exposed to the graph:
- Use transient resources for storage needed only by one compiled execution; import storage that the caller owns or that must survive execution.
- Imported resources explicitly choose
InitialContents::DefinedorInitialContents::Undefined. The first write to a transient range must fully overwrite it. - Prefer structured render, compute, copy, and clear nodes. Use command passes for custom work on a graph-owned encoder.
- Use external submissions for renderers that own and submit their encoders. The boundary orders queue submissions but is not a GPU-completion fence.
- ZenFG performs no cross-frame dependency analysis and never acquires or presents a surface for the application.
See Core concepts for the shared ownership, content, dependency, lifetime, and integration model.
§Common mistakes
| Symptom | Fix |
|---|---|
| A pass is absent from the compiled plan | Retain its final value with the appropriate root, or declare only genuine side effects. |
| A read or preserving write reports undefined contents | Overwrite the complete range first or choose the correct imported initial contents. |
| An imported resource cannot execute | Bind the matching native object and ensure descriptor and usage metadata agree. |
| A typed token fails to resolve | Resolve it only through the pass that declared it and only inside that pass’s callback. |
| A transient object is used later | Never clone or retain resolved transient wgpu handles across callbacks or frames. |
| External work is incorrectly ordered | Submit all declared work on the shared queue before the external callback returns. |
| Timing is unavailable | Treat unsupported, busy, readback failure, and overflow as non-fatal timing results. |
§Complete examples
The following Cargo examples are published with the crate and compile-checked outside the workspace:
| Workflow | Example |
|---|---|
| Minimal presentation lifecycle | minimal_frame.rs |
| Transient render target to presentation | transient_to_present.rs |
| Caller-owned imported resource | imported_resource.rs |
| Cross-frame persistent state | persistent_state.rs |
| Opaque third-party submission | external_submission.rs |
| Portable Snapshot export | snapshot_export.rs |
| Asynchronous GPU timing | gpu_timing.rs |
| Compute storage output | compute_output.rs |
The repository also contains CPU-only compile and pool benchmarks. Snapshot
export requires the snapshot feature.
§Further reading
§Documentation and versions
This README describes zenfg 0.1.0. Registry badges show the current published channel, not your installed version.
- Exact installed APIs: read the included
src/, or runcargo doc --openin your consuming project. - Online guide (development branch). The site may describe changes newer than this package.
- Rust API for this version.
- Source and documentation for this release.
- Shared concepts for this release and compatibility.
- Plain Markdown documentation index (development branch).
- Complete Cargo recipes are included in
examples/.
§Execution timing
Use execute_with_timing(&queue, options, TimingMode::Cpu),
TimingMode::Gpu, or TimingMode::Both. The result owns an optional synchronous
CPU report and optional GPU readback. Poll the latter with try_take(); CPU-only
execution never waits for a GPU result. Ordinary execution collects no timing.
CPU is synchronous elapsed time for all executed node kinds, not thread CPU
usage. Execution total includes preparation, submission and transient release.
Modules§
- snapshot
- Snapshot 1.2 export plus the portable
zenfg_snapshotwire API.
Structs§
- Access
Id - Recording-local identity of a declared resource access.
- Access
Report - One declared resource access in recording order.
- Access
Token - A typed identity for one declared access in one pass.
- Allocation
Id - Compilation-local identity of a physical allocation.
- Allocation
Report - One physical transient allocation and all aliased logical resources assigned to it.
- Buffer
- Typed handle to one logical buffer in a single frame recording.
- Buffer
Copy Dst - Type marker for the
BufferCopyDstaccess role. - Buffer
Copy Src - Type marker for the
BufferCopySrcaccess role. - Buffer
Desc - Logical buffer descriptor used for validation and transient allocation.
- Buffer
Range - A byte range within a logical buffer.
- Buffer
Texture Copy Location - One logical buffer and texel layout used by a buffer-texture copy operation.
- Clear
Buffer Op - One ordered zero-fill operation in a structured clear-buffer node.
- Color
Attachment - Type marker for the
ColorAttachmentaccess role. - Color
Attachment Ops - Load/store operations for one color attachment.
- Command
Context - Synchronous callback context for direct command encoding.
- Compilation
Report - Optional compilation diagnostics selected by
ReportLevel. - Compilation
Summary - Counts, memory estimates, and timings available at summary report level.
- Compilation
Timings - CPU time spent in each compilation phase, in nanoseconds.
- Compile
Options - Options controlling CPU compilation and report generation.
- Compiled
Frame - An owned, single-use compilation result.
- Compute
Pass Context - Synchronous callback context for one retained structured compute node.
- CpuTiming
Node Report - CPU-side elapsed time for one executed node, including local setup and cleanup.
- CpuTiming
Report - Detached CPU report from one successful execution.
- Culled
Node Report - One removed node in original recording order.
- Debug
Group Id - Recording-local identity of a diagnostic debug group.
- Debug
Group Report - One recording-only diagnostic scope in group-open order.
- Dependency
Report - One value-carrying or ordering dependency between graph nodes.
- Depth
Attachment - Type marker for the
DepthAttachmentaccess role. - Depth
Attachment Ops - Load/store operations for one writable, pure-depth attachment.
- Diagnostic
- Structured non-fatal diagnostic attached to a compilation report.
- Execution
Options - Options controlling one GPU execution without changing the compiled plan.
- Execution
Resources - Pass-scoped resolver for logical resources declared by typed access tokens.
- Execution
Segment Report - Ordered retained nodes belonging to one execution segment.
- Execution
Timing - Immediate CPU results and an independently asynchronous GPU readback.
- External
Submission Context - Synchronous callback context for caller-controlled queue submission.
- Frame
- A single-use FrameGraph recording.
- Frame
Graph - Long-lived owner of frame identities, transient allocations, and GPU timing state.
- Full
Compilation Report - Complete recording, retention, dependency, and allocation tables.
- GpuTiming
Node Report - GPU duration for one retained render or compute pass.
- GpuTiming
Readback - A one-shot, non-blocking handle for one GPU timing result.
- Import
Buffer Options - Initial-content and native-usage contract for an imported buffer.
- Import
Texture Options - Initial-content and native-usage contract for an imported texture.
- Index
Buffer - Type marker for the
IndexBufferaccess role. - Indirect
Buffer - Type marker for the
IndirectBufferaccess role. - Node
Report - One retained node in original recording order.
- Normalized
Texture View Desc - Fully resolved recording-time metadata for a logical texture view.
- Pass
Builder - Builder for one graph node and its declared resource accesses.
- PassId
- Recording-local identity of a graph node/pass.
- Render
Pass Context - Synchronous callback context for one retained structured render node.
- Resource
Id - Recording-local identity of a logical resource.
- Resource
Lifetime - Inclusive retained execution-order interval of one logical resource.
- Resource
Pool Stats - Aggregate counters for the FrameGraph-owned transient resource pool.
- Resource
Report - Descriptor, usage, lifetime, and allocation facts for one logical resource.
- Root
Report - One observable resource range and the producers that keep it defined.
- Root
Resolution - Sources of the final contents in a root’s selected range.
- Sampled
Texture - Type marker for the
SampledTextureaccess role. - Storage
Buffer Read - Type marker for the
StorageBufferReadaccess role. - Storage
Buffer Write - Type marker for the
StorageBufferWriteaccess role. - Storage
Texture Read - Type marker for the
StorageTextureReadaccess role. - Storage
Texture Write - Type marker for the
StorageTextureWriteaccess role. - Texture
- Typed handle to one logical texture in a single frame recording.
- Texture
Copy Dst - Type marker for the
TextureCopyDstaccess role. - Texture
Copy Location - One logical texture subresource location used by a declarative copy operation.
- Texture
Copy Src - Type marker for the
TextureCopySrcaccess role. - Texture
Desc - Logical texture descriptor used for validation and transient allocation.
- Texture
Subresource Range - One normalized texture subresource range.
- Texture
View - Typed handle to one normalized logical texture view in a single recording.
- Texture
View Desc - Descriptor for a logical texture view.
- Uniform
Buffer - Type marker for the
UniformBufferaccess role. - ValueId
- Recording-local identity of a logical content value.
- Value
Report - One initial or pass-produced logical content value.
- Vertex
Buffer - Type marker for the
VertexBufferaccess role. - ViewId
- Recording-local identity of a logical texture view.
- View
Report - One normalized logical texture view.
Enums§
- Access
Mode - Whether an access reads or writes a logical resource.
- Access
Role - Pipeline role declared for one resource access.
- Attachment
Store Op - Whether attachment contents remain defined after the pass.
- Color
Attachment Load Op - Load/store operations for one color attachment.
- Culled
Node Reason - Why a recorded node was removed from the retained plan.
- Dependency
Kind - Whether a dependency carries data or only constrains execution order.
- Depth
Attachment Load Op - Load/store operations for one writable, pure-depth attachment.
- Diagnostic
Severity - Severity of a compilation diagnostic.
- Execution
Segment Kind - Kind of encoder/submission segment in the retained execution plan.
- Frame
Graph Error - All recording, compilation, and execution errors emitted by the FrameGraph.
- GpuTiming
Node Kind - The executable node kinds that can be measured with pass timestamp writes.
- GpuTiming
Report - The result of one requested GPU timing sample.
- GpuTiming
Unavailable Reason - Why a timed execution completed without timestamp values.
- Hazard
Kind - Hazard that caused a graph dependency.
- Initial
Contents - Whether an imported resource contains readable data at frame start.
- Node
Kind - Kind of work represented by a graph node.
- Report
Level - Amount of compilation diagnostics to retain.
- Resource
Descriptor - Snapshotted logical descriptor of a reported resource.
- Resource
Kind - Kind of logical resource recorded in a frame.
- Resource
Origin - Ownership and allocation source of a logical resource.
- Resource
Range - Normalized region of a logical resource used in reports.
- Resource
Usage - Effective native usage inferred for a retained logical resource.
- Root
Reason - Why the contents of a resource must remain live after graph compilation.
- Texture
Target - A whole logical texture or an explicitly created logical view.
- Timing
Mode - Timing families requested for one synchronous execution.
- Undefined
Cause - Origin of undefined contents reported by the compiler.
- Usage
Policy - Policy used to choose native wgpu usage flags for a transient resource.
- Value
Kind - Origin of one logical content value.
- Write
Contents - Content behavior of a write access.
Traits§
- Access
Marker - Sealed type-level role carried by an
AccessToken. - Buffer
Access Marker - Access role that resolves to a
wgpu::Bufferduring execution. - Texture
Access Marker - Access role that resolves to a
wgpu::Textureorwgpu::TextureView.