Skip to content

fix(mhires): pad absent percell slots with bg0 to stop out-of-palette (green) flicker on slow transports#40

Merged
kfox merged 2 commits into
mainfrom
fix/mhires-percell-bg0-filler
Jun 24, 2026
Merged

fix(mhires): pad absent percell slots with bg0 to stop out-of-palette (green) flicker on slow transports#40
kfox merged 2 commits into
mainfrom
fix/mhires-percell-bg0-filler

Conversation

@kfox

@kfox kfox commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Problem

On the mhires percell path with a small forced palette (e.g. force_palette_indices = [0, 4, 6, 14]), cells flicker with green squares — a color that isn't in the allowed set — and letterboxed video shows the all-bg0 edge cells flashing (reads as a "flashing border"). Pronounced on the TeensyROM serial backend.

Root cause

Each 4×8 cell picks its top-3 non-bg0 colors with argpartition. A cell with fewer than 3 distinct non-bg0 colors present — the norm under a small forced palette, where most cells are mostly bg0 with 0–2 accents — left the surplus slots holding arbitrary zero-count palette indices. For an all-bg0 cell, argpartition returns index 5 (green) (among others) straight into the screen/color-RAM nibbles.

In steady state those filler slots are never selected (a present pixel resolves to its own in-set color), so on a fast transport the garbage stays invisible. But push() ships screen ($0400) / color ($D800) / bitmap ($2000) as three non-atomic writes. On a slow, ack-gated transport the VIC can read a new bitmap byte against a still-stale screen/color byte mid-frame and briefly render the garbage filler — the green-square flicker and the flashing letterbox edges.

Fix

Replace any per-cell pick whose smoothed count is 0 (never present in that cell) with bg0. Screen/color RAM then only ever carries colors genuinely present in the cell — nothing outside the source's set can leak — and the absent slots become a deterministic bg0, so present colors stop churning slots frame-to-frame. bg0 in a filler slot is a harmless duplicate (the %00 code already reaches it; the per-pixel argmin breaks ties to the real bg0 at slot 0).

No change to rendered output on fast transports — the filler slots were never selected there.

Verification

Offline, driving the real compose path with an mhires + percell + force_palette [0,4,6,14] config over 200 frames:

frames leaking an out-of-palette index into screen/color RAM leaked indices
pre-fix 200/200 [1, 5, 15] (5 = green)
post-fix 0/200

Tests

  • New PercellFillerSafetyTest: screen/color RAM never carries a color outside the cell's present set; an all-bg0 cell collapses to solid bg0.
  • Rewrote the stale excludes_bg0_from_per_cell_picks test to the correct invariant: bg0 fills only unused slots and never displaces a genuinely-present color.

Also in this PR

  • test(menu): wrap an expected REU-staged "panel not drawn" WARNING in assertLogs — it was printing on every full-suite run and burying real failures.

kfox added 2 commits June 23, 2026 21:39
… (green) flicker on slow transports

The percell path picks each 4×8 cell's top-3 non-bg0 colors via argpartition.
A cell with fewer than 3 distinct non-bg0 colors present — mostly-bg0 cells,
which are the norm under a small forced palette like [0,4,6,14] — left the
surplus slots holding ARBITRARY zero-count palette indices. For an all-bg0
cell argpartition literally picks index 5 (green), plus 1/15, into the
screen/color RAM nibbles.

In steady state those filler slots are never selected (present pixels resolve
to their own in-set color), so on the U64's fast TCP DMA the garbage stays
invisible. But push() ships screen ($0400) / color ($D800) / bitmap ($2000)
as three NON-ATOMIC writes; on the TeensyROM serial transport (~10 KB/frame,
ack-gated, far slower) the VIC reads a new bitmap byte against a still-stale
screen/color byte mid-frame and briefly renders the garbage filler — the
green-square flicker, and on letterboxed video the all-bg0 edge cells flashing
("flashing border").

Fix: replace any per-cell pick whose smoothed count is 0 (never present in the
cell) with bg0. screen/color RAM then only ever carries colors genuinely
present in the cell — nothing outside the source's set can leak — and the
absent slots become a deterministic bg0, so present colors stop churning slots.
bg0 in a filler slot is a harmless duplicate (the %00 code already reaches it;
argmin breaks ties to the real bg0 at slot 0). No change to rendered output on
fast transports — the filler slots were never selected.

Verified offline against the actual config (mhires + percell + force_palette
[0,4,6,14], Rush - Subdivisions): pre-fix 200/200 frames leak out-of-palette
indices into screen/color RAM (incl. 5=green); post-fix 0/200.

Tests: new PercellFillerSafetyTest (no out-of-present-set leak; all-bg0 cell is
solid bg0); rewrote the stale "excludes bg0 from per-cell picks" test to the
correct invariant (bg0 fills only unused slots, never displaces a real color).
test_staged_bitmap_skips_panel exercised the menu's "panel not drawn on
REU-staged bitmap scenes" path, which logs a one-shot WARNING — left
unasserted, it printed on every full-suite run and buried real failures.
Wrap the call in assertLogs("c64cast.menu", WARNING) so the message is
verified AND kept off the console.
@kfox kfox merged commit 29487b0 into main Jun 24, 2026
5 checks passed
@kfox kfox deleted the fix/mhires-percell-bg0-filler branch June 24, 2026 02:49
@codecov

codecov Bot commented Jun 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.57%. Comparing base (e3d3aea) to head (7943c76).
⚠️ Report is 3 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main      #40   +/-   ##
=======================================
  Coverage   79.56%   79.57%           
=======================================
  Files          68       68           
  Lines       12858    12861    +3     
  Branches     1898     1898           
=======================================
+ Hits        10231    10234    +3     
  Misses       2188     2188           
  Partials      439      439           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

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