implements PLN-0088
This commit is contained in:
parent
340442d374
commit
fae4e9a9fd
@ -18,7 +18,7 @@
|
||||
{"type":"discussion","id":"DSC-0002","status":"done","ticket":"palette-management-in-studio","title":"Palette Management in Studio","created_at":"2026-03-26","updated_at":"2026-04-23","tags":["studio","legacy-import","palette-management","tile-bank","packer-boundary"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0042","file":"discussion/lessons/DSC-0002-palette-management-in-studio/LSN-0042-schema-driven-palette-authoring-and-local-metadata-events.md","status":"done","created_at":"2026-04-23","updated_at":"2026-04-23"}]}
|
||||
{"type":"discussion","id":"DSC-0003","status":"done","ticket":"packer-docs-import","title":"Import docs/packer into discussion-framework artifacts","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["packer","migration","discussion-framework","docs-import"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0009","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0009-mental-model-packer-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0010","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0010-asset-identity-and-runtime-contract-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0011","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0011-foundations-workspace-runtime-and-build-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0012","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0012-runtime-ownership-and-studio-boundary-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0013","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0013-metadata-convergence-and-runtime-sink-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0014","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0014-pack-wizard-summary-validation-and-pack-execution-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0015","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0015-tile-bank-packing-contract-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0017","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0017-packer-docs-import-pattern.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"}]}
|
||||
{"type":"discussion","id":"DSC-0004","status":"abandoned","ticket":"tilemap-and-metatile-runtime-binary-layout","title":"Tilemap and Metatile Runtime Binary Layout","created_at":"2026-03-26","updated_at":"2026-04-24","tags":["packer","legacy-import","tilemap","metatile","runtime-layout"],"agendas":[{"id":"AGD-0004","file":"AGD-0004-tilemap-and-metatile-runtime-binary-layout.md","status":"abandoned","created_at":"2026-03-26","updated_at":"2026-04-24","_override_reason":"Explicit user request on 2026-04-24 to abandon AGD-0004 because the agenda is no longer valid."}],"decisions":[],"plans":[],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0005","status":"open","ticket":"variable-tile-bank-palette-serialization","title":"Variable Glyph Bank Palette Serialization","created_at":"2026-03-26","updated_at":"2026-07-14","tags":["packer","legacy-import","glyph-bank","palette-serialization","rgba8888","runtime-alignment"],"agendas":[{"id":"AGD-0005","file":"AGD-0005-variable-tile-bank-palette-serialization.md","status":"accepted","created_at":"2026-03-26","updated_at":"2026-07-14"}],"decisions":[{"id":"DEC-0038","file":"DEC-0038-variable-glyph-bank-palette-serialization.md","status":"accepted","created_at":"2026-07-14","updated_at":"2026-07-14","ref_agenda":"AGD-0005"}],"plans":[{"id":"PLN-0087","file":"PLN-0087-variable-glyph-palette-spec-propagation.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]},{"id":"PLN-0088","file":"PLN-0088-variable-glyph-palette-metadata-and-count-model.md","status":"open","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]},{"id":"PLN-0089","file":"PLN-0089-variable-glyph-palette-payload-emission-and-size-accounting.md","status":"open","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]},{"id":"PLN-0090","file":"PLN-0090-studio-and-packer-projections-for-variable-glyph-palettes.md","status":"open","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]},{"id":"PLN-0091","file":"PLN-0091-variable-glyph-palette-tests-fixtures-and-final-validation.md","status":"open","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]}],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0005","status":"open","ticket":"variable-tile-bank-palette-serialization","title":"Variable Glyph Bank Palette Serialization","created_at":"2026-03-26","updated_at":"2026-07-14","tags":["packer","legacy-import","glyph-bank","palette-serialization","rgba8888","runtime-alignment"],"agendas":[{"id":"AGD-0005","file":"AGD-0005-variable-tile-bank-palette-serialization.md","status":"accepted","created_at":"2026-03-26","updated_at":"2026-07-14"}],"decisions":[{"id":"DEC-0038","file":"DEC-0038-variable-glyph-bank-palette-serialization.md","status":"accepted","created_at":"2026-07-14","updated_at":"2026-07-14","ref_agenda":"AGD-0005"}],"plans":[{"id":"PLN-0087","file":"PLN-0087-variable-glyph-palette-spec-propagation.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]},{"id":"PLN-0088","file":"PLN-0088-variable-glyph-palette-metadata-and-count-model.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]},{"id":"PLN-0089","file":"PLN-0089-variable-glyph-palette-payload-emission-and-size-accounting.md","status":"open","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]},{"id":"PLN-0090","file":"PLN-0090-studio-and-packer-projections-for-variable-glyph-palettes.md","status":"open","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]},{"id":"PLN-0091","file":"PLN-0091-variable-glyph-palette-tests-fixtures-and-final-validation.md","status":"open","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]}],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0006","status":"done","ticket":"pbs-game-facing-asset-refs-and-call-result-discard","title":"PBS Game-Facing Asset References and Ignored Call Result Lowering","created_at":"2026-03-27","updated_at":"2026-03-30","tags":["compiler","pbs","ergonomics","lowering","runtime","asset-identity","expression-statements"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0024","file":"discussion/lessons/DSC-0006-pbs-game-facing-asset-refs-and-call-result-discard/LSN-0024-addressable-surface-host-metadata-and-ignored-value-discipline.md","status":"done","created_at":"2026-03-30","updated_at":"2026-03-30"}]}
|
||||
{"type":"discussion","id":"DSC-0007","status":"done","ticket":"pbs-learn-to-discussion-lessons-migration","title":"Migrate PBS Learn Documents into Discussion Lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","migration","discussion-framework","lessons","learn-prune"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0018","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0018-pbs-ast-and-parser-contract-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0019","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0019-pbs-name-resolution-and-linking-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0020","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0020-pbs-runtime-values-identity-memory-boundaries-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0021","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0021-pbs-diagnostics-and-conformance-governance-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0022","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0022-pbs-globals-lifecycle-and-published-entrypoint-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]}
|
||||
{"type":"discussion","id":"DSC-0008","status":"done","ticket":"pbs-low-level-asset-manager-surface","title":"PBS Low-Level Asset Manager Surface for Runtime AssetManager","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","runtime","asset-manager","host-abi","stdlib","asset"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0023","file":"discussion/lessons/DSC-0008-pbs-low-level-asset-manager-surface/LSN-0023-lowassets-runtime-aligned-sdk-surface.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]}
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
id: PLN-0088
|
||||
ticket: variable-tile-bank-palette-serialization
|
||||
title: Variable Glyph Palette Metadata and Count Model
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-07-14
|
||||
completed:
|
||||
completed: 2026-07-14
|
||||
ref_decisions:
|
||||
- DEC-0038
|
||||
tags:
|
||||
@ -121,11 +121,11 @@ the field that runtime consumes.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Packer computes runtime-effective `palette_count` from declared palettes.
|
||||
- [ ] `palette_count` is validated as `1..=64`.
|
||||
- [ ] Sparse palette ids are not remapped.
|
||||
- [ ] `palette_authored` is not treated as runtime-effective metadata.
|
||||
- [ ] Read/details projections do not expose stale fixed-count assumptions.
|
||||
- [x] Packer computes runtime-effective `palette_count` from declared palettes.
|
||||
- [x] `palette_count` is validated as `1..=64`.
|
||||
- [x] Sparse palette ids are not remapped.
|
||||
- [x] `palette_authored` is not treated as runtime-effective metadata.
|
||||
- [x] Read/details projections do not expose stale fixed-count assumptions.
|
||||
|
||||
## Dependencies
|
||||
|
||||
|
||||
@ -38,11 +38,11 @@ import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class FileSystemPackerWorkspaceService implements PackerWorkspaceService {
|
||||
private static final int GLYPH_BANK_PALETTE_COUNT = 64;
|
||||
private static final int GLYPH_BANK_MAX_PALETTE_COUNT = 64;
|
||||
private static final int GLYPH_BANK_COLORS_PER_PALETTE = 16;
|
||||
private static final int GLYPH_BANK_RGBA8888_BYTES_PER_COLOR = 4;
|
||||
private static final int GLYPH_BANK_PALETTE_BYTES =
|
||||
GLYPH_BANK_PALETTE_COUNT * GLYPH_BANK_COLORS_PER_PALETTE * GLYPH_BANK_RGBA8888_BYTES_PER_COLOR;
|
||||
GLYPH_BANK_MAX_PALETTE_COUNT * GLYPH_BANK_COLORS_PER_PALETTE * GLYPH_BANK_RGBA8888_BYTES_PER_COLOR;
|
||||
private static final String SCENE_BANK_SUPPORT_FILE = "scene-bank.studio.json";
|
||||
private static final String SCENE_PACK_REQUEST_FILE = "scene-bank.pack-request.json";
|
||||
|
||||
@ -1125,6 +1125,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
});
|
||||
|
||||
final byte[] packedPixels = packNibbles(sheetPixels);
|
||||
final int paletteCount = runtimeGlyphPaletteCount(declaration);
|
||||
final byte[] paletteBytes = emitGlyphBankPalettes(declaration);
|
||||
final byte[] payload = new byte[packedPixels.length + paletteBytes.length];
|
||||
System.arraycopy(packedPixels, 0, payload, 0, packedPixels.length);
|
||||
@ -1134,7 +1135,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
runtimeMetadata.put("tile_size", tileSize);
|
||||
runtimeMetadata.put("width", width);
|
||||
runtimeMetadata.put("height", height);
|
||||
runtimeMetadata.put("palette_count", GLYPH_BANK_PALETTE_COUNT);
|
||||
runtimeMetadata.put("palette_count", paletteCount);
|
||||
runtimeMetadata.put("palette_authored", countAuthoredGlyphBankPalettes(declaration));
|
||||
runtimeMetadata.put("codec", Map.of());
|
||||
declaration.outputMetadata().forEach((key, value) -> {
|
||||
@ -1288,7 +1289,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
continue;
|
||||
}
|
||||
final int paletteIndex = indexNode.intValue();
|
||||
if (paletteIndex < 0 || paletteIndex >= GLYPH_BANK_PALETTE_COUNT) {
|
||||
if (paletteIndex < 0 || paletteIndex >= GLYPH_BANK_MAX_PALETTE_COUNT) {
|
||||
continue;
|
||||
}
|
||||
final JsonNode rgbaNode = paletteNode.path("rgba8888");
|
||||
@ -1313,6 +1314,40 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
return palettesNode instanceof ArrayNode palettesArray ? palettesArray.size() : 0;
|
||||
}
|
||||
|
||||
private int runtimeGlyphPaletteCount(PackerAssetDeclaration declaration) {
|
||||
final JsonNode palettesNode = declaration.outputPipelineMetadata().get("palettes");
|
||||
if (!(palettesNode instanceof ArrayNode palettesArray) || palettesArray.isEmpty()) {
|
||||
throw new IllegalStateException("Glyph packing requires at least one declared pipeline palette.");
|
||||
}
|
||||
int maxPaletteIndex = -1;
|
||||
final Set<Integer> seen = new HashSet<>();
|
||||
for (JsonNode declarationNode : palettesArray) {
|
||||
final JsonNode indexNode = declarationNode.path("index");
|
||||
final JsonNode paletteNode = declarationNode.path("palette");
|
||||
if (!indexNode.isInt() || indexNode.intValue() < 0) {
|
||||
throw new IllegalStateException("Glyph packing requires non-negative integer palette indices.");
|
||||
}
|
||||
final int paletteIndex = indexNode.intValue();
|
||||
if (!seen.add(paletteIndex)) {
|
||||
throw new IllegalStateException("Glyph packing requires unique palette indices.");
|
||||
}
|
||||
if (paletteIndex >= GLYPH_BANK_MAX_PALETTE_COUNT) {
|
||||
throw new IllegalStateException("Glyph packing supports palette indices in 0.."
|
||||
+ (GLYPH_BANK_MAX_PALETTE_COUNT - 1) + ".");
|
||||
}
|
||||
if (!paletteNode.path("rgba8888").isArray()) {
|
||||
throw new IllegalStateException("Glyph packing requires rgba8888 entries in each declared palette.");
|
||||
}
|
||||
maxPaletteIndex = Math.max(maxPaletteIndex, paletteIndex);
|
||||
}
|
||||
final int paletteCount = maxPaletteIndex + 1;
|
||||
if (paletteCount < 1 || paletteCount > GLYPH_BANK_MAX_PALETTE_COUNT) {
|
||||
throw new IllegalStateException("Glyph packing requires palette_count in 1..="
|
||||
+ GLYPH_BANK_MAX_PALETTE_COUNT + ".");
|
||||
}
|
||||
return paletteCount;
|
||||
}
|
||||
|
||||
private byte[] encodeScenePackRequest(ScenePackRequest request) {
|
||||
final ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
writeBytes(output, new byte[]{'S', 'C', 'N', 'E'});
|
||||
|
||||
@ -324,6 +324,14 @@ public final class PackerAssetDeclarationParser {
|
||||
"Field 'output.pipeline.palettes' contains duplicate palette index " + indexNode.intValue() + ".",
|
||||
manifestPath,
|
||||
true));
|
||||
} else if (indexNode.intValue() >= MAX_PIPELINE_PALETTES) {
|
||||
diagnostics.add(new PackerDiagnostic(
|
||||
PackerDiagnosticSeverity.ERROR,
|
||||
PackerDiagnosticCategory.STRUCTURAL,
|
||||
"Field 'output.pipeline.palettes' cannot declare palette index " + indexNode.intValue()
|
||||
+ "; maximum supported index is " + (MAX_PIPELINE_PALETTES - 1) + ".",
|
||||
manifestPath,
|
||||
true));
|
||||
}
|
||||
|
||||
if (!paletteNode.isObject()) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user