Skip to main content

EngineRenderer

Struct EngineRenderer 

Source
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

Source

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.

Source

pub fn format(&self) -> TextureFormat

The target format this renderer warmed its pipelines for.

Source

pub fn caps(&self) -> &TierCaps

The adapter capabilities every sizing decision is made against.

Source

pub fn targets(&self) -> &IntermediateTargets

The pool the engine’s off-screen intermediates come from.

Source

pub fn page_config(&self) -> PageConfig

The bounds this renderer sizes intermediate layer pages between.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn unbind_texture(&mut self, id: SceneTextureId) -> Option<TextureView>

Removes the texture registered under id, returning its view.

Source

pub fn bound_texture(&self, id: SceneTextureId) -> Option<&TextureView>

The view registered under id, if any.

Source

pub fn bound_texture_count(&self) -> usize

How many external textures are currently bound.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Trait Implementations§

Source§

impl Debug for EngineRenderer

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T, S> SimdFrom<T, S> for T
where S: Simd,

Source§

fn simd_from(_simd: S, value: T) -> T

Source§

impl<F, T, S> SimdInto<T, S> for F
where T: SimdFrom<F, S>, S: Simd,

Source§

fn simd_into(self, simd: S) -> T

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WasmNotSend for T
where T: Send,

Source§

impl<T> WasmNotSendSync for T

Source§

impl<T> WasmNotSync for T
where T: Sync,