pub struct EngineRenderer { /* private fields */ }Expand description
One surface’s 2D render engine: a scene in, recorded passes out.
Create one per surface and keep it across frames — the retained scene compiler, gradient cache, pipeline cache, resource textures and intermediate pool are the reason a steady-state frame allocates nothing.
Implementations§
Source§impl EngineRenderer
impl EngineRenderer
Sourcepub fn new(
device: &Device,
caps: &TierCaps,
format: TextureFormat,
pipeline_cache: Option<&PipelineCache>,
) -> Result<Self, EngineError>
pub fn new( device: &Device, caps: &TierCaps, format: TextureFormat, pipeline_cache: Option<&PipelineCache>, ) -> Result<Self, EngineError>
Builds a renderer for format targets on caps’ adapter, warming
every engine pipeline in the background.
pipeline_cache is the host’s persisted driver cache when it has one —
None on every backend but Vulkan.
§Errors
EngineError::AtlasError when the adapter’s resource_texture_dim
is not a power of two of at least [MIN_RESOURCE_TEXTURE_DIM]. The
strip shader reconstructs a resource texture’s width as 1 << bits and
an upload’s row stride must satisfy wgpu’s copy alignment, so neither
the shader’s addressing nor the uploads would be valid otherwise.
Sourcepub fn format(&self) -> TextureFormat
pub fn format(&self) -> TextureFormat
The target format this renderer warmed its pipelines for.
Sourcepub fn caps(&self) -> &TierCaps
pub fn caps(&self) -> &TierCaps
The adapter capabilities every sizing decision is made against.
Sourcepub fn targets(&self) -> &IntermediateTargets
pub fn targets(&self) -> &IntermediateTargets
The pool the engine’s off-screen intermediates come from.
Sourcepub fn page_config(&self) -> PageConfig
pub fn page_config(&self) -> PageConfig
The bounds this renderer sizes intermediate layer pages between.
Sourcepub fn atlas_budget(&self) -> AtlasBudget
pub fn atlas_budget(&self) -> AtlasBudget
The atlas geometry image residency allocates within.
Derived from the adapter in Self::new, so this is the tier’s budget
narrowed to what the adapter can create — not a constant.
Sourcepub fn set_atlas_budget(&mut self, budget: AtlasBudget)
pub fn set_atlas_budget(&mut self, budget: AtlasBudget)
Re-budget image residency, dropping every image currently resident and the atlas array holding them.
An atlas rectangle only means anything against the geometry it was allocated in, so a new budget invalidates every one already handed out — which is why the array, the registry of where each image lives and the bind groups naming that array all go in the same step, and each image re-uploads on the next frame that draws it. A start-up or adapter-change operation, never a per-frame one.
Sourcepub fn set_image_residency(&mut self, images: ImageResidency)
pub fn set_image_residency(&mut self, images: ImageResidency)
Replace image residency wholesale, on the same invalidation terms as
Self::set_atlas_budget.
The programmatic counterpart to FRUST_ENGINE_NO_ATLAS: an
[ImageResidency::disabled] residency takes both atlas classes out of
the frame — images are skipped and every glyph is drawn as outline
strips — without a process-global environment variable, which is what a
caller comparing the two paths on one device needs.
Sourcepub fn refused_atlas_regions(&self) -> u64
pub fn refused_atlas_regions(&self) -> u64
How many atlas regions this renderer has declined to write or clear.
Zero on every sound frame: residency allocates inside the budget the array is created at, and the array is grown to the depth the frame reports before its regions are written, so a refusal means those two went out of agreement. The count exists so that disagreement is measurable rather than silent — a refused write is a region the frame believed it had filled.
Sourcepub fn finish_warm_up(&mut self, device: &Device)
pub fn finish_warm_up(&mut self, device: &Device)
Finishes pipeline warm-up on the calling thread, returning only once every engine pipeline exists.
Warm-up is started in the background by Self::new and normally
needs no attention. Two callers want it forced: a host that must not
let the first frame pay for a compile, and anything about to drop the
wgpu::Device shortly after building a renderer — the warm-up worker
holds its own handle on that device, and tearing it down while the
worker is mid-compile is a driver-level hazard rather than a clean
cancellation.
Each variant is requested through the cache, which builds a queued one inline and waits for one the worker has already started, so nothing is compiled twice and nothing is left for the worker to claim afterwards.
Sourcepub fn compiled_pipelines(&self) -> u64
pub fn compiled_pipelines(&self) -> u64
How many render pipelines this renderer has compiled so far.
Counts distinct pipelines, which is why it is a diagnostic rather
than something to wait on: two entries of EnginePipeline::ALL that
differ only in their colour format describe the same pipeline whenever
the frame’s target format happens to equal
crate::gpu::pipelines::INTERMEDIATE_FORMAT, and the cache compiles
that one variant once. Use Self::finish_warm_up to wait.
Sourcepub fn set_depth_pre_cleared(&mut self, pre_cleared: bool)
pub fn set_depth_pre_cleared(&mut self, pre_cleared: bool)
States whether a caller-supplied depth attachment already holds the
depth this frame should test against — see
DepthAttachment::set_pre_cleared.
Sourcepub fn depth_pre_cleared(&self) -> bool
pub fn depth_pre_cleared(&self) -> bool
Whether a caller-supplied depth attachment is treated as already populated.
The statement is sticky and set once, so a host driving several surfaces (or re-establishing one after a device loss) can read back what this renderer is on rather than tracking it a second time.
Sourcepub fn bind_texture(
&mut self,
id: SceneTextureId,
size: (u32, u32),
view: TextureView,
) -> Option<TextureView>
pub fn bind_texture( &mut self, id: SceneTextureId, size: (u32, u32), view: TextureView, ) -> Option<TextureView>
Registers a caller-owned texture so a
Command::SceneTexture naming
id draws it, returning whatever was registered under id before.
id is the SceneTextureId the texture minted for itself
(frust_gpu::Texture::as_scene_texture), and size is its extent in
texels — the rectangle a display list’s destination is mapped onto.
view must be a non-array 2D view of a float-sampleable texture
carrying wgpu::TextureUsages::TEXTURE_BINDING; wgpu rejects
anything else when the frame’s bind group is built.
Both halves of the registration land here: the view a pass samples and the extent the compiler composes a paint transform against. Registering is idempotent — re-registering the same id replaces the view and drops the bind groups naming the old one.
An extent past u16::MAX on either axis, or a zero one, registers
nothing and answers None: the record the shader reads packs the
source region into u16 halves, so there is no honest rectangle to
name. Scenes drawing that id go on drawing nothing.
Sourcepub fn unbind_texture(&mut self, id: SceneTextureId) -> Option<TextureView>
pub fn unbind_texture(&mut self, id: SceneTextureId) -> Option<TextureView>
Removes the texture registered under id, returning its view.
Sourcepub fn bound_texture(&self, id: SceneTextureId) -> Option<&TextureView>
pub fn bound_texture(&self, id: SceneTextureId) -> Option<&TextureView>
The view registered under id, if any.
Sourcepub fn bound_texture_count(&self) -> usize
pub fn bound_texture_count(&self) -> usize
How many external textures are currently bound.
Sourcepub fn resize(&mut self, device: &Device, width: u32, height: u32)
pub fn resize(&mut self, device: &Device, width: u32, height: u32)
Releases everything sized against the old surface extent and re-establishes what the next frame needs at the new one.
Pipelines, shader modules, the gradient cache and the resource textures are all extent-independent and deliberately survive: a resize must not cost a pipeline rebuild or a ramp re-bake. What goes is the intermediate pool’s parked entries — every one keyed on an extent nothing will ask for again — and the engine-owned depth attachment, which has to match its colour attachment exactly. Reallocating the depth buffer here rather than on the next frame keeps it off the frame path.
Sourcepub fn end_frame(&mut self, _queue: &Queue)
pub fn end_frame(&mut self, _queue: &Queue)
Closes the frame out: ages the intermediate pool by one frame and evicts the gradient cache down to its capacity.
Call once per frame, after the frame’s commands have been submitted and including frames that drew nothing — those are the frames a parked intermediate ages on. Gradient eviction compacts the packed LUT buffer and rewrites the offsets of the survivors, which is why it belongs at the frame boundary rather than mid-frame, where it would invalidate offsets the frame’s own encoded paints already carry.
_queue is part of the signature because the end-of-frame maintenance
this method owns grows queue writes as the engine does (an atlas region
cleared after the frame that consumed it, in the reference renderer);
it has none of them yet.
Sourcepub fn encode(
&mut self,
device: &Device,
queue: &Queue,
encoder: &mut CommandEncoder,
scene: &Scene,
target: EngineTarget<'_>,
base_color: Color,
root: Affine,
) -> Result<(), EngineError>
pub fn encode( &mut self, device: &Device, queue: &Queue, encoder: &mut CommandEncoder, scene: &Scene, target: EngineTarget<'_>, base_color: Color, root: Affine, ) -> Result<(), EngineError>
Compiles scene and records the frame’s passes into encoder.
root is applied ahead of every command’s own transform and
base_color is what the target is cleared to before anything is drawn.
Neither the encoder nor the queue is submitted — see the module header.
§Errors
EngineError::TargetTooLarge for a target outside the u16 device
grid the strip pipeline addresses, EngineError::InvalidTransform for
a non-finite transform, EngineError::InvalidGeometry for non-finite
command geometry (rect extents, radii, path points, stroke or dash
values), EngineError::SchedulerEscalation for a layer shape the
engine’s scheduler does not serve,
EngineError::IntermediateTextureTooLarge for a layer no page can be
sized to, EngineError::AlphaCapacity when a frame’s coverage
outgrows the alpha texture, and EngineError::PaintCapacity when its
encoded paints or colour ramps outgrow theirs. Every one of them is
returned before anything is recorded, uploaded, allocated or submitted,
so a refused frame leaves encoder exactly as it was found and the
renderer’s own resources — the atlas array included — exactly as they
were. That is what lets the caller skip the frame cleanly (nothing is
presented and the previously presented content persists) rather than
present it half-drawn, and what keeps a refused frame’s image uploads
alive for the next frame that is not refused.
Sourcepub fn encode_traced(
&mut self,
device: &Device,
queue: &Queue,
encoder: &mut CommandEncoder,
scene: &Scene,
target: EngineTarget<'_>,
base_color: Color,
root: Affine,
timestamps: FrameTimestamps<'_>,
) -> Result<(), EngineError>
pub fn encode_traced( &mut self, device: &Device, queue: &Queue, encoder: &mut CommandEncoder, scene: &Scene, target: EngineTarget<'_>, base_color: Color, root: Affine, timestamps: FrameTimestamps<'_>, ) -> Result<(), EngineError>
Self::encode, with each pass’s GPU time stamped into timestamps.
The one difference is the sink: every pass this records asks
timestamps for its own timestamp_writes and takes None for an
answer, so a frame encoded with FrameTimestamps::inert — which is
exactly what Self::encode passes — records byte-identical work.
Which pass is charged to which span is EngineSpan’s own
documentation; the host owns the ring behind the sink and reads the
frame’s spans back out of it some frames later (see
frust_gpu::diag::TimestampRing).
§Errors
Exactly Self::encode’s, on exactly its terms — a refused frame has
recorded no pass, so it has taken no timestamp either and the host
abandons the ring’s slot rather than mapping it.
Sourcepub fn atlas_render_report(&self) -> AtlasRenderReport
pub fn atlas_render_report(&self) -> AtlasRenderReport
What the last frame’s render-to-atlas pass serviced.
Zero across the board on a steady-state frame: text that hit the cache on every glyph frees no rectangle, queues no pixmap and dirties no page.