Skip to main content

Module pool

Module pool 

Source
Expand description

A recycling pool for the short-lived textures a frame renders into: TexturePool, the PooledTexture it hands out, and the TextureAllocator seam that keeps both host-testable.

An intermediate render target — a scratch surface a pass draws into and a later pass consumes — is allocated and dropped every frame if nothing recycles it. That is exactly the cost the hybrid renderer’s own TODO names (“we currently allocate a new strips buffer for each render pass”), and it is worst during a resize storm: dragging a window edge produces a new surface size every frame, so a pool keyed on the exact requested extent allocates a fresh texture per frame and parks the previous one forever.

Two decisions make the pool survive that storm.

  • Quantization. A request’s extent is rounded up to the next multiple of SIZE_QUANTUM (256 px) before it becomes a key, so a drag through 4096 distinct widths lands on a couple of dozen keys instead. The caller gets a texture at least as large as it asked for and renders into the sub-rect it actually wants (PooledTexture::requested_size); Impeller’s coverage-size quantization is the same trade — a bounded amount of slack memory in exchange for reuse across a size that never stops changing.
  • Aging with slack. A free entry survives DEFAULT_MAX_UNUSED_FRAMES (60) frames of disuse before TexturePool::age drops it. frust-render’s compositor scratch ages out after 2 frames, which is right for one full-surface texture whose size is pinned to the surface, and far too aggressive here: a resize drag revisits a quantized size seconds later, and a two-frame window would evict every entry between visits and turn the pool back into a plain allocator. Grow-and-never-shrink is the other failure mode, so the window is finite and tunable (TexturePool::with_max_unused_frames).

§Recycled contents are never loaded

A pooled texture’s prior contents belong to whichever pass used it last, so a pass targeting one must CLEAR, never LOAD — that is what PooledTexture::color_attachment builds, and it is a correctness rule before it is a bandwidth one. Pairing LoadOp::Clear with StoreOp::Discard is also the precondition for wgpu::TextureUsages::TRANSIENT_ATTACHMENT, which effective_usage adds on an adapter that reports TierCaps::transient_saves_memory — a write-only attachment can then live in tile memory and may never get backing storage at all. TRANSIENT_ATTACHMENT is incompatible with every other usage, so it is applied only to a texture whose whole usage set is RENDER_ATTACHMENT; anything sampled or copied out afterwards keeps the clear/discard policy and no flag.

§No GPU in the loop

Allocation goes through TextureAllocator rather than a wgpu::Device borrow, so the pool’s keying, reuse, aging and statistics are exercised against a counting fake with no device — the same pure-decision / platform-lookup split crate::caps::TierCaps::fake gives adapter capabilities. wgpu::Device implements the trait, and it is the only implementation the engine itself ever passes.

Structs§

PoolKey
The configuration a pooled texture is interchangeable within: two requests that produce the same key may share one texture, and two that do not never can.
PoolStats
A TexturePool’s counters, as of the TexturePool::stats call.
PooledTexture
A texture checked out of a TexturePool, the key it returns under, and the frame clock it ages against.
TexturePool
Recycles the intermediate textures a frame renders into, keyed by quantized configuration and aged by frame clock.

Constants§

DEFAULT_MAX_UNUSED_FRAMES
How many frames a free entry may go unused before TexturePool::age drops it: roughly a second of frames at 60 Hz, so a resize drag that revisits a quantized size still finds it parked.
SIZE_QUANTUM
The multiple every pooled texture’s width and height is rounded up to before it is used as a pool key.

Traits§

TextureAllocator
Creates the texture/view pair a TexturePool hands out.

Functions§

effective_usage
The usage a pooled texture requested as usage is actually created with.
quantize_extent
Rounds width and height up to the next multiple of SIZE_QUANTUM, never past max_dimension.