Skip to main content

Module pipelines

Module pipelines 

Source
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

StateClasses servedVariants
EnginePipeline::StripIntermediateall eight8
EnginePipeline::StripAlphaall eight8
EnginePipeline::StripDepthAlphaall eight8
EnginePipeline::StripOpaquesolid, gradient, image, image-filtered, glyph, Uber6
EnginePipeline::StripDestOutsolid, Uber2
EnginePipeline::StripDepthDestOutsolid, Uber2
clear, copy, filtertheir own program3

— 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

VariantTargetBlendDepth
EnginePipeline::StripIntermediateINTERMEDIATE_FORMATpremultipliednone
EnginePipeline::StripAlphathe frame’s target formatpremultipliednone
EnginePipeline::StripDepthAlphathe frame’s target formatpremultipliedtest, no write
EnginePipeline::StripOpaquethe frame’s target formatnonetest and write
EnginePipeline::StripDestOutthe frame’s target formatDEST_OUT_BLENDnone
EnginePipeline::StripDepthDestOutthe frame’s target formatDEST_OUT_BLENDtest, 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§

EngineShaders
The engine’s compiled shader modules, by id, and the one explicit layout every strip pipeline is built against.

Enums§

EnginePipeline
One of the engine’s render pipelines.
EngineShaderModule
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_CLASSES are 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_PIPELINE is 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 u16 pair packed into one u32, 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 — eight u32s, 32 bytes per instance.
warm_up
Queues the warm_up_descs list for background compilation.
warm_up_descs
The full warm-up list: every pipeline in EnginePipeline::ALL (each strip state’s Uber program), then WARM_CLASSES across WARM_CLASS_STATES, described against target_format.
warm_up_descs_for
warm_up_descs with the kill switch stated rather than read: with uber_only set the list is exactly the nine Uber states.