--- id: LSN-0046 ticket: rgba8888-framebuffer-and-pixel-format-direction title: Pixel Format Contracts Must Move as One Surface created: 2026-05-23 tags: [gfx, framebuffer, rgba8888, abi, assets, host] decision: DEC-0029 --- ## Context The runtime migrated its graphics contract from RGB565 to RGBA8888. The important discovery was that RGB565 was not only a renderer storage detail. It had become a cross-cutting contract spanning VM values, syscalls, HAL types, renderer buffers, asset palette bytes, host presentation, tests, and specs. Treating the change as a local buffer replacement would have left contradictory contracts behind. The successful migration split the work into small plans, but each plan still respected one global rule: a pixel format is a published surface, not an implementation token. ## Key Decisions ### RGBA8888 Runtime Pixel Format Contract **What:** RGBA8888 became the single logical color and physical framebuffer contract. RGB565 compatibility surfaces were removed instead of deprecated as canonical aliases. **Why:** Keeping RGB565 as compatibility would have preserved the old coupling between logical color, framebuffer storage, asset palette encoding, and host presentation. A single RGBA8888 contract made the runtime easier to reason about and aligned with alpha-capable UI and modern host presentation. **Trade-offs:** The migration intentionally broke existing RGB565 assets and tests. It also doubled framebuffer byte width. The payoff was removing support for two simultaneous color models and making alpha an ordinary property of color instead of a special case. ## Patterns and Algorithms Move published contracts before code. Specs, ABI docs, and public docs were updated first so implementation work had a stable target. Keep API names format-neutral when the runtime supports only one format. Names such as `GfxClear565` create permanent compatibility residue. The canonical surface should be `GfxClear`; RGBA8888 is the contract behind it. Use the alpha channel as data, not a magic index. Palette indices are ordinary indices. Transparency is represented by the RGBA alpha channel of the resolved palette entry rather than by reserving palette index `0`. Validate with residue scans. The final pass searched for RGB565, `Gfx*565`, RGB565 palette sizing, host conversion helpers, and `u16` framebuffer surfaces. Remaining hits had to be negative documentation or unrelated `u16` usage. ## Pitfalls Pixel format names leak into places that look unrelated: generator fixtures, cartridge payload size calculations, syscall metadata, host copy utilities, and test assembly snippets. Endian assumptions are easy to hide when using `u32`. The contract must say RGBA channel order explicitly, and host presentation code must unpack channels by shifts instead of relying on native memory layout. Compatibility helpers are useful only as short-lived scaffolding. If they are left in normal paths, they become a second contract. ## Takeaways - A framebuffer format is a runtime-wide contract once it reaches ABI, assets, host presentation, or tests. - Format-suffixed public APIs should be removed when the format is no longer a selectable backend. - Alpha should be modeled as color data, not as a reserved palette index. - A staged migration can still enforce one final contract when each plan has a residue scan and acceptance criteria.