Skip to content

Render follow-ups drain: UiTransform paint + degraded effect groups (R1–R2)#71

Closed
intendednull wants to merge 2 commits into
mainfrom
followups-render
Closed

Render follow-ups drain: UiTransform paint + degraded effect groups (R1–R2)#71
intendednull wants to merge 2 commits into
mainfrom
followups-render

Conversation

@intendednull

Copy link
Copy Markdown
Owner

Second track of the follow-ups-drain campaign (docs/plans/2026-06-18-followups-drain.md): the actionable render follow-ups. Two slices, sequenced because both touch the shared PackedInstance float layout.

Slices

  1. 37ec57a UiTransform affine paint (rotate + scale) — render now consumes the full 2D affine from GlobalTransform (already composed by the transform bridge) instead of dropping all but translation, so rotated/scaled UI paints correctly. The affine basis is appended after the existing 13 floats so every field offset is unchanged (alpha stays float 7); named COLOR_FLOAT_OFFSET/ALPHA_FLOAT_OFFSET consts. Identity affine → byte-identical to before. Quad + shadow WGSL transform the corner about box-local origin. GPU rotate/scale reftest (#[ignore]). Residuals kept (not closed): transform-origin is a layout-side gap (elements pivot at top-left); skew/general Matrix bounded by the bridge's TRS decompose; perspective/Preserve3d/backface stay C-tier deferred.

  2. acf7311 degraded effect groups forward-composite flat — a budget-degraded group (no pooled target) used to vanish; it now folds group.opacity per-instance and draws flat (effect-compositor.md §2.3). Scoped to root-degraded groups — nested degraded children need a different draw path (composite into the parent's off-screen target), guarded by a debug_assert and filed as a new follow-up. Per-tier dirty gates prevent compounding alpha drift; glyph alpha uses the correct float index 11 (GLYPH_ALPHA_FLOAT_OFFSET), not quad's 7.

Verification

cargo fmt --all --check, cargo clippy --workspace --all-targets -D warnings, RUSTDOCFLAGS=-D warnings cargo doc --workspace --no-deps all clean; buiy_core + buiy_verify (the blast radius of the cross-crate PackedInstance growth) 162 ok / 0 failed. GPU reftests stay #[ignore] and compile; the lavapipe CI lane exercises them. Full OS test matrix runs here in CI.

🤖 Generated with Claude Code

intendednull and others added 2 commits June 18, 2026 14:40
Render now consumes the full 2D affine GlobalTransform already composed by the
write_buiy_transform bridge, instead of dropping everything but translation —
so a rotated/scaled UI element paints correctly instead of axis-aligned.

extracted_node_for reads global_transform.affine().matrix3 xy columns into a
new ExtractedNode.affine ([[f32;2];2]); it flows through PackedInstance, the
bucket raw record (now [f32;17]), packed_to_raw, the prepare RawBufferVec, the
quad vertex layout (stride 52->68, attrs at locations 8/9), and both quad +
shadow WGSL (corner = rect_pos + mat2x2(affine) * box-local corner).

PackedInstance constraint (campaign-review MAJOR, R2 depends on it): the affine
basis is APPENDED after the existing 13 floats, so every existing field offset
is unchanged — color stays float 4, alpha float 7. Named COLOR_FLOAT_OFFSET /
ALPHA_FLOAT_OFFSET consts now name those offsets (R2's degraded-group re-tint
reads alpha via ALPHA_FLOAT_OFFSET). Identity affine = [1,0,0,1] -> every
existing pixel/test byte-identical; 4 instance hex snapshots re-blessed with the
trailing identity floats. VertexOut slot 4 (rect_center, dead) becomes
frag_logical = the affine-transformed window corner for the clip-AABB discard.

GPU rotate/scale reftest added (#[ignore], compiles; human runs the GPU lane).

Scope = transform-paint only. Residuals kept (NOT closed): transform-origin is
a layout-side gap (compose_transform doesn't bake ui.origin, so elements pivot
at top-left until that lands); skew / general TransformMatrix::Matrix are bounded
by the bridge's TRS-only Transform::from_matrix decompose; perspective /
Preserve3d / BackfaceVisibility stay C-tier deferred. clip-and-transform.md §B.5
+ follow-ups.md narrowed to transform-paint LANDED with those residuals.

Cross-crate: buiy_verify PackedInstance/ExtractedNode sites + 3 snapshot tests
updated for the new field; clippy --workspace -D warnings clean; render suites +
buiy_verify green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DHcf8nQ9PTT3m5E7u3Q6XV
A degraded effect group (plan_allocation == false under RT-pool budget
pressure, no pooled target) previously had its members excluded from the flat
draw AND skipped in the step-1 group pass, so its quads + glyphs painted
nowhere (vanished). effect-compositor.md §2.3 mandates forward-compositing.

prepare_effect_groups now, for each ROOT degraded group (parent == None),
folds group.opacity into the ALPHA slot of every member instance in place and
merges its quad + glyph ranges into the flat ranges, so the node's flat window
draw paints them — the group dims exactly once instead of vanishing.

Three correctness points (plan-review majors):
- Scope = ROOT degraded only. A nested degraded child must composite into its
  PARENT's off-screen target, a different draw path (node-side) — conflating it
  with the window flat pass would paint in the wrong space/clip. Guarded by a
  debug_assert + filed as a new follow-up (render — nested degraded forward-
  composite into parent target).
- Per-tier dirty gates: the alpha fold runs iff the corresponding BUFFER was
  repacked this frame (quad fold on quad_dirty, glyph fold on glyph_dirty —
  the buffer-repack signals, not the wider partition signal), else a retained
  already-folded buffer would re-multiply and drift to black. The glyph RANGE
  merge gates on the wider partition signal, kept distinct from the fold.
- Glyph alpha is color[3] = float index 11 (new GLYPH_ALPHA_FLOAT_OFFSET in
  atlas/primitive.rs), NOT quad's ALPHA_FLOAT_OFFSET (7) — folding via the quad
  offset would corrupt glyph instances.

Rebases on R1's PackedInstance layout (quad alpha via ALPHA_FLOAT_OFFSET).
Latent today (64 MiB budget degrades nothing); fixtures force degradation via a
small RtPoolBudget. GPU degraded-paint test #[ignore] (compiles). Fixed 4
rustdoc -D-warnings intra-doc link errors (pub(crate) prepare_effect_groups ->
code spans; offset consts -> full-path links).

Tests: buiy_core + buiy_verify 162 ok / 0 failed; fmt + clippy --workspace +
doc --workspace clean. Final render-track slice.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DHcf8nQ9PTT3m5E7u3Q6XV
@intendednull

Copy link
Copy Markdown
Owner Author

Superseded: this PR was built on Bevy 0.18, but main has since migrated to 0.19-rc (the BSN campaign, #72) which rewrote the render pipeline. R1 (UiTransform affine paint) and R2 (degraded effect groups) are being re-applied fresh on the 0.19 render code in a new PR (both gaps confirmed still open on 0.19; the design ports verbatim since 0.19 left PackedInstance + WGSL untouched).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant