Skip to content

perf(P5): cache native palette uploads and pixel-aligned zone quads - #293

Closed
Roxas2712 wants to merge 22 commits into
UNDERdecoded:mainfrom
Roxas2712:codex/upstream-perf-p5
Closed

Roxas2712 wants to merge 22 commits into
UNDERdecoded:mainfrom
Roxas2712:codex/upstream-perf-p5

Conversation

@Roxas2712

@Roxas2712 Roxas2712 commented Oct 26, 2026 •

Copy link
Copy Markdown

Each native palette zone currently allocates effective-color/uniform tables and sends all four uniforms; each zone also draws the full canvas through a scissor. P5 resolves native palette rows by index, compares twelve normalized floats in a per-shader private batch cache, reuses uniform vectors and crops supported zones with one cached Quad/setViewport.

Cache validity is limited to one contiguous native blit. Every later blit starts with a full upload; public sendColors calls always resend, and consumer-provided table send methods still receive fresh vectors. Existing public palette/graphics wrappers disable batching assumptions. Public effectiveColors/permute return behavior, c0–c3 uniform names, zone records and pipeline interfaces remain unchanged. Existing copyright/licence notices are retained.

Quad geometry is limited to identity transforms, nearest filtering, positive integer framebuffer magnification/origins, matching target DPI and ordinary render targets. Unsupported layouts retain the original scissor path. LÖVE 11 logical truncation and LÖVE 12 physical rounding are matched explicitly; the latter uses the quad path only at target DPI 1/2/4. Initial fractional-zoom crops failed the GPU oracle and were restricted/corrected before qualification.

Locations: src/render/PaletteFX.lua:1238 (public send), :1253 (native batch), src/render/Renderer.lua:708 (quad eligibility), :737 (crop), :760 (blit), src/core/Performance.lua:53. Independent rollback: require("src.core.Performance").paletteZoneCache = false (default true).

Stacked on #291, which includes #290 and profiler prerequisite #272. P5-only commit: 28e158f.

Validation:

  • Fork: 430/430 engine suites, 3/3 SDK suites, 253/253 gallery checks; 200 contract comparisons / 400 isolated loaders against pinned reference 535a5ec.
  • Authenticated fixture phase: 13/14 entry points load; the remaining follower fixture reports the exact named missing sprite-module gap allowed at this intermediate phase. Full follower support remains open.
  • Public adaptation: 69/80 engine suites, matching all 29 inherited failure lines across the same eleven failing suites, with no added failure. SDK 3/3.
  • 491 focused checks cover all display modes, shade mapping, palette mutations, changed-row sends, raw shader writes between blits, public overrides/wrappers, fresh consumer vectors and crop mapping.
  • Native GPU readbacks: 432/432 byte-identical cases on each of LÖVE 11.5/OpenGL and LÖVE 12/Metal. OpenGL exercises 48 quad/384 fallback cases; Metal 16 quad/416 fallback cases. Cases include Red/Gold, display/shade modes, overlapping/true-color zones, clipping, transforms, three target densities, fractional origin/zoom and source filtering/density.

Native timings: macOS arm64, 60 Hz/VSync 1, 96 equal-palette zones, 120 warm-up + 120 measured frames per switch phase. Public F11 zone blit: OpenGL 0.41 → 0.22 ms/frame; Metal 16.37 → 16.35. Raw draw means: 0.445 → 0.243 and 16.376 → 16.364 ms/frame. Process CPU: 0.968 → 0.705 and 0.759 → 0.735 ms/frame. Heap growth with GC stopped over each 120-frame window: approximately 4368 → 49 KiB; this includes host/profiler work and is not retained memory. The equal-palette batch issues four Shader:send calls instead of 384. The fork's OpenGL run was slower, so no general FPS/CPU gain is established. Metal queue submission can absorb GPU/VSync waits. Full-game, Android, dedicated ARM-handheld and long-session: not measured.

Reproduce using the guarded tools/check_frame_pacing.py --palette-zones host. Raw F11 logs, measured-source fingerprints, API/backend rounding sources, both-tree measurements and limitations are described in docs/palette-zone-cache.md.

Contribution permission and credit: Roxas2712 contribution licence. Existing source notices and third-party rights remain effective.

Final composed acceptance and native Emerald profile

The completed composed fork runtime 9532ee36 passes 493/493 engine suites, 3 SDK suites, 253 gallery checks, 200 pinned API-3 comparisons and all 17 unchanged external entrypoints in complete mode. The complete public performance stack in #323 (runtime a620d0c7) passes 179/179 suites, 3 SDK suites, 253 gallery checks, 48 contract comparisons and all 17 complete entrypoints. It reuses repair PRs #128–#134 and #294. These results supplement the older standalone/baseline snapshots above; the eleven inherited failures are resolved on that final stack.

Real imported Emerald / Pacifidlog Town, actual native Game/Overworld and ordinary DRAMATIC_SHAPE loading on Apple M2 ARM64, LÖVE 12 Metal (profile source 79ff56cb): FrameProfile renderer: zone blit = 0.0215 → 0.0227 ms/frame, flag off → on, 240 measured frames per state and two real 120-frame profile windows. This is the real GBA zone path. It does not exercise GB c0–c3 uniforms; their separate byte/pixel and upload-count tests remain authoritative. The small change is within this sample's scene variability.

The guarded full-game starter freezes committed sources and uses a fresh owned ROM import/profile. It explicitly admits scripted QA into the existing native pacing/idle-GC body in its frozen copy; the timing implementation is unchanged. P1 uses live cap 60 / VSync 1, other phases the normalized native cap 160 / VSync off. The additional OpenGL replay passes all 14 native phases, then is stopped by the shared 20 GiB reserve during the optional voxel replay; no complete OpenGL voxel result is claimed.

These are sequential wall/submission-time samples, including slower values, not GPU completion times or a causal universal FPS gain. See the programme ledger and all samples/source hashes. Physical Android/Switch/handheld timings: not measured; the measured Mac is ARM64.

Current upstream qualification

Merged current upstream 8d5b4387; standalone 82/82 engine suites and all SDK suites pass at c8936955. Native Gen4 fog and poison-step bands are preserved across the cached zone-blit path (150 focused assertions). This early standalone host still lacks later acceptance tooling/fixtures and retains its inherited gallery failure; complete five-gate compatibility is qualified on the composed P9 stack (#323).

@Roxas2712 Roxas2712 closed this Nov 4, 2026
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