The phosh top-right strip is the FIRST GMEM tile, restored with the previous process's MSAA registers -- fd5 tile init never programs them
scope: soc:msm8998 · severity: finding · confidence: proven · subsystem: gpu
The question – with a browser maximised, the phosh statusbar from x = 1024 rightwards fills with shredded browser pixels. Handoff of 2026-09-03 AM had it down as “the clipped third GMEM bin column” after five refuted hypotheses.
The answer – fd5_emit_tile_init() programs zs and mrt for the GMEM pass
but not the sample count. emit_msaa() is first called from
fd5_emit_tile_renderprep(), which runs after the first tile’s mem2gmem. So
the binning pass and the first restore of every batch run with whatever
RB_RAS_MSAA_CNTL / RB_DEST_MSAA_CNTL (and the GRAS/TPL1 pair) held when the
previous submit ended. On a5xx nothing restores register state between
submits, so with a second process on the GPU that is its state. WebKit’s
Skia mask passes leave 4x behind; the restore then copies with a four-sample
layout and the undamaged rows of the first tile come back sample-interleaved
– the same shredding as the unresolved MSAA store of
a5xx-gmem-never-resolves-multisample-buffers, which is what the pictures
always looked like. fd5_emit_sysmem_prep() has called emit_msaa() all
along; the tile init simply never did.
Why it looked like the third column. The tile order is an ‘S’
(freedreno_gmem.c, “Swap the order of alternating rows”): the top row runs
right to left, so the first tile rendered is (1024, 0). FD_MESA_DEBUG=nosbin
disables the swap and the corruption moved to the top-left tile (left panel
region 40/40, right strip 0/40).
Why only undamaged pixels, and only with some clients. Damaged pixels are
redrawn after the restore; PHOC_DEBUG=damage-whole measured 0/40. A client
whose submits end single-sampled (glmark2, a single-layer page, software
rendering) leaves nothing to inherit: all 0/40.
How the fix was found once the register diff ran dry. A diagnostic mesa
(vendor-patches/mesa/diagnostic/a5xx-hack-tile-init.patch) took an
FD5_HACK bitmask from ~/.phoshdebug and switched candidate operations on
at tile init, so one 20-minute build served nine 5-minute arms:
| bits | what | right strip |
|---|---|---|
| 0x3fb | everything | 0/40 |
| 0 | nothing (control on the same build) | 40/40 |
| 0x003 | fast clear of tile 0 GMEM; restore twice | 40/40 |
| 0x218 | RB_CNTL in GMEM mode; WFI after mode switch; CCU_CNTL toggle | 40/40 |
| 0x1e0 | CCU flush; UCHE inval; CCU inval; MSAA regs | 0/40 |
| 0x020 / 0x040 / 0x080 | CCU flush / UCHE inval / CCU inval, singly | 40/40, 37/40, 40/40 |
| 0x100 | emit_msaa(ring, pfb->samples) |
0/40 |
The second restore not helping is what separated “the blit is dropped” from “the blit copies wrongly”.
Measurement traps found today, on top of the AM handoff’s:
- The stddev oracle is blind to a flat non-black panel; every clean arm was
re-checked for
mean == 0. glmark2’s last tile is bright, so its 1/35 is a real negative. - On the charger the battery icon enters the sampled strip: score
CROP=230x75+0+0(x 1024..1253), or a clean panel reads 100%. echo N > $FD_RD_DUMP_PATH/phoc_triggerdoes nothing: phoc opens the GPU twice, one close unlinks the trigger, the submitting device reads a deleted inode.tools/repro/a5xx-gmem/rd-trigger.shwrites through/proc/<pid>/fd.- greetd’s
initial_sessiononly fires when greetd starts; afterterminate-sessionyou get the greeter, and the relogin needs the account password (TK_LOGIN_PASSWORD, kept in a 0600 file). - A register-state diff of the previous submit is the right instrument, but read the whole survivor list: MSAA was in it from the start and was discounted because 7 of 8 captured WebKit submits ended single-sampled.
Fix: pmaports/temp/mesa/a5xx-tile-init-msaa.patch, one call. Upstreamable
as is.
