frust_render/lib.rs
1//! Layer 4: GPU backend — wgpu 30 over the frust-owned `frust-engine` strip
2//! pipeline.
3//!
4//! Consumes the renderer-agnostic [`frust_scene::Scene`] display list and
5//! renders it into a window's swapchain. `frust-engine` draws through ordinary
6//! render passes into the acquired swapchain texture — no compute pass, no
7//! storage write, no intermediate and no blit — so the surface needs only
8//! `RENDER_ATTACHMENT` in whatever format the platform reports, plus the depth
9//! attachment the surface carries alongside it. The one variation is a
10//! swapchain whose compositor genuinely reads STRAIGHT alpha: there the frame
11//! lands in a surface-owned intermediate and one fragment pass un-premultiplies
12//! it into the swapchain. Both presentation models live in [`RenderContext`] /
13//! [`SurfaceRenderer`].
14//!
15//! `wgpu` types are kept out of the public API except at one deliberate seam:
16//! [`SurfaceRenderer::on_surface_created`] takes a `wgpu::SurfaceTarget` (the
17//! shell must hand over a window). There is no renderer to select: the engine
18//! is the only one this crate contains, so what was a tier probe is now a
19//! plain capability gate ([`engine_support`] over [`TierCaps`], against
20//! [`ENGINE_REQUIRED_DOWNLEVEL_FLAGS`]) that refuses an adapter which cannot
21//! run it. The cargo feature that gated the engine, the env-var/CLI override
22//! that picked a renderer, and the vello-classic/`vello_cpu` tiers they chose
23//! between are all gone.
24//!
25//! [`HeadlessRenderer`] renders the same scenes with no surface at all — the
26//! offscreen harness this crate's pixel-regression tests compare against,
27//! resolving its adapter and limits exactly as a real device does and asking
28//! the same capability gate.
29//!
30//! [`external_pass`] is the third `wgpu`-typed seam, and the one that points
31//! outward: an app or a crate beside the facade registers an [`ExternalPass`],
32//! and [`SurfaceRenderer`] hands it the frame's own device, queue and encoder
33//! ahead of the scene pass so it can render into a target of its own and bind
34//! the result for a `Command::SceneTexture` to composite. See that module's
35//! docs for the contract; `HeadlessRenderer` does not drain that registry.
36//!
37//! Surface lifecycle is a first-class state machine: see [`SurfaceRenderer`]
38//! and [`SurfacePhase`]/[`FrameOutcome`], whose pure transition tables live in
39//! `frust_gpu::lifecycle` and are re-exported here.
40//!
41//! # Where the foundation lives
42//!
43//! The `wgpu` instance, the lazily created logical device, the surface factory
44//! and its cross-thread [`DetachedSurface`] hand-off, the surface lifecycle
45//! state machine, the persisted pipeline-cache framing and the offscreen
46//! shader-effect pipelines are all `frust-gpu`'s
47//! ([`frust_gpu::context::RenderContext`], [`frust_gpu::surface`],
48//! [`frust_gpu::lifecycle`], [`frust_gpu::pipeline_cache`],
49//! [`frust_gpu::effects`]) — one copy, shared with every other consumer of that
50//! crate. The context/surface/lifecycle types a shell touches are re-exported
51//! here under the names they have always had, so `frust_render::RenderContext`
52//! keeps working; the pipeline-cache framing and the shader-effect pipelines
53//! were never public under this crate and are reached as
54//! `frust_gpu::pipeline_cache`/`frust_gpu::effects` directly. What is genuinely this crate's own is the
55//! renderer: the render-path decision, the engine resources each arm owns, the
56//! engine's own capability gate, and [`SurfaceRenderer`] itself.
57
58mod context;
59// The pre-scene GPU seam: caller-supplied passes recorded into the frame's own
60// encoder ahead of the scene, binding their results into the engine's
61// external-texture registry. Public as a module (rather than only through the
62// flat re-export below) so its own docs — the whole contract a pass is written
63// against — are reachable under one heading.
64pub mod external_pass;
65// Offscreen (no surface, no swapchain) engine rendering: the harness the
66// pixel-regression tests render through. Reachable from outside the crate,
67// unlike the effects module below, because those tests live outside it — and
68// still leaking no `wgpu` type (see its module docs).
69mod headless;
70mod renderer;
71mod tier;
72
73// The device/surface foundation, re-exported name for name from `frust-gpu`:
74// every one of these was defined in this crate before the two copies were
75// merged, and a shell must not have to care that they moved.
76pub use frust_gpu::{
77 AcquireOutcome, DetachedSurface, EncodeOutcome, FrameOutcome, RenderContext,
78 SurfaceAlphaRequest, SurfaceFactory, SurfacePhase,
79};
80// `DeviceHandle` (the cheap-to-clone device/queue/adapter pair a shell hands
81// off to `frust-shell-common`'s process-wide GPU slot — see that crate's
82// `gpu` module) stays behind this crate's own `gpu` feature rather than
83// joining the unconditional re-export above: `frust-gpu` itself is not
84// optional, so gating costs nothing but keeps a default build of this crate
85// (the only one most apps ever produce) at zero new public symbols.
86#[cfg(feature = "gpu")]
87pub use frust_gpu::DeviceHandle;
88// Flat, alongside every other name a caller reaches this crate by; the module
89// above stays public for its docs.
90pub use external_pass::{
91 ExternalFrame, ExternalPass, register_external_pass, unregister_external_pass,
92};
93pub use headless::{
94 GOLDEN_EXPECT_ADAPTER_ENV_VAR, GOLDEN_EXPECT_BACKEND_ENV_VAR, HeadlessImage, HeadlessMeta,
95 HeadlessOptions, HeadlessRenderer, HeadlessSpec,
96};
97// `DeferredPresent` stays here: it wraps a frame this crate's renderer
98// acquired, submitted and handed back un-presented, which is a renderer
99// concern, not a foundation one.
100pub use renderer::{DeferredPresent, SurfaceRenderer};
101// The adapter-capability gate is all that is left of the tier seam: the
102// renderer itself is no longer a choice, so the tier enum, its selection
103// result types, the selection function, the env-var name and the override
104// parsers are all gone from this surface along with the choice they
105// described.
106pub use tier::{ENGINE_REQUIRED_DOWNLEVEL_FLAGS, EngineUnsupported, TierCaps, engine_support};