Skip to main content

ShaderEffects

Struct ShaderEffects 

Source
pub struct ShaderEffects { /* private fields */ }
Expand description

Owns every GPU resource the shader-showcase feature needs: lazily-compiled per-program pipelines and per-(program, quantized size) offscreen targets.

Implementations§

Source§

impl ShaderEffects

Source

pub fn new(pipeline_cache: Option<PipelineCache>) -> Self

Create an empty engine seeded with an optional clone of the surface’s wgpu::PipelineCache (used as RenderPipelineDescriptor.cache so a persisted cache speeds first compilation). None on adapters without PIPELINE_CACHE support (Metal/desktop), which is a plain cold start.

Source

pub fn needs_compile(&self, id: u64) -> bool

Whether program id still needs its pipeline compiled — false once it is either compiled or known-failed. Pure predicate (the fast-path skip ensure_pipeline consults first), unit-testable without a device.

Source

pub fn ensure_pipeline(&mut self, device: &Device, id: u64, wgsl: &str)

Lazily compile the render pipeline for program id from wgsl (the fragment source; see the module-level shader contract). A no-op if the program is already compiled or already known-failed.

Compilation is wrapped in a Validation error scope: a shader that fails to validate records id as failed (skipped forever after) with a rate-limited log::warn!, and never panics — the FFI no-panic invariant. The validation error also surfaces through the device’s latched uncaptured-error handler.

Source

pub fn mark_seen( &mut self, live_ids: &HashSet<u64>, live_target_keys: &HashSet<(u64, u32, u32)>, ) -> Vec<u64>

Bump the frame counter and record every id in live_ids (this frame’s distinct program ids, taken before compile/target work — so an id whose pipeline just failed to compile is still marked seen) as last seen at the new frame, and every key in live_target_keys (this frame’s distinct (id, quantized w, quantized h) target keys — see quantized_target_key) as last seen the same way. Ages out and drops any target key that has now gone unseen for at least MAX_UNSEEN_TARGET_FRAMES frames (its own, shorter clock — [reapable_target_ages]), then returns every program id that has now gone unseen for at least MAX_UNSEEN_FRAMES frames (via [reapable_ids]), ready to hand to Self::reap.

Called once per frame regardless of whether either set is empty — a scene that stops drawing shader quads entirely must still age out and eventually reap every previously-seen id and target, not just ones still present.

Source

pub fn reap(&mut self, stale_ids: &[u64])

Reap every resource for each id in stale_ids (a Self::mark_seen output): every targets entry whose key’s id matches (and its target_last_seen row), plus the compiled pipelines entry, any failed record, and the last_seen row itself. Dropping failed alongside the rest means a program id that reappears after being reaped is treated as brand new: it recompiles cleanly rather than hitting a stale skip from a compile failure that happened frames ago (or never happened at all). The clamp-warning latch is dropped on the same trigger: a reaped id that returns warns afresh on its first oversized request, exactly like a brand-new program — and this reap is also what bounds the latch set’s growth, the same way it bounds every other map here.

Source

pub fn ensure_target(&mut self, device: &Device, id: u64, w: u32, h: u32)

Ensure a target exists for (id, w, h), creating it (at quantized_target_key’s quantized, device-clamped extent) if absent. Age-based reclaim of an unseen target — same-quantized-key requests aside — is handled separately by Self::mark_seen, so two distinct (quantized) sizes of the same program can coexist across many frames rather than evicting each other every one.

A no-op if program id has no compiled pipeline (never compiled, or compile-failed): a target is useless without the pipeline that owns its bind-group layout, so the caller compiles first. w/h are quantized and clamped to the device’s own max_texture_dimension_2d ceiling (floored at 1) — a device-validity floor only, not the caller’s size policy: a caller may still apply a tighter policy cap of its own (e.g. frust_engine::effects::shader_quad’s 8192) before calling, but an oversized request that reaches here creates a texture at the device ceiling instead of tripping wgpu validation, and warns once per distinct oversized (id, requested_w, requested_h) pair — the warning latches the first time this exact request is clamped by the ceiling (never merely because quantization rounded it up), then repeats only if the request changes. A freshly created entry’s target_last_seen row is stamped at the current frame so it starts with a valid age even if the caller’s own Self::mark_seen call for this frame has not run yet, and its generation is stamped from Self::next_generation — see the module header’s “Target identity” section.

