Skip to main content

Crate zenfg

Crate zenfg 

Source
Expand description

§zenfg

zenfg published version docs.rs MIT license

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-beta.4

§Features

FeatureDefaultAdds
defaultYesEmpty feature set; the core compiler and device-backed executor are always available
serdeNoSerde support for internal compilation report types
snapshotNozenfg-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-shot
  • begin_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 FrameGraph releases retained pool and profiler resources, but not caller-owned imported resources.

§Common tasks

TaskPublic API
Create a CPU-only compilerFrameGraph::new()
Create a device-backed runtimeFrameGraph::with_device()
Start a recordingbegin_frame()
Create transient storagecreate_texture(), create_buffer()
Register imported storageimport_texture(), import_surface_texture(), import_buffer()
Bind imported native objectsbind_imported_texture(), bind_imported_buffer()
Select texture subresourcescreate_texture_view()
Record render or compute workrender_pass() / finish_render(), compute_pass() / finish_compute()
Record copies or clearscopy_pass() with typed copy methods, clear_buffer(), clear_buffers()
Encode custom graph-owned commandscommand_pass() / finish_command()
Call a renderer that submits itselfexternal_submission() / finish_external()
Retain observable valuesmark_present(), mark_buffer_root(), mark_texture_root(), mark_readback()
Compile compact or full diagnosticscompile(CompileOptions::default()), compile(CompileOptions::full_report())
Execute retained workexecute(), execute_with_options()
Request CPU/GPU timingexecute_with_timing()
Inspect or clear retained allocationsresource_pool_stats(), clear_resource_pool()
Export Snapshot 1.2snapshot::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::Defined or InitialContents::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

SymptomFix
A pass is absent from the compiled planRetain its final value with the appropriate root, or declare only genuine side effects.
A read or preserving write reports undefined contentsOverwrite the complete range first or choose the correct imported initial contents.
An imported resource cannot executeBind the matching native object and ensure descriptor and usage metadata agree.
A typed token fails to resolveResolve it only through the pass that declared it and only inside that pass’s callback.
A transient object is used laterNever clone or retain resolved transient wgpu handles across callbacks or frames.
External work is incorrectly orderedSubmit all declared work on the shared queue before the external callback returns.
Timing is unavailableTreat 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:

WorkflowExample
Minimal presentation lifecycleminimal_frame.rs
Transient render target to presentationtransient_to_present.rs
Caller-owned imported resourceimported_resource.rs
Cross-frame persistent statepersistent_state.rs
Opaque third-party submissionexternal_submission.rs
Portable Snapshot exportsnapshot_export.rs
Asynchronous GPU timinggpu_timing.rs
Compute storage outputcompute_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-beta.4. Registry badges show the current published channel, not your installed version.

§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_snapshot wire API.

Structs§

AccessId
Recording-local identity of a declared resource access.
AccessReport
One declared resource access in recording order.
AccessToken
A typed identity for one declared access in one pass.
AllocationId
Compilation-local identity of a physical allocation.
AllocationReport
One physical transient allocation and all aliased logical resources assigned to it.
Buffer
Typed handle to one logical buffer in a single frame recording.
BufferCopyDst
Type marker for the BufferCopyDst access role.
BufferCopySrc
Type marker for the BufferCopySrc access role.
BufferDesc
Logical buffer descriptor used for validation and transient allocation.
BufferRange
A byte range within a logical buffer.
BufferTextureCopyLocation
One logical buffer and texel layout used by a buffer-texture copy operation.
ClearBufferOp
One ordered zero-fill operation in a structured clear-buffer node.
ColorAttachment
Type marker for the ColorAttachment access role.
ColorAttachmentOps
Load/store operations for one color attachment.
CommandContext
Synchronous callback context for direct command encoding.
CompilationReport
Optional compilation diagnostics selected by ReportLevel.
CompilationSummary
Counts, memory estimates, and timings available at summary report level.
CompilationTimings
CPU time spent in each compilation phase, in nanoseconds.
CompileOptions
Options controlling CPU compilation and report generation.
CompiledFrame
An owned, single-use compilation result.
ComputePassContext
Synchronous callback context for one retained structured compute node.
CpuTimingNodeReport
CPU-side elapsed time for one executed node, including local setup and cleanup.
CpuTimingReport
Detached CPU report from one successful execution.
CulledNodeReport
One removed node in original recording order.
DebugGroupId
Recording-local identity of a diagnostic debug group.
DebugGroupReport
One recording-only diagnostic scope in group-open order.
DependencyReport
One value-carrying or ordering dependency between graph nodes.
DepthAttachment
Type marker for the DepthAttachment access role.
DepthAttachmentOps
Load/store operations for one writable, pure-depth attachment.
Diagnostic
Structured non-fatal diagnostic attached to a compilation report.
ExecutionOptions
Options controlling one GPU execution without changing the compiled plan.
ExecutionResources
Pass-scoped resolver for logical resources declared by typed access tokens.
ExecutionSegmentReport
Ordered retained nodes belonging to one execution segment.
ExecutionTiming
Immediate CPU results and an independently asynchronous GPU readback.
ExternalSubmissionContext
Synchronous callback context for caller-controlled queue submission.
Frame
A single-use FrameGraph recording.
FrameGraph
Long-lived owner of frame identities, transient allocations, and GPU timing state.
FullCompilationReport
Complete recording, retention, dependency, and allocation tables.
GpuTimingNodeReport
GPU duration for one retained render or compute pass.
GpuTimingReadback
A one-shot, non-blocking handle for one GPU timing result.
ImportBufferOptions
Initial-content and native-usage contract for an imported buffer.
ImportTextureOptions
Initial-content and native-usage contract for an imported texture.
IndexBuffer
Type marker for the IndexBuffer access role.
IndirectBuffer
Type marker for the IndirectBuffer access role.
NodeReport
One retained node in original recording order.
NormalizedTextureViewDesc
Fully resolved recording-time metadata for a logical texture view.
PassBuilder
Builder for one graph node and its declared resource accesses.
PassId
Recording-local identity of a graph node/pass.
RenderPassContext
Synchronous callback context for one retained structured render node.
ResourceId
Recording-local identity of a logical resource.
ResourceLifetime
Inclusive retained execution-order interval of one logical resource.
ResourcePoolStats
Aggregate counters for the FrameGraph-owned transient resource pool.
ResourceReport
Descriptor, usage, lifetime, and allocation facts for one logical resource.
RootReport
One observable resource range and the producers that keep it defined.
RootResolution
Sources of the final contents in a root’s selected range.
SampledTexture
Type marker for the SampledTexture access role.
StorageBufferRead
Type marker for the StorageBufferRead access role.
StorageBufferWrite
Type marker for the StorageBufferWrite access role.
StorageTextureRead
Type marker for the StorageTextureRead access role.
StorageTextureWrite
Type marker for the StorageTextureWrite access role.
Texture
Typed handle to one logical texture in a single frame recording.
TextureCopyDst
Type marker for the TextureCopyDst access role.
TextureCopyLocation
One logical texture subresource location used by a declarative copy operation.
TextureCopySrc
Type marker for the TextureCopySrc access role.
TextureDesc
Logical texture descriptor used for validation and transient allocation.
TextureSubresourceRange
One normalized texture subresource range.
TextureView
Typed handle to one normalized logical texture view in a single recording.
TextureViewDesc
Descriptor for a logical texture view.
UniformBuffer
Type marker for the UniformBuffer access role.
ValueId
Recording-local identity of a logical content value.
ValueReport
One initial or pass-produced logical content value.
VertexBuffer
Type marker for the VertexBuffer access role.
ViewId
Recording-local identity of a logical texture view.
ViewReport
One normalized logical texture view.

Enums§

AccessMode
Whether an access reads or writes a logical resource.
AccessRole
Pipeline role declared for one resource access.
AttachmentStoreOp
Whether attachment contents remain defined after the pass.
ColorAttachmentLoadOp
Load/store operations for one color attachment.
CulledNodeReason
Why a recorded node was removed from the retained plan.
DependencyKind
Whether a dependency carries data or only constrains execution order.
DepthAttachmentLoadOp
Load/store operations for one writable, pure-depth attachment.
DiagnosticSeverity
Severity of a compilation diagnostic.
ExecutionSegmentKind
Kind of encoder/submission segment in the retained execution plan.
FrameGraphError
All recording, compilation, and execution errors emitted by the FrameGraph.
GpuTimingNodeKind
The executable node kinds that can be measured with pass timestamp writes.
GpuTimingReport
The result of one requested GPU timing sample.
GpuTimingUnavailableReason
Why a timed execution completed without timestamp values.
HazardKind
Hazard that caused a graph dependency.
InitialContents
Whether an imported resource contains readable data at frame start.
NodeKind
Kind of work represented by a graph node.
ReportLevel
Amount of compilation diagnostics to retain.
ResourceDescriptor
Snapshotted logical descriptor of a reported resource.
ResourceKind
Kind of logical resource recorded in a frame.
ResourceOrigin
Ownership and allocation source of a logical resource.
ResourceRange
Normalized region of a logical resource used in reports.
ResourceUsage
Effective native usage inferred for a retained logical resource.
RootReason
Why the contents of a resource must remain live after graph compilation.
TextureTarget
A whole logical texture or an explicitly created logical view.
TimingMode
Timing families requested for one synchronous execution.
UndefinedCause
Origin of undefined contents reported by the compiler.
UsagePolicy
Policy used to choose native wgpu usage flags for a transient resource.
ValueKind
Origin of one logical content value.
WriteContents
Content behavior of a write access.

Traits§

AccessMarker
Sealed type-level role carried by an AccessToken.
BufferAccessMarker
Access role that resolves to a wgpu::Buffer during execution.
TextureAccessMarker
Access role that resolves to a wgpu::Texture or wgpu::TextureView.