prometeu-studio/discussion/lessons/DSC-0005-variable-glyph-bank-palette-serialization/LSN-0054-runtime-owned-variable-glyph-palette-counts.md
bQUARKz ad2ae00f39
All checks were successful
JaCoCo Coverage #### Project Overview No changes detected, that affect the code coverage. * Line Coverage: 61.08% (17147/28073) * Branch Coverage: 51.93% (6599/12707) * Lines of Code: 28073 * Cyclomatic Complexity: 11234 #### Quality Gates Summary Output truncated.
Test / Build skipped: 15, passed: 583
Intrepid/Prometeu/Studio/pipeline/head This commit looks good
Variable Glyph Bank Palette Serialization
2026-07-14 16:53:20 +01:00

5.3 KiB

id ticket title created tags
LSN-0054 variable-tile-bank-palette-serialization Runtime-Owned Variable Glyph Palette Counts 2026-07-14
packer
studio
glyph-bank
palette-serialization
rgba8888
runtime-alignment

Runtime-Owned Variable Glyph Palette Counts

Original Problem

The first RGBA8888 alignment wave made Studio and packer emit palette colors in the runtime channel order, but it deliberately kept the old fixed glyph-bank palette payload shape. Every GLYPH/indexed_v1 payload still reserved room for 64 palettes, so the emitted palette block was always 64 * 16 * 4 = 4096 bytes even when the bank used one or a few palettes.

That fixed shape left two problems:

  • packer output was larger than the runtime needed;
  • palette_count could not be trusted as the effective runtime resident count, because the payload still looked like a 64-palette bank.

Runtime later accepted the variable-palette protocol for GLYPH/indexed_v1. Studio and packer had to follow that runtime-owned wire contract instead of keeping a stale producer-side convention.

Consolidated Decision

Runtime owns the GLYPH/indexed_v1 protocol. Studio and packer are downstream producers of runtime-conforming assets.pa payloads.

The accepted rule is:

  1. palette_count is the effective serialized and resident palette count.
  2. palette_count must be in the inclusive range 1..=64.
  3. Palette bytes are exactly palette_count * 16 * 4.
  4. size = ceil(width * height / 2) + palette_count * 16 * 4.
  5. decoded_size = width * height + palette_count * 16 * 4.
  6. Palette bytes remain RGBA8888 in R, G, B, A order.
  7. GLYPH/indexed_v1 remains the format name. This change does not introduce GLYPH/indexed_v2.
  8. There is no compatibility mode for the old fixed 64-palette padding.
  9. Palette IDs remain direct. There is no sparse-to-dense remapping metadata in v1.
  10. palette_authored, when present, is informative tooling metadata only.

Implementation Result

The packer specs now define palette_count as runtime-effective metadata for glyph banks and describe the variable palette payload in the build-artifact contract. Studio-facing asset workspace docs now keep palette_count distinct from any authored or tooling-only palette count.

The packer metadata model derives and validates effective palette count instead of hard-coding 64. Sparse direct palette IDs are preserved by materializing intermediate palette slots up to the highest referenced ID; those slots are not a remapping table.

Payload emission now writes exactly the effective number of palette slots. Size and decoded-size accounting use the same formula as the runtime contract, so metadata, asset-table entries, and payload lengths agree.

Tests and fixtures cover palette_count = 1, intermediate counts, 64, sparse direct identity, invalid counts above 64, RGBA8888 byte order, and payload size accounting. Final validation passed with:

  • discussion validate;
  • ./gradlew :prometeu-packer:prometeu-packer-v1:test;
  • ./gradlew build.

Practical Examples

A 256 by 256 glyph bank with one palette has:

pixel bytes = ceil(256 * 256 / 2) = 32768
palette bytes = 1 * 16 * 4 = 64
size = 32768 + 64 = 32832
decoded_size = 256 * 256 + 64 = 65600

A bank that references palette ID 6 has an effective palette_count of at least 7. Palette slots 0 through 6 exist in the serialized palette block. If some lower slots were not authored directly, they are deterministic placeholders; palette 6 is not remapped to palette 0.

Common Pitfalls and Anti-patterns

  • Do not infer runtime payload length from the maximum allowed palette count. 64 is the upper bound, not the default emitted count.
  • Do not use palette_authored as a runtime-effective field. It may describe a tool view, but runtime loads palette_count.
  • Do not preserve fixed 4096-byte palette padding for banks with fewer than 64 effective palettes.
  • Do not create GLYPH/indexed_v2 to carry this change. The accepted runtime protocol keeps the v1 format name.
  • Do not introduce sparse-to-dense palette remapping metadata. Existing scene, sprite, packer, and runtime references use direct palette identity.
  • Do not combine RGBA8888 channel-order work with palette-count semantics. RGBA8888 defines each color value; palette_count defines how many palettes are serialized and resident.

References

  • Runtime decision: ../runtime/discussion/workflow/decisions/DEC-0041-variable-glyph-bank-palette-protocol.md.
  • Studio/packer decision: discussion/workflow/decisions/DEC-0038-variable-glyph-bank-palette-serialization.md.
  • PLN-0087: Variable glyph palette spec propagation.
  • PLN-0088: Variable glyph palette metadata and count model.
  • PLN-0089: Variable glyph palette payload emission and size accounting.
  • PLN-0090: Studio and packer projections for variable glyph palettes.
  • PLN-0091: Variable glyph palette tests, fixtures, and final validation.
  • Prior lesson: discussion/lessons/DSC-0038-studio-packer-rgba8888-asset-pipeline/LSN-0053-rgba8888-is-the-canonical-studio-packer-palette-contract.md.
  • docs/specs/packer/3. Asset Declaration and Virtual Asset Contract Specification.md.
  • docs/specs/packer/4. Build Artifacts and Deterministic Packing Specification.md.
  • docs/specs/studio/4. Assets Workspace Specification.md.