perf: char-boundary-split blitter; raise enemy cap to 8 - #4
Open
mpasternak wants to merge 1 commit into
Open
Conversation
Blitter (src/blit.asm): replace the per-row DOWN macro (~27 T of
boundary testing on every scanline) with a two-segment split around a
single, simplified boundary step. An 8-row sprite crosses a char-cell
boundary at most once, after exactly 8-(y&7) rows; bottom-clipped
sprites (192 % 8 == 0) never cross at all. Intra-cell advance is now a
bare `inc h`; wide paths use `inc l`/`dec l` (safe: gated on bx<31).
Measured render: 23,170 -> 21,438 T/frame (-7.5%) via z88dk-ticks.
Verified byte-identical to the old blitter by a differential sweep on
z88dk-ticks: 4 routines x bx {0,1,15,30,31} x sh 0..7 x all y 0..191
(30,720 calls) in both the scld_row_off-table and ZX128_PAGE_FLIP
builds -- identical buffer checksums.
Enemy cap (lockstep): MAX_ENEMIES 7 -> 8 in enemy.h,
enemy_update.asm, collide.asm, and the collision.c guard. The wave
table already asks for 8 enemies from wave 11, so the 8th enemy now
actually spawns. measure_main.c erases 1+MAX_ENEMIES sprites;
test_spawn_wave16_cap7 renamed to test_spawn_wave16_full (wave 16 no
longer clamps: 8 alive, 7 hunters).
Worst-case frame (wave-16 hunter mix, 8-cap, 2026-07-08): render
24.0k + enemies_update 8.0k + PT3 6.2k + collide 3.4k + player_hit
0.5k = 42.1k T subtotal, ~13k headroom to the ~55k budget -- the same
margin the old 7-cap had. Docs updated (perf-budget.md, CLAUDE.md),
including the stale ZX128_NO_MUSIC note (ZX128 pays PT3 every frame
since full parity).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Co i po co
Priorytet projektu to SMOOTH and FAST — ten PR odzyskuje T-states w najgorętszym miejscu klatki (render = ~58% budżetu) i wydaje je na 8. wroga na ekranie (tabela fal prosi o 8 od fali 11; dotąd przycinane do 7).
1. Blitter: char-boundary split (
src/blit.asm)Makro
DOWNpłaciło ~27 T testu granicy komórki znaku na każdym wierszu, choć 8-wierszowy sprite przekracza granicę co najwyżej raz — po dokładnie8-(y&7)wierszach, a sprite przycięty na dole (192 % 8 == 0) nie przekracza jej wcale. Pętle wierszy podzielone na dwa segmenty czystegoinc h(4 T) z jednym uproszczonym, bezwarunkowym krokiem granicznym pomiędzy; ścieżki "wide" używająinc l/dec l(bezpieczne: bramkowanebx<31).2. Cap wrogów 7 → 8 (lockstep)
enemy.h+ld b,7→8wenemy_update.asmicollide.asm+ guard wcollision.c+ harnessmeasure_main.c(9 operacji erase). Maski slotów pozostająu8(8 slotów = bity 0..7). Testtest_spawn_wave16_cap7→test_spawn_wave16_full(fala 16 już nie jest przycinana: 8 żywych, 7 hunterów).Pomiary (z88dk-ticks, worst case: fala 16, mix hunterów, 200 iter)
Netto: +1 wróg za +2,0k T — zapas (~13k T) praktycznie identyczny jak przy starym capie 7.
Weryfikacja
enemy_update.asm): 30 720 wywołań — 4 procedury × bx {0,1,15,30,31} × sh 0..7 × wszystkie y 0..191, w obu wariantach buildu (tabelascld_row_offiZX128_PAGE_FLIP) — sumy kontrolne bufora identyczne co do bajta.sh==0,y&7==0, pociski 2-wierszowe, liveness rejestrów, zakazy IY/EXX/SP) — zero blockerów/majorów../test/run.sh— wszystkie testy hostowe zielone.make all— 3 tapy (timex/zx128/zx48) budują się, walidatory layoutu "safe".docs/perf-budget.md(nowa tabela 8-cap, stare jako historia, poprawiona nieaktualna wzmianka oZX128_NO_MUSIC),CLAUDE.md(sekcja Performance budget).Do ręcznego sprawdzenia
make run-tc2048— dograć do fali 11+, zobaczyć 8 wrogów przy stabilnych 50 Hzmake run-zx128/make run-zx48— sanity flip/single-buffer🤖 Generated with Claude Code