fix(shape): clip descendants to ancestor Bounding regions - #112
Conversation
|
this branch causes GPU faults on my RX580 (your branch separately and also when merged with current master which touches adjacent code) |
|
when I log in with restore session on, and systemsettings was open, immediate GPU crash that takes the system down, every single time: |
|
Hypothesis: per-frame descriptor-pool exhaustion MAX_DESCRIPTOR_SETS_PER_FRAME = 1024 (crates/yserver/src/kms/vk/pipeline.rs:119) is not merely advisory — it sizes each per-output descriptor pool, via CompositePoolRing::new(..., MAX_DESCRIPTOR_SETS_PER_FRAME) at crates/yserver/src/kms/render/scene.rs:647. Every emitted CompositeDraw consumes one descriptor set (record_descriptor_allocations(built.scene.draws.len()), scene.rs:~1868). The 64-rectangle / bounding-box fallback this PR removes was therefore load-bearing for that budget, not only a performance guard: it bounded draws.len() per window. With it gone, one shaped window can emit thousands of draws — this PR's own regression test constructs a 65x65 vertical/horizontal stripe intersection, i.e. 4225 rects from a single window, 4x the entire per-output pool. Why this reproduces here and not on your setup — three configuration differences:
If a frame's descriptor demand exceeds the pool, sets are exhausted or recycled while a prior submission is still in flight; the GPU then dereferences a stale image descriptor and samples a recycled BO. That produces exactly the observed signature — a VM_CONTEXT1 protection fault reading from the texture cache, at a constant GART address, attributed to whichever process owned that memory. Which is why it looks like a client bug and why the named process varies. Verified: the constant and its role in sizing the pool, one descriptor set per draw, the cap removal, the 4225-rect test, KWin's full-framebuffer COW shape, and the 3/3-vs-clean A/B. Inferred: that descriptor exhaustion specifically is what corrupts. Cheap confirmation would be logging draws.len() per frame per output and checking whether it crosses 1024 shortly before the fault. Suggested direction Please don't reinstate the extents fallback — the reasoning for removing it is correct, replacing an exact region with its bounding box paints outside the mask and violates SHAPE. The problem is one draw per rectangle, so either:
The second is the more robust fix, since it also removes the per-frame cost of a complex mask rather than just making it affordable. |
|
This one will require some more work I guess. I should have a Polaris card to try, I just need a spare machine for these tests. Give me a few more days and I'll report back. |
Why
The SHAPE Bounding region applies to a window and its inferiors. The KMS
scene walk already accumulated ancestor geometry for parent clipping, but it
did not accumulate ancestor Bounding regions. A shaped parent therefore
clipped its own draw while an unshaped descendant could still paint outside
that shape.
This was visible on non-composited Plasma 6.6 on amdgpu/Strix Halo. KWin
shapes the panel frame into a rounded three-rectangle band inside a
3440x40frame, but the unshaped full-height wrapper child was emitted infull. Its background painted an opaque white band over the panel margin.
Enabling compositing hid the bug because KWin then sampled the shaped window
as a texture and the cut-out was carried by alpha.
The first extents-based attempt also demonstrated why preserving the exact
region matters: it left the four bounding-box corner pixels painted even
though they were outside the rounded mask.
What changed
emit_window_subtreeas absolute half-open rectangles.NoneversusSome([])distinction: no shaped ancestor isdifferent from an explicitly empty intersection that suppresses the
subtree.
regions are sorted, non-overlapping YX bands. This also prevents
overlapping client rectangles from becoming overlapping SrcOver draws in
a COW subtree.
canonical result once per emitted window.
exceed 64 rectangles, and vertical stripes intersecting horizontal stripes
can inherently produce the Cartesian product as disjoint output. Replacing
that exact result with its extents violates SHAPE by painting holes.
a candidate
Vec, and a shaped leaf with no shaped ancestor does not buildan unused absolute-region copy.
sampled_idsand presentation-damage snapshots once per window evenwhen one shape produces multiple draws.
Validation
The original panel case was verified on hardware, including the four
bounding-box corner pixels of the rounded mask. The review follow-ups add
regressions for:
All repository gates pass:
cargo +nightly fmtcargo clippy --all-targets -- -D warningscargo test --workspace