Expand description
The engine’s render pipelines, as plain values.
Nine render states (EnginePipeline::ALL): six of them rasterize sparse
strips out of the one strip.wgsl source, differing only in their render
state; two more clear and copy intermediate textures, and the last runs one
pass of a layer filter. Each strip state is further
specialised along a second axis, the PaintClass — which program the
strip source is compiled to (see The paint-class axis below). Every
variant is described by a frust_gpu::RenderPipelineDesc, a value whose
only GPU handle is the shared strip layout, so the set the first frames are
known to need can be listed at start-up and handed to
frust_gpu::PipelineCache::warm_up before the first frame asks for one.
§The paint-class axis
The strip fragment stage’s colour-source dispatch costs every fragment the
branches it never takes (the S1 ablation on an Adreno 660: a program
keeping only coverage, solid and gradients shaded S1 in under half the time
of the whole one). So each strip state is compiled once per class it
serves, from the class’s specialisation of the one
source (shader_src::strip_class_source), and a pass sets the pipeline of
each run’s class in turn (renderer’s class runs). PaintClass::Uber is
the whole program, and the only one FRUST_ENGINE_UBER_PROGRAM uses.
The axis is folded rather than multiplied out: the destination-out pair only ever draws the punch’s solid source, and the opaque state never draws a composite or a blurred rectangle (neither is ever opaque), so
| State | Classes served | Variants |
|---|---|---|
EnginePipeline::StripIntermediate | all eight | 8 |
EnginePipeline::StripAlpha | all eight | 8 |
EnginePipeline::StripDepthAlpha | all eight | 8 |
EnginePipeline::StripOpaque | solid, gradient, image, image-filtered, glyph, Uber | 6 |
EnginePipeline::StripDestOut | solid, Uber | 2 |
EnginePipeline::StripDepthDestOut | solid, Uber | 2 |
| clear, copy, filter | their own program | 3 |
— VARIANT_COUNT, 37 at most. The glyph class is served wherever an
image is, since a glyph quad is an atlas draw like any other. A run of a
class its state does not serve is drawn with Uber
(EnginePipeline::fold_class), which draws everything. Variants are
created lazily, on the first frame that draws a run of that class through
that state; warm_up_descs lists the nine Uber states plus
WARM_CLASSES across WARM_CLASS_STATES — the solid, gradient, image
and glyph programs of the two depth-tested surface states, which is what an
ordinary frame draws through — so steady state compiles nothing.
Every strip variant, Uber included, is built against one explicit pipeline
layout (crate::gpu::bindings::strip_pipeline_layout) instead of the
layout wgpu derives per module: a class that stops reading a binding would
otherwise derive a different layout, and the renderer’s bind groups would
stop fitting it.
§The program family
The image sampler (FRUST_ENGINE_IMAGE_SAMPLER) is not a third axis: a
renderer compiles ONE family of strip programs — by default the one whose
image programs filter bilinear atlas images through a hardware sampler, or
under FRUST_ENGINE_IMAGE_SAMPLER=0 the manual four-tap one
(crate::gpu::shader_src’s The two program families) — so the
table above holds for either, and VARIANT_COUNT is per family. The
sampler family adds exactly one pipeline beside them: the atlas-layer
target below keeps drawing through the manual family’s Uber program and
layout, because the glyph pass’s own bind groups
(crate::gpu::atlas) carry no sampler. That pipeline is warmed with the
rest, so the family costs no inline compile either.
§The six strip variants
| Variant | Target | Blend | Depth |
|---|---|---|---|
EnginePipeline::StripIntermediate | INTERMEDIATE_FORMAT | premultiplied | none |
EnginePipeline::StripAlpha | the frame’s target format | premultiplied | none |
EnginePipeline::StripDepthAlpha | the frame’s target format | premultiplied | test, no write |
EnginePipeline::StripOpaque | the frame’s target format | none | test and write |
EnginePipeline::StripDestOut | the frame’s target format | DEST_OUT_BLEND | none |
EnginePipeline::StripDepthDestOut | the frame’s target format | DEST_OUT_BLEND | test, no write |
The depth comparison is wgpu::CompareFunction::LessEqual against a
DEPTH_FORMAT attachment, which is what the vertex stage’s own z
encoding expects: strip.wgsl maps painter’s-order index 0 (the backmost
draw) to z = 1.0 and each draw in front of it to a smaller z, so drawing
front-to-back lets the opaque pass reject everything already covered.
§The atlas-layer target
A pass that rasterizes glyph outlines into one layer of the atlas array
draws through ATLAS_STRIP_PIPELINE, which is
EnginePipeline::StripIntermediate itself rather than a variant of its
own (in the manual program family; see The program family above for
the one pipeline the sampler family adds for it). An atlas layer is an crate::gpu::atlas::ATLAS_FORMAT colour target
with no depth attachment, composited source-over so a COLR glyph’s layers
and a re-used slot’s clear stack in the order they were recorded — which is
the render state StripIntermediate already is, field for field. A second
enum arm carrying identical state would compile a second identical pipeline
object, and would break
the_warm_up_list_covers_every_pipeline_exactly_once, whose whole point is
that no two arms describe the same pipeline. What the atlas target gets
instead is a name and [the_atlas_target_reuses_the_intermediate_variant],
which pins the format equality the reuse rests on: if either format is ever
moved, that test fails rather than glyphs quietly rendering through a
pipeline whose colour target no longer matches its attachment.
§The destination-out pair
The hole punch crate::compile::clear lowers is a strip run like any
other — same program, same instance layout — and differs only in its blend
state, which is why it is a pipeline variant rather than a shader of its
own. DEST_OUT_BLEND computes dst · (1 − src.a) in fixed function, for
the colour and the alpha component, which is what erases a rectangle to
(0, 0, 0, 0) without a shader-side composite reading a target it is also
writing.
There are two of them for the same reason there are two alpha variants: a
pipeline’s depth state has to agree with whether the pass it runs in
attaches a depth buffer at all, and the engine’s depth attachment is
optional (no attachment, or FRUST_ENGINE_NO_DEPTH). The depth-testing one
is what restores the paint order the display list recorded; the other is
the same trade the two draw passes already make when they collapse into one.
§The filter pass
EnginePipeline::Filter is a genuinely new pipeline rather than a second
name for an existing one: a different program (filter.wgsl), a different
instance layout (filter_vertex_layout, eight words per instance against
the strip’s six), and a different bind-group shape. It writes
INTERMEDIATE_FORMAT unblended and with no depth, because a filter pass
replaces the region it writes rather than compositing onto it — the
scheduler clears the destination page ahead of every filter round, and the
quad overdraws a transparent padding border around the region for the same
reason (see crate::schedule’s Filter rounds).
It is also where the engine’s first sampler appears. Every other
pipeline reads its textures with textureLoad at integer coordinates; the
blur kernels sample bilinearly at fractional offsets, which is what lets a
decimation cost four samples instead of sixteen and a convolution tap one
instead of two. The sampler itself is crate::gpu::targets::filter_sampler
— the layout here only has to know that group 1 carries a texture and a
sampler.
§Bind groups
The clear, copy and filter pipelines use wgpu’s default layout, derived
from their shader module; the strip pipelines use the explicit layout above,
which is exactly the one the whole strip program derives. The group
structure is therefore whatever the WGSL declares, and it matches the
reference renderer’s explicit layouts group for group — strip programs bind
groups 0..3 (alphas + config + layer input; atlas array + external texture;
encoded paints; gradient LUT), which is the WebGL2 ceiling of four exactly,
with no headroom. The filter program binds two (the filter-data texture;
the source page plus its sampler), well inside it.
EnginePipeline::layout_desc restates each pipeline’s shape as the value
frust_gpu::lint::lint_pipeline_layout checks, so that ceiling is a test
rather than a comment.
Structs§
- Engine
Shaders - The engine’s compiled shader modules, by id, and the one explicit layout every strip pipeline is built against.
Enums§
- Engine
Pipeline - One of the engine’s render pipelines.
- Engine
Shader Module - Which of the four registered modules a pipeline draws with.
Constants§
- ATLAS_
STRIP_ PIPELINE - The strip pipeline a pass whose colour attachment is one atlas array layer draws through.
- DEPTH_
FORMAT - The depth attachment format the depth-testing strip variants use.
- DEST_
OUT_ BLEND - The fixed-function destination-out blend the punch pass draws with.
- FS_MAIN
- Fragment entry point of every program here.
- INTERMEDIATE_
FORMAT - The format every intermediate (off-screen) strip target uses.
- TOPOLOGY
- Every strip instance expands to one quad, so every pipeline here draws a four-vertex strip with no index buffer.
- VARIANT_
COUNT - Every distinct pipeline the engine can create for one target format: each strip state times the classes it serves, plus the clear, copy and filter pipelines. See the module doc’s table.
- VS_MAIN
- Vertex entry point of the strip, clear (region) and copy programs alike.
- VS_
MAIN_ FULLSCREEN - Vertex entry point of the scissor-driven atlas clear.
- WARM_
CLASSES - The classes whose programs are warmed up ahead of the first frame, beside the nine Uber states: the solid, gradient, image and glyph programs — what every ordinary frame’s surface passes draw, settled text included.
- WARM_
CLASS_ STATES - The strip states
WARM_CLASSESare warmed up across: the two depth-tested surface states every frame with a depth attachment draws through.
Functions§
- atlas_
strip_ desc - The description
ATLAS_STRIP_PIPELINEis built from. - clear_
vertex_ layout - The clear program’s instance layout: origin, size and target size, each a
pair of
u32s, 24 bytes per instance. - copy_
vertex_ layout - The copy program’s instance layout: destination origin, source origin,
region size and destination size, each a
u16pair packed into oneu32, 16 bytes per instance. - filter_
vertex_ layout - The filter program’s instance layout: two
u16-pair extents each for the source region, the destination region and the destination page, plus the filter’s own texel offset, the layer’s unscaled extent and the pass kind — eightu32s, 32 bytes per instance. - warm_up
- Queues the
warm_up_descslist for background compilation. - warm_
up_ descs - The full warm-up list: every pipeline in
EnginePipeline::ALL(each strip state’s Uber program), thenWARM_CLASSESacrossWARM_CLASS_STATES, described againsttarget_format. - warm_
up_ descs_ for warm_up_descswith the kill switch stated rather than read: withuber_onlyset the list is exactly the nine Uber states.