dev/studio-packer-rgba8888-asset-pipeline #11

Merged
bquarkz merged 10 commits from dev/studio-packer-rgba8888-asset-pipeline into master 2026-05-24 21:46:24 +00:00
3 changed files with 33 additions and 29 deletions
Showing only changes of commit e17344af63 - Show all commits

View File

@ -1,5 +1,5 @@
{"type":"meta","next_id":{"DSC":39,"AGD":42,"DEC":38,"PLN":87,"LSN":53,"CLSN":1}}
{"type":"discussion","id":"DSC-0038","status":"open","ticket":"studio-packer-rgba8888-asset-pipeline","title":"Studio and Packer RGBA8888 Asset Pipeline Alignment","created_at":"2026-05-23","updated_at":"2026-05-23","tags":["studio","packer","assets","glyph-bank","palette","rgba8888","runtime-alignment"],"agendas":[{"id":"AGD-0041","file":"AGD-0041-studio-packer-rgba8888-asset-pipeline.md","status":"accepted","created_at":"2026-05-23","updated_at":"2026-05-23"}],"decisions":[{"id":"DEC-0037","file":"DEC-0037-studio-packer-rgba8888-asset-pipeline.md","status":"accepted","created_at":"2026-05-23","updated_at":"2026-05-23","ref_agenda":"AGD-0041"}],"plans":[{"id":"PLN-0081","file":"PLN-0081-rgba8888-packer-and-studio-spec-propagation.md","status":"done","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0082","file":"PLN-0082-rgba8888-palette-model-and-argb-conversion.md","status":"done","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0083","file":"PLN-0083-rgba8888-glyph-payload-emission-and-sizes.md","status":"review","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0084","file":"PLN-0084-rgba8888-packer-projections-and-workspace-apis.md","status":"review","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0085","file":"PLN-0085-rgba8888-tests-fixtures-and-conformance.md","status":"review","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0086","file":"PLN-0086-rgba8888-final-integration-and-residue-removal.md","status":"review","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0038","status":"open","ticket":"studio-packer-rgba8888-asset-pipeline","title":"Studio and Packer RGBA8888 Asset Pipeline Alignment","created_at":"2026-05-23","updated_at":"2026-05-23","tags":["studio","packer","assets","glyph-bank","palette","rgba8888","runtime-alignment"],"agendas":[{"id":"AGD-0041","file":"AGD-0041-studio-packer-rgba8888-asset-pipeline.md","status":"accepted","created_at":"2026-05-23","updated_at":"2026-05-23"}],"decisions":[{"id":"DEC-0037","file":"DEC-0037-studio-packer-rgba8888-asset-pipeline.md","status":"accepted","created_at":"2026-05-23","updated_at":"2026-05-23","ref_agenda":"AGD-0041"}],"plans":[{"id":"PLN-0081","file":"PLN-0081-rgba8888-packer-and-studio-spec-propagation.md","status":"done","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0082","file":"PLN-0082-rgba8888-palette-model-and-argb-conversion.md","status":"done","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0083","file":"PLN-0083-rgba8888-glyph-payload-emission-and-sizes.md","status":"done","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0084","file":"PLN-0084-rgba8888-packer-projections-and-workspace-apis.md","status":"review","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0085","file":"PLN-0085-rgba8888-tests-fixtures-and-conformance.md","status":"review","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0086","file":"PLN-0086-rgba8888-final-integration-and-residue-removal.md","status":"review","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0037","status":"done","ticket":"pbs-autocomplete-parameter-names","title":"PBS autocomplete parameter names for stdlib and method calls","created_at":"2026-05-08","updated_at":"2026-05-14","tags":["compiler-pbs","studio","lsp","autocomplete","signature-help","stdlib"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0052","file":"discussion/lessons/DSC-0037-pbs-autocomplete-parameter-names/LSN-0052-canonical-callable-parameter-names-through-pbs-editor-assistance.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14"}]}
{"type":"discussion","id":"DSC-0036","status":"open","ticket":"pbs-symbol-documentation-and-hover-markdown","title":"Modelo de documentacao de simbolos em PBS e consumo markdown no hover","created_at":"2026-05-08","updated_at":"2026-05-08","tags":["compiler","compiler-pbs","studio","lsp","vscode","editor","hover","documentation","markdown"],"agendas":[{"id":"AGD-0039","file":"AGD-0039-pbs-symbol-documentation-and-hover-markdown.md","status":"open","created_at":"2026-05-08","updated_at":"2026-05-08"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0035","status":"done","ticket":"pbs-lsp-editor-assistance-wave-1","title":"Wave 1 de assistencia editorial via LSP para PBS no VS Code","created_at":"2026-05-08","updated_at":"2026-05-08","tags":["studio","lsp","vscode","compiler","compiler-pbs","editor","completion","hover","signature-help"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0051","file":"discussion/lessons/DSC-0035-pbs-lsp-editor-assistance-wave-1/LSN-0051-compiler-backed-editor-assistance-for-pbs.md","status":"done","created_at":"2026-05-08","updated_at":"2026-05-08"}]}

View File

@ -2,9 +2,9 @@
id: PLN-0083
ticket: studio-packer-rgba8888-asset-pipeline
title: RGBA8888 Glyph Payload Emission and Size Accounting
status: review
status: done
created: 2026-05-23
completed:
completed: 2026-05-23
ref_decisions:
- DEC-0037
tags:
@ -126,12 +126,12 @@ index into the referenced glyph palette.
## Acceptance Criteria
- [ ] Glyph payloads keep `GLYPH/indexed_v1`.
- [ ] Glyph palette blocks are 4096 bytes.
- [ ] Palette entries are emitted as RGBA bytes in runtime channel order.
- [ ] No synthetic magenta/color-key value is emitted.
- [ ] Size and decoded-size metadata match the new payload length.
- [ ] Scene `palette_id` payload shape is unchanged.
- [x] Glyph payloads keep `GLYPH/indexed_v1`.
- [x] Glyph palette blocks are 4096 bytes.
- [x] Palette entries are emitted as RGBA bytes in runtime channel order.
- [x] No synthetic magenta/color-key value is emitted.
- [x] Size and decoded-size metadata match the new payload length.
- [x] Scene `palette_id` payload shape is unchanged.
## Dependencies

View File

@ -38,7 +38,11 @@ import java.util.*;
import java.util.stream.Stream;
public final class FileSystemPackerWorkspaceService implements PackerWorkspaceService {
private static final int GLYPH_BANK_COLOR_KEY_RGB565 = 0xF81F;
private static final int GLYPH_BANK_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;
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";
@ -1091,7 +1095,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
final byte[] sheetPixels = new byte[width * height];
final Map<String, PackerRuntimeWalkFile> selectedByPath = new LinkedHashMap<>();
runtimeAsset.walkProjection().buildCandidateFiles().forEach(file -> selectedByPath.put(file.relativePath(), file));
final Map<Integer, Integer> declaredPaletteIndexByRgb = declaredPaletteIndexByRgb(declaration);
final Map<Integer, Integer> declaredPaletteIndexByRgba = declaredPaletteIndexByRgba(declaration);
declaration.artifacts().stream()
.sorted(Comparator.comparingInt(PackerAssetArtifactSelection::index))
@ -1108,7 +1112,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
tile,
walkFile.metadata().get("palette"),
artifact.file(),
declaredPaletteIndexByRgb);
declaredPaletteIndexByRgba);
final int tileX = (artifact.index() % tilesPerRow) * tileSize;
final int tileY = (artifact.index() / tilesPerRow) * tileSize;
for (int localY = 0; localY < tile.height(); localY += 1) {
@ -1130,7 +1134,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
runtimeMetadata.put("tile_size", tileSize);
runtimeMetadata.put("width", width);
runtimeMetadata.put("height", height);
runtimeMetadata.put("palette_count", 64);
runtimeMetadata.put("palette_count", GLYPH_BANK_PALETTE_COUNT);
runtimeMetadata.put("palette_authored", countAuthoredGlyphBankPalettes(declaration));
runtimeMetadata.put("codec", Map.of());
declaration.outputMetadata().forEach((key, value) -> {
@ -1150,7 +1154,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
"GLYPH",
"NONE",
payload,
width * height + 2048,
width * height + GLYPH_BANK_PALETTE_BYTES,
runtimeMetadata,
toolingMetadata,
declaration.preloadEnabled());
@ -1160,7 +1164,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
PackerTileIndexedV1 tile,
Object paletteMetadata,
String artifactFile,
Map<Integer, Integer> declaredPaletteIndexByRgb) {
Map<Integer, Integer> declaredPaletteIndexByRgba) {
final int[] rgba8888 = paletteColors(paletteMetadata);
if (rgba8888.length == 0) {
throw new IllegalStateException("Selected tile artifact is missing palette metadata required for palette remapping: " + artifactFile);
@ -1172,7 +1176,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
throw new IllegalStateException("Tile palette index exceeds discovered palette size for artifact: " + artifactFile);
}
final int rgba = rgba8888[paletteIndex];
final Integer materialIndex = declaredPaletteIndexByRgb.get(rgba);
final Integer materialIndex = declaredPaletteIndexByRgba.get(rgba);
if (materialIndex == null || materialIndex < 0 || materialIndex > 15) {
throw new IllegalStateException("Unable to resolve canonical palette index for artifact color in " + artifactFile);
}
@ -1181,7 +1185,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
return remapped;
}
private Map<Integer, Integer> declaredPaletteIndexByRgb(PackerAssetDeclaration declaration) {
private Map<Integer, Integer> declaredPaletteIndexByRgba(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.");
@ -1272,7 +1276,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
}
private byte[] emitGlyphBankPalettes(PackerAssetDeclaration declaration) {
final byte[] bytes = new byte[64 * 16 * 2];
final byte[] bytes = new byte[GLYPH_BANK_PALETTE_BYTES];
final JsonNode palettesNode = declaration.outputPipelineMetadata().get("palettes");
if (!(palettesNode instanceof com.fasterxml.jackson.databind.node.ArrayNode palettesArray)) {
return bytes;
@ -1284,21 +1288,21 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
continue;
}
final int paletteIndex = indexNode.intValue();
if (paletteIndex < 0 || paletteIndex >= 64) {
if (paletteIndex < 0 || paletteIndex >= GLYPH_BANK_PALETTE_COUNT) {
continue;
}
final int paletteBaseOffset = (paletteIndex * 16) * 2;
bytes[paletteBaseOffset] = (byte) (GLYPH_BANK_COLOR_KEY_RGB565 & 0xFF);
bytes[paletteBaseOffset + 1] = (byte) ((GLYPH_BANK_COLOR_KEY_RGB565 >>> 8) & 0xFF);
final JsonNode convertedNode = paletteNode.path("convertedRgb565");
if (!convertedNode.isArray()) {
final JsonNode rgbaNode = paletteNode.path("rgba8888");
if (!rgbaNode.isArray()) {
continue;
}
for (int colorIndex = 0; colorIndex < Math.min(15, convertedNode.size()); colorIndex += 1) {
final int rgb565 = convertedNode.get(colorIndex).asInt(0);
final int baseOffset = ((paletteIndex * 16) + colorIndex + 1) * 2;
bytes[baseOffset] = (byte) (rgb565 & 0xFF);
bytes[baseOffset + 1] = (byte) ((rgb565 >>> 8) & 0xFF);
for (int colorIndex = 0; colorIndex < Math.min(GLYPH_BANK_COLORS_PER_PALETTE, rgbaNode.size()); colorIndex += 1) {
final int rgba = rgbaNode.get(colorIndex).asInt(0);
final int baseOffset = ((paletteIndex * GLYPH_BANK_COLORS_PER_PALETTE) + colorIndex)
* GLYPH_BANK_RGBA8888_BYTES_PER_COLOR;
bytes[baseOffset] = (byte) ((rgba >>> 24) & 0xFF);
bytes[baseOffset + 1] = (byte) ((rgba >>> 16) & 0xFF);
bytes[baseOffset + 2] = (byte) ((rgba >>> 8) & 0xFF);
bytes[baseOffset + 3] = (byte) (rgba & 0xFF);
}
}
return bytes;