Skip to main content

Module clear

Module clear 

Source
Expand description

ClearRect: the platform-view hole punch, hoisted to the frame root and lowered to destination-out coverage.

frust_scene’s ClearRect erases an axis-aligned rectangle to full transparency, colour and alpha both, so an OS view hosted behind a translucent surface shows through a slot the app’s own opaque backdrop would otherwise seal. It is a real destination-clearing composite, not a skipped paint.

§Hoisting

A punch confined to the group it was recorded in would only erase that group’s own accumulated content — an app-root backdrop painted outside a scroll view’s clip survives the group composite and seals the hole again. So the punch is hoisted past every enclosing clip, layer and snapshot bracket to the frame root, bounded by the intersection of those brackets’ device-space bounds so a partially scrolled slot still clips to its viewport (punch_rect). At the root there is nothing left to hoist past, and the intersection is the punch itself.

§Destination-out, not a clear

The lowered form is coverage drawn with dst' = dst · (1 − src.a), which erases colour and alpha exactly where the source covers and leaves an uncovered pixel bit-identical. Deliberately not a clear op: a clear applies at whole-tile granularity, which bleeds the punch up to a tile past a tile-unaligned rectangle edge. Destination-out weights the erase by the source’s own coverage instead, so a pixel-aligned edge lands pixel-exact however it falls inside a tile, and a fully covered pixel reads exactly (0, 0, 0, 0) on a target that carries alpha.

§The wiring contract

This module produces the lowered form and stops there — a ClearPunch per surviving punch, naming the strips that carry its coverage, the device bounds they cover, and the painter-order depth it was hoisted from. Issuing them is the renderer’s, and is specified here so the pass that grows it has one place to read:

  1. One pass, at the punch’s own painter-order position. A punch is issued into the frame’s own target once everything recorded before the clear has been recorded, and before anything recorded after it is. The renderer does that by cutting the surface round at the punch’s depth — the ops before it end one pass, the punch pass follows, the ops after it resume in the next — so punches at one position are issued together and punches at different positions each get a pass. A punch past every op of the frame, which is what a ClearRect recorded last is, therefore lands after the last round exactly as an unconditionally-trailing pass would. They never touch an intermediate page: a punch inside an isolated layer was hoisted out of it.

  2. Fixed-function destination-out, over the strip program. A punch is a strip run like any other, so it goes through the same strip program and instance layout; only the blend state differs — src_factor: Zero, dst_factor: OneMinusSrcAlpha, operation: Add, for the colour and the alpha component. That computes dst · (1 − src.a), which is the COMPOSE_DEST_OUT arm of shaders/blend.wgsl evaluated for a premultiplied source, without a shader-side composite. It has to be fixed-function here: the blend module reads its backdrop with textureLoad, and the frame’s own target is not readable inside the pass that writes it. shaders/blend.wgsl remains the route for a composite between intermediate textures.

  3. Ordered by the cut, and depth-tested against the one pass the cut cannot order it against. Point 1’s cut is what orders the punch against everything drawn into the surface in a round: recorded before the clear, it is erased; recorded after it, it lands on top of the erase. That holds whether or not the content wrote depth, so an anti-aliased fringe, a translucent paint’s spans and a layer’s composite all survive a punch they were recorded over.

    One pass escapes the cut, and it is why the punch still carries the depth it was hoisted from: the frame’s opaque strips are recorded once, ahead of every round (see crate::renderer), so they hold post-clear coverage that no cut can put after the punch. The ordinary LessEqual test against the frame’s depth attachment restores exactly that ordering — opaque coverage recorded after the clear wrote a nearer depth and the punch fails against it, opaque coverage recorded before it is erased — and it is the only ordering the test is asked for. Everything else in the frame writes no depth, and needs none: the cut already put it on the correct side.

    Without a depth attachment at all there is no separate opaque pass to order against: every instance travels through the surface rounds in painter order, so the cut alone is the whole contract and nothing is erased that was recorded after the clear.

    One shape the cut cannot express is a bracket that straddles the clear. An isolated layer reaches the surface as a single composite carrying the deepest index inside it, so a layer holding content recorded both before and after the clear composites after the punch as a whole — the half of it recorded before the clear survives where the reference renderer, which closes and reopens the bracket around the punch, erases it. That is the engine’s one-composite-per-layer model, not this pass’s ordering.

  4. Skipped whole on a target that disregards alpha. Destination-out darkens colour as well as erasing alpha, so on an opaque presentation — where the erased alpha is disregarded — issuing the pass would leave a black rectangle where the display list says nothing should change. The punches are kept out of CompiledFrame::draws for exactly this reason: dropping the pass restores the frame exactly, which is the no-op the display list mandates there.

Structs§

ClearPunch
One hoisted clear, ready to be issued as destination-out coverage.
StagedPunch
A punch the walk has hoisted, held until the frame’s draws are done.

Functions§

device_bounds
A device-space rectangle on the u16 grid the strip pipeline addresses.
punch_rect
The device-space rectangle a clear punches, confined to every bracket open around it, or None when nothing of it survives.