Before minting a genuinely new key, enforces MAX_TARGETS_PER_ID: if id already holds the cap’s worth of resident keys, its own least-recently-seen one ([oldest_target_key_for_id]) is evicted immediately, ahead of and independent of Self::mark_seen’s age-based reap — see MAX_TARGETS_PER_ID’s own doc for why.

Source

pub fn encode_pass( &self, encoder: &mut CommandEncoder, queue: &Queue, device: &Device, id: u64, requested: (u32, u32), time: f32, )

Encode one fullscreen-triangle pass for program id, requested at requested (the caller’s exact device-space extent — never the quantized target’s own, larger size), into its target, writing the uniform buffer (resolution, time) first. A no-op if the program’s pipeline or its (quantized) target is missing. Adds no queue.submit — the caller owns encoder creation and submission ordering relative to the frame’s own passes.

Renders into the sub-rect (0, 0)..requested of the target via wgpu’s viewport, never the whole (possibly larger, quantized) attachment: the resolution uniform a shader reads is requested itself (clamped to what the target can actually hold, in the rare case the device ceiling shrank it below the request), so a quad samples back content sized exactly to itself even when its target is shared with, or larger than, other nearby-sized requests.

Source

pub fn target_texture( &self, device: &Device, id: u64, w: u32, h: u32, ) -> Option<&Texture>

The offscreen texture program id was rendered into for a request at (w, h) (looked up by quantized_target_key, device’s own ceiling), or None if that (quantized) target does not exist (never ensure_targeted, or the pipeline compile failed).

The read-back seam. A consumer that draws the result instead wants Self::target_view, since a scene-texture registration takes a view; nothing is registered with a foreign renderer and nothing is handed back on eviction — the pool owns the texture and the borrow lives as long as the caller holds it. The returned texture is the whole (possibly larger, quantized) attachment — a read-back consumer wanting only the rendered sub-rect wants Self::target_extent alongside it.

Source

pub fn target_view( &self, device: &Device, id: u64, w: u32, h: u32, ) -> Option<&TextureView>

The full (quantized) extent view of program id’s (w, h)-requested target — the handle a consumer registers to sample the rendered result — or None on the same terms as Self::target_texture.

The same view the pass wrote through, rather than a fresh one per frame: a view is a handle onto the texture, so creating one per registration would churn a resource that never changes while its target lives. This is a whole-texture view even where only the (0, 0)..target_extent sub-rect was actually rendered this frame — see Self::target_extent for the sub-rect a registration should state instead.

Source

pub fn target_extent( &self, device: &Device, id: u64, w: u32, h: u32, ) -> Option<(u32, u32)>

The extent a (w, h) request at program id was actually rendered at this call — the sub-rect Self::encode_pass draws into, not the (quantized, possibly larger) texture Self::target_view hands back — or None on the same terms as Self::target_texture (no (quantized) target exists for this id/extent at all).

Not (w, h) echoed back unconditionally: in the rare case the device’s real ceiling constrains the target below what was asked (w/h past device’s max_texture_dimension_2d), the answer is clamped the same way Self::encode_pass itself clamps what it renders, so a registration built from this value and the target’s view always agree on what the attachment actually holds at the caller’s requested corner.

Source

pub fn target_generation( &self, device: &Device, id: u64, w: u32, h: u32, ) -> Option<u64>

The generation program id’s (w, h)-requested target was created at — bumped by Self::ensure_target every time it actually creates a new texture at the resolved key, never on a cache hit — or None on the same terms as Self::target_texture (no target exists for this id/extent at all).

The identity half of a registration alongside Self::target_extent: two targets can share an identical requested extent yet be genuinely different GPU resources (one reaped and recreated behind a caller that only compared extents), and this is what lets a caller (e.g. frust_engine::effects::shader_quad::ShaderQuadPass::register) tell them apart — see the module header’s “Target identity” section.

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, 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,