implements PLN-0073 render contract specs

This commit is contained in:
bQUARKz 2026-05-25 11:35:49 +01:00
parent 2b69be0f11
commit 96a23e5419
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
5 changed files with 165 additions and 149 deletions

View File

@ -1,5 +1,5 @@
{"type":"meta","next_id":{"DSC":39,"AGD":39,"DEC":31,"PLN":87,"LSN":47,"CLSN":1}} {"type":"meta","next_id":{"DSC":39,"AGD":39,"DEC":31,"PLN":87,"LSN":47,"CLSN":1}}
{"type":"discussion","id":"DSC-0038","status":"in_progress","ticket":"render-frame-packet-boundary","title":"Logical Render Pipelines and Command Packets","created_at":"2026-05-25","updated_at":"2026-05-25","tags":["gfx","renderer","runtime","frame-composer","architecture","ui","pipeline"],"agendas":[{"id":"AGD-0038","file":"AGD-0038-renderframepacket-boundary-for-classic-2d-renderer.md","status":"accepted","created_at":"2026-05-25","updated_at":"2026-05-25"}],"decisions":[{"id":"DEC-0030","file":"DEC-0030-logical-render-pipeline-command-boundary.md","status":"accepted","created_at":"2026-05-25","updated_at":"2026-05-25","ref_agenda":"AGD-0038"}],"plans":[{"id":"PLN-0073","file":"PLN-0073-render-contract-specs.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0074","file":"PLN-0074-hal-render-submission-types.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0075","file":"PLN-0075-rendermanager-core.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0076","file":"PLN-0076-capabilities-and-abi-domain-split.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0077","file":"PLN-0077-composer-buffer-and-game2d-packet.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0078","file":"PLN-0078-classic2d-game-renderer-consumer.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0079","file":"PLN-0079-gfx2d-primitive-domain.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0080","file":"PLN-0080-shell-ui-packet-and-gfxui-domain.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0081","file":"PLN-0081-shell-hub-migration.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0082","file":"PLN-0082-frame-publication-and-present-boundary.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0083","file":"PLN-0083-fade-removal.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0084","file":"PLN-0084-host-debug-overlay-removal.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0085","file":"PLN-0085-pbs-stdlib-and-syscall-declarations.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0086","file":"PLN-0086-end-to-end-render-boundary-validation.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]}],"lessons":[]} {"type":"discussion","id":"DSC-0038","status":"in_progress","ticket":"render-frame-packet-boundary","title":"Logical Render Pipelines and Command Packets","created_at":"2026-05-25","updated_at":"2026-05-25","tags":["gfx","renderer","runtime","frame-composer","architecture","ui","pipeline"],"agendas":[{"id":"AGD-0038","file":"AGD-0038-renderframepacket-boundary-for-classic-2d-renderer.md","status":"accepted","created_at":"2026-05-25","updated_at":"2026-05-25"}],"decisions":[{"id":"DEC-0030","file":"DEC-0030-logical-render-pipeline-command-boundary.md","status":"accepted","created_at":"2026-05-25","updated_at":"2026-05-25","ref_agenda":"AGD-0038"}],"plans":[{"id":"PLN-0073","file":"PLN-0073-render-contract-specs.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0074","file":"PLN-0074-hal-render-submission-types.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0075","file":"PLN-0075-rendermanager-core.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0076","file":"PLN-0076-capabilities-and-abi-domain-split.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0077","file":"PLN-0077-composer-buffer-and-game2d-packet.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0078","file":"PLN-0078-classic2d-game-renderer-consumer.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0079","file":"PLN-0079-gfx2d-primitive-domain.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0080","file":"PLN-0080-shell-ui-packet-and-gfxui-domain.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0081","file":"PLN-0081-shell-hub-migration.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0082","file":"PLN-0082-frame-publication-and-present-boundary.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0083","file":"PLN-0083-fade-removal.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0084","file":"PLN-0084-host-debug-overlay-removal.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0085","file":"PLN-0085-pbs-stdlib-and-syscall-declarations.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0086","file":"PLN-0086-end-to-end-render-boundary-validation.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0035","status":"done","ticket":"task-owned-shell-windows","title":"Agenda - Task-Owned Shell Windows","created_at":"2026-05-15","updated_at":"2026-05-15","tags":["runtime","os","task","window-manager","shell","lifecycle"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0044","file":"discussion/lessons/DSC-0035-task-owned-shell-windows/LSN-0044-task-window-liveness-belongs-to-the-task.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15"}]} {"type":"discussion","id":"DSC-0035","status":"done","ticket":"task-owned-shell-windows","title":"Agenda - Task-Owned Shell Windows","created_at":"2026-05-15","updated_at":"2026-05-15","tags":["runtime","os","task","window-manager","shell","lifecycle"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0044","file":"discussion/lessons/DSC-0035-task-owned-shell-windows/LSN-0044-task-window-liveness-belongs-to-the-task.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15"}]}
{"type":"discussion","id":"DSC-0034","status":"done","ticket":"system-os-domain-facades","title":"Agenda - SystemOS Domain Facades","created_at":"2026-05-15","updated_at":"2026-05-15","tags":["runtime","os","services","api-surface","lifecycle","fs"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0043","file":"discussion/lessons/DSC-0034-system-os-domain-facades/LSN-0043-systemos-domain-facades.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15"}]} {"type":"discussion","id":"DSC-0034","status":"done","ticket":"system-os-domain-facades","title":"Agenda - SystemOS Domain Facades","created_at":"2026-05-15","updated_at":"2026-05-15","tags":["runtime","os","services","api-surface","lifecycle","fs"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0043","file":"discussion/lessons/DSC-0034-system-os-domain-facades/LSN-0043-systemos-domain-facades.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15"}]}
{"type":"discussion","id":"DSC-0023","status":"done","ticket":"perf-full-migration-to-atomic-telemetry","title":"Agenda - [PERF] Full Migration to Atomic Telemetry","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["perf","runtime","telemetry"],"agendas":[{"id":"AGD-0021","file":"AGD-0021-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0008","file":"DEC-0008-full-migration-to-atomic-telemetry.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10"}],"plans":[{"id":"PLN-0007","file":"PLN-0007-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"lessons":[{"id":"LSN-0028","file":"discussion/lessons/DSC-0023-perf-full-migration-to-atomic-telemetry/LSN-0028-converging-to-single-atomic-telemetry-source.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} {"type":"discussion","id":"DSC-0023","status":"done","ticket":"perf-full-migration-to-atomic-telemetry","title":"Agenda - [PERF] Full Migration to Atomic Telemetry","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["perf","runtime","telemetry"],"agendas":[{"id":"AGD-0021","file":"AGD-0021-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0008","file":"DEC-0008-full-migration-to-atomic-telemetry.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10"}],"plans":[{"id":"PLN-0007","file":"PLN-0007-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"lessons":[{"id":"LSN-0028","file":"discussion/lessons/DSC-0023-perf-full-migration-to-atomic-telemetry/LSN-0028-converging-to-single-atomic-telemetry-source.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]}

View File

@ -2,7 +2,7 @@
id: PLN-0073 id: PLN-0073
ticket: render-frame-packet-boundary ticket: render-frame-packet-boundary
title: Render Contract Specs title: Render Contract Specs
status: open status: done
created: 2026-05-25 created: 2026-05-25
ref_decisions: [DEC-0030] ref_decisions: [DEC-0030]
tags: [gfx, renderer, runtime, frame-composer, architecture, ui, pipeline] tags: [gfx, renderer, runtime, frame-composer, architecture, ui, pipeline]

View File

@ -9,7 +9,29 @@ Didactic companion: [`../learn/mental-model-gfx.md`](../runtime/learn/mental-mod
The **GFX** peripheral is responsible for generating images in PROMETEU. The **GFX** peripheral is responsible for generating images in PROMETEU.
It is an explicit 2D graphics device based on: `DEC-0030` defines the current logical render boundary. PROMETEU render
production is split from render implementation:
```text
domain buffers during logical frame
-> RenderManager closes buffers
-> RenderSubmission snapshot
-> render surface / implementation consumes submission
-> RGBA8888 surface publication
```
The GFX peripheral remains the classic local raster implementation for
PROMETEU's 2D output. Logical render APIs do not target `Gfx` or the
framebuffer directly.
`RenderSubmission` is the closed snapshot passed across the render boundary. It
MUST contain a frame id, the active app mode, and one typed packet:
`Game2DFramePacket` for `AppMode::Game` or `ShellUiFramePacket` for
`AppMode::Shell`. Once closed, producers MUST treat the submission as
immutable. The runtime backpressure policy is latest-complete-submission-wins;
it MUST NOT accumulate an unbounded frame queue.
The current 2D graphics model is based on:
- framebuffer - framebuffer
- tilemaps - tilemaps
@ -48,19 +70,19 @@ palette, host presentation, or compatibility contract.
## 3. Double Buffering ## 3. Double Buffering
The GFX maintains two buffers: The render surface implementation maintains the concrete buffers needed to
publish a frame. The classic software implementation uses:
- **Back Buffer** — where the frame is built - **Back Buffer** — where the frame is built
- **Front Buffer** — where the frame is displayed - **Front Buffer** — where the frame is displayed
Per-frame flow: Per-frame flow:
1. The system prepares the logical frame 1. Game or Shell code mutates mode-specific domain buffers during the logical frame.
2. Canonical game composition is rendered into the back buffer 2. `RenderManager` closes the active buffers into an immutable `RenderSubmission`.
3. Deferred final overlay/debug primitives are drained on top of the completed game frame 3. The render surface implementation consumes the submission and rasterizes to its back buffer.
4. Calls `present()` 4. The render surface publishes the completed RGBA8888 surface.
5. Buffers are swapped 5. The host displays the published surface.
6. The host displays the front buffer
This guarantees: This guarantees:
@ -192,14 +214,20 @@ Access:
--- ---
## 10. Canonical Game Projection to the Back Buffer ## 10. Canonical Game Projection
For each frame: Game mode uses a typed Game 2D submission. `composer.*` owns high-level Game 2D
frame composition: scene binding, camera, sprites, HUD, and frame orchestration.
`gfx2d.*` owns Game 2D primitives only. Both domains are mutable while the
logical frame is being produced and are closed by `RenderManager` into a
`Game2DFramePacket`.
For each Game 2D packet:
1. For each Tile Layer, in order: 1. For each Tile Layer, in order:
- Rasterize visible tiles from the cache - Rasterize visible tiles from the cache
- Apply scroll, flip, and transparency - Apply scroll, flip, and transparency
- Write to the back buffer - Write to the render surface's working buffer
2. Draw sprites: 2. Draw sprites:
- With priority between layers - With priority between layers
@ -207,9 +235,11 @@ For each frame:
3. Draw HUD layer last 3. Draw HUD layer last
This section describes only the canonical game composition path. 4. Draw buffered `gfx2d.*` primitives according to the Game 2D packet contract.
`gfx.*` primitives such as `draw_text`, `draw_line`, and `draw_disc` are not part of this canonical game projection order. In v1 they belong to a deferred final overlay/debug stage that is drained after canonical game composition is complete. This section describes only the Game 2D packet rendering path. Shell/system UI
uses `gfxui.*` and `ShellUiFramePacket`; it is never part of Game HUD or
`composer.*`.
--- ---
@ -227,15 +257,15 @@ Base order:
4. Tile Layer 3 4. Tile Layer 3
5. Sprites (by priority between layers) 5. Sprites (by priority between layers)
6. HUD Layer 6. HUD Layer
7. Scene Fade 7. Buffered `gfx2d.*` Game primitives
8. HUD Fade
9. Deferred `gfx.*` overlay/debug primitives
Normative boundary: Normative boundary:
- Items 1 through 8 belong to canonical game-frame composition. - Items 1 through 6 belong to `composer.*` Game-frame composition.
- Item 9 is a separate overlay/debug stage. - Item 7 belongs to `gfx2d.*`.
- Deferred `gfx.*` primitives MUST NOT be interpreted as scene, sprite, or canonical HUD content. - `gfx2d.*` primitives MUST NOT be interpreted as scene, sprite, camera, HUD,
or frame orchestration.
- Shell/system UI belongs to `gfxui.*` and `ShellUiFramePacket`, not Game HUD.
--- ---
@ -287,7 +317,8 @@ Everything is:
- Blending occurs during drawing - Blending occurs during drawing
- For canonical game composition, the result goes to the back buffer during composition - For canonical game composition, the result goes to the back buffer during composition
- For deferred `gfx.*` overlay/debug primitives, the result is applied during the final overlay/debug drain stage - For `gfx2d.*` and `gfxui.*` primitives, the result is applied when the render
surface consumes the closed packet for the active app mode
- There is no automatic GPU-style post-processing pipeline - There is no automatic GPU-style post-processing pipeline
--- ---
@ -301,6 +332,7 @@ By design:
- HDR - HDR
- Gamma correction - Gamma correction
- RGB565 compatibility framebuffers - RGB565 compatibility framebuffers
- fade fields, fade syscalls, or fade packet members
- multi-format backend selection - multi-format backend selection
- render-thread ownership as part of this contract - render-thread ownership as part of this contract
@ -318,104 +350,24 @@ By design:
--- ---
## 17. Special PostFX — Fade (Scene and HUD) ## 17. Transitions and Removed Fade Contract
PROMETEU supports **gradual fade** as a special PostFX, with two independent The previous special fade model is not part of the canonical render contract.
controls: Current render packets, syscalls, and ABI domains MUST NOT expose scene fade,
HUD fade, fade levels, fade colors, or equivalent inherited fade state.
- **Scene Fade**: affects the entire scene (Tile Layers 03 + Sprites) Future visual transitions between Shell and Game belong to `RenderManager`.
- **HUD Fade**: affects only the HUD Layer (always composed last) They are not owned by `composer.*`, `gfx2d.*`, `gfxui.*`, `Game2DFramePacket`,
or `ShellUiFramePacket`.
In v1, deferred `gfx.*` overlay/debug primitives are drained after both fades and therefore are not themselves part of scene or HUD fade application.
The fade uses a **discrete integer level** (0..31), which in practice produces an
"almost continuous" visual result in Prometeu's `480x270` pixel-art framebuffer.
---
### 17.1 Fade Representation
Each fade is represented by:
- `fade_level: u8` in the range **[0..31]**
- `0` → fully replaced by the fade color
- `31` → fully visible (no fade)
- `fade_color: RGBA8888`
- color the image will be blended into
Registers:
- `SCENE_FADE_LEVEL` (0..31)
- `SCENE_FADE_COLOR` (RGBA8888)
- `HUD_FADE_LEVEL` (0..31)
- `HUD_FADE_COLOR` (RGBA8888)
Common cases:
- Fade-out: `fade_color = BLACK`
- Flash/teleport: `fade_color = WHITE`
- Special effects: any RGBA8888 color
---
### 17.2 Fade Operation (Blending with Arbitrary Color)
For each RGBA8888 pixel `src` and fade color `fc`, the final pixel `dst` is calculated per channel.
1) Extract components:
- `src_r8`, `src_g8`, `src_b8`, `src_a8`
- `fc_r8`, `fc_g8`, `fc_b8`, `fc_a8`
2) Apply integer blending:
```
src_weight = fade_level // 0..31
fc_weight = 31 - fade_level
r8 = (src_r8 * src_weight + fc_r8 * fc_weight) / 31
g8 = (src_g8 * src_weight + fc_g8 * fc_weight) / 31
b8 = (src_b8 * src_weight + fc_b8 * fc_weight) / 31
a8 = (src_a8 * src_weight + fc_a8 * fc_weight) / 31
```
- `src_r8`, `src_g8`, `src_b8`, `src_a8`
- `fc_r8`, `fc_g8`, `fc_b8`, `fc_a8`
3) Repack:
```
dst = pack_rgba8888_rgba(r8, g8, b8, a8)
```
Notes:
- Deterministic operation
- Integers only
- Can be optimized via LUT
---
### 17.3 Order of Application in the Frame
The frame composition follows this order:
1. Rasterize **Tile Layers 03** → Back Buffer
2. Rasterize **Sprites** according to priority
3. (Optional) Extra pipeline (Emission/Light/Glow etc.)
4. Apply **Scene Fade** using:
- `SCENE_FADE_LEVEL`
- `SCENE_FADE_COLOR`
5. Rasterize **HUD Layer**
6. Apply **HUD Fade** using:
- `HUD_FADE_LEVEL`
- `HUD_FADE_COLOR`
7. `present()`
Rules: Rules:
- Scene Fade never affects the HUD - render domain packets MUST NOT contain fade fields;
- HUD Fade never affects the scene - public render syscalls MUST NOT expose fade controls;
- render implementations MUST NOT treat fade as an inherited post-processing
habit;
- transition work requires an explicit `RenderManager` contract or a later
decision.
--- ---
@ -570,8 +522,13 @@ The system can measure:
Graphics-related public ABI in v1 is split between: Graphics-related public ABI in v1 is split between:
- `gfx.*` for direct drawing/backend-oriented operations; - `composer.*` for Game 2D high-level frame composition;
- `composer.*` for frame orchestration operations. - `gfx2d.*` for Game 2D primitives only;
- `gfxui.*` for Shell UI primitives only.
`composer.*` and `gfx2d.*` are available to `AppMode::Game`.
`gfxui.*` is available to `AppMode::Shell`. Renderers and capabilities may
differ by app mode.
Only operations with real operational rejection paths return explicit status values. Only operations with real operational rejection paths return explicit status values.
@ -585,29 +542,32 @@ Fault boundary:
| Syscall | Return | Policy basis | | Syscall | Return | Policy basis |
| ----------------------- | ------------- | --------------------------------------------------- | | ----------------------- | ------------- | --------------------------------------------------- |
| `gfx.clear` | `void` | no real operational failure path in v1 | | `gfx2d.clear` | `void` | no real operational failure path in v1 |
| `gfx.fill_rect` | `void` | no real operational failure path in v1 | | `gfx2d.fill_rect` | `void` | no real operational failure path in v1 |
| `gfx.draw_line` | `void` | no real operational failure path in v1 | | `gfx2d.draw_line` | `void` | no real operational failure path in v1 |
| `gfx.draw_circle` | `void` | no real operational failure path in v1 | | `gfx2d.draw_circle` | `void` | no real operational failure path in v1 |
| `gfx.draw_disc` | `void` | no real operational failure path in v1 | | `gfx2d.draw_disc` | `void` | no real operational failure path in v1 |
| `gfx.draw_square` | `void` | no real operational failure path in v1 | | `gfx2d.draw_square` | `void` | no real operational failure path in v1 |
| `gfx.draw_text` | `void` | no real operational failure path in v1 | | `gfx2d.draw_text` | `void` | no real operational failure path in v1 |
| `gfxui.*` primitives | `void` | no real operational failure path in v1 |
| `composer.bind_scene` | `status:int` | status-returning API, but missing scene glyph dependencies are fatal runtime errors | | `composer.bind_scene` | `status:int` | status-returning API, but missing scene glyph dependencies are fatal runtime errors |
| `composer.unbind_scene` | `status:int` | explicit orchestration-domain operational result | | `composer.unbind_scene` | `status:int` | explicit orchestration-domain operational result |
| `composer.set_camera` | `void` | no real operational failure path in v1 | | `composer.set_camera` | `void` | no real operational failure path in v1 |
| `composer.emit_sprite` | `status:int` | explicit orchestration-domain operational rejection | | `composer.emit_sprite` | `status:int` | explicit orchestration-domain operational rejection |
### 19.1.a Deferred overlay/debug semantics for `gfx.*` ### 19.1.a Primitive domain semantics
The public `gfx.*` primitive family remains valid in v1, but its stable operational meaning is: The primitive domains have stable operational meaning:
- deferred final overlay/debug composition; - `gfx2d.*` is Game 2D primitive command buffering;
- screen-space and pipeline-agnostic relative to `composer.*`; - `gfx2d.*` is screen-space and primitive-only relative to `composer.*`;
- outside `FrameComposer`; - `gfx2d.*` is outside scene, camera, sprites, HUD, and Game 2D frame orchestration;
- above scene, sprites, and canonical HUD; - `gfxui.*` is Shell UI primitive command buffering;
- drained after `hud_fade`. - `gfxui.*` does not contain widget or layout policy.
This means callers MUST NOT rely on stable immediate writes to the working back buffer as the public contract for `gfx.draw_text(...)` or sibling primitives. Callers MUST NOT rely on stable immediate writes to the working back buffer as
the public contract for primitive drawing. Primitive calls mutate domain command
buffers that close into the active typed submission.
### 19.1.b Scene dependency fatal boundary ### 19.1.b Scene dependency fatal boundary

View File

@ -116,26 +116,26 @@ Hardware differences:
The graphics system: The graphics system:
- operates on a logical RGBA8888 framebuffer - produces typed logical render submissions that render to RGBA8888 output
- uses an indexed palette - uses an indexed palette
- does not depend on a specific GPU - does not depend on a specific GPU
The platform layer: The platform layer:
- only displays the framebuffer - consumes closed render submissions through a render-surface implementation
- does not reinterpret graphics commands
- **may overlay technical HUDs without modifying the logical framebuffer** - **may overlay technical HUDs without modifying the logical framebuffer**
- may transport the logical framebuffer into a host presentation surface where a host-only overlay layer is composed - may transport published RGBA8888 output into a host presentation surface where a host-only overlay layer is composed
The host presentation layer MUST treat RGBA8888 as the canonical logical The host presentation layer MUST treat RGBA8888 as the canonical logical
framebuffer format. RGB565 conversion is not part of the normal host framebuffer format. RGB565 conversion is not part of the normal host
presentation contract. presentation contract.
Host presentation SHOULD be driven by published logical frames and explicit host-owned invalidation, not by perpetual redraw polling. Host presentation SHOULD be driven by published render submissions and explicit
host-owned invalidation, not by perpetual redraw polling.
In particular: In particular:
- a stable logical frame MAY remain visible across multiple host wakeups without recomposition; - a stable published submission MAY remain visible across multiple host wakeups without recomposition;
- a host MAY redraw the same logical frame again when its own surface is invalidated by resize, expose, or host-only overlay/debug changes; - a host MAY redraw the same logical frame again when its own surface is invalidated by resize, expose, or host-only overlay/debug changes;
- a host MUST NOT invent intermediate logical frames or require continuous redraw merely to discover whether a new logical frame exists. - a host MUST NOT invent intermediate logical frames or require continuous redraw merely to discover whether a new logical frame exists.

View File

@ -220,29 +220,78 @@ For `asset.load`:
- JSON-on-the-wire bank inspection payloads are not valid public ABI; - JSON-on-the-wire bank inspection payloads are not valid public ABI;
- `bank.info` returns stack values, not textual structured payloads. - `bank.info` returns stack values, not textual structured payloads.
### GFX surface (`gfx`, v1) ### Render surfaces (`composer`, `gfx2d`, `gfxui`, v1)
The public GFX ABI uses format-neutral operation names. API and syscall names `DEC-0030` splits logical render production from render implementation. Public
MUST NOT encode RGB565 or any other pixel format suffix. render syscalls mutate app-mode-specific domain buffers. They do not directly
publish a framebuffer and do not call host presentation APIs.
The canonical flow is:
```text
domain buffers during logical frame
-> RenderManager closes buffers
-> RenderSubmission snapshot
-> render surface / implementation consumes submission
-> RGBA8888 surface publication
```
`RenderSubmission` is a typed closed snapshot containing `frame_id`, `app_mode`,
and exactly one packet: `Game2DFramePacket` for `AppMode::Game` or
`ShellUiFramePacket` for `AppMode::Shell`. Producers may mutate domain buffers
before closure only. Once `RenderManager` closes a submission, producers MUST
NOT mutate it. Backpressure is latest-complete-submission-wins; the runtime
MUST NOT expose an unbounded submission queue as part of the ABI.
Render API and syscall names MUST NOT encode RGB565 or any other pixel format
suffix.
#### Game 2D primitive surface (`gfx2d`, v1)
The `gfx2d` module is a Game profile primitive surface. It is limited to Game
2D primitive commands and does not own scene, camera, sprites, HUD, or frame
orchestration.
Canonical operations in v1 include: Canonical operations in v1 include:
- `gfx.clear(color) -> void` - `gfx2d.clear(color) -> void`
- `gfx.fill_rect(x, y, w, h, color) -> void` - `gfx2d.fill_rect(x, y, w, h, color) -> void`
- `gfx.draw_line(x0, y0, x1, y1, color) -> void` - `gfx2d.draw_line(x0, y0, x1, y1, color) -> void`
- `gfx.draw_circle(x, y, radius, color) -> void` - `gfx2d.draw_circle(x, y, radius, color) -> void`
- `gfx.draw_disc(x, y, radius, color) -> void` - `gfx2d.draw_disc(x, y, radius, color) -> void`
- `gfx.draw_square(x, y, size, color) -> void` - `gfx2d.draw_square(x, y, size, color) -> void`
- `gfx.draw_text(x, y, text, color) -> void` - `gfx2d.draw_text(x, y, text, color) -> void`
#### Shell UI primitive surface (`gfxui`, v1)
The `gfxui` module is a Shell profile primitive surface. It contains Shell UI
primitive commands only. Widget and layout policy belongs in Shell/UI code or
stdlib code, not in the host/render-surface implementation.
Canonical operations in v1 include primitive shapes equivalent to the subset
needed by Shell UI:
- `gfxui.clear(color) -> void`
- `gfxui.fill_rect(x, y, w, h, color) -> void`
- `gfxui.draw_line(x0, y0, x1, y1, color) -> void`
- `gfxui.draw_circle(x, y, radius, color) -> void`
- `gfxui.draw_disc(x, y, radius, color) -> void`
- `gfxui.draw_square(x, y, size, color) -> void`
- `gfxui.draw_text(x, y, text, color) -> void`
Rules: Rules:
- `color` arguments are packed RGBA8888 values using RGBA channel order. - `color` arguments are packed RGBA8888 values using RGBA channel order.
- RGB565 raw values are not a public ABI contract. - RGB565 raw values are not a public ABI contract.
- `Gfx*565` syscall names and `gfx.*_565` canonical identities are not valid - `Gfx*565` syscall names and `gfx*.*_565` canonical identities are not valid
current ABI. current ABI.
- The runtime framebuffer exposed across the host boundary is RGBA8888, not - The runtime framebuffer exposed across the host boundary is RGBA8888, not
`u16` RGB565. `u16` RGB565.
- The old primitive `gfx.*` ABI is not the canonical render domain. PBS stdlibs
may expose ergonomic high-level names, but generated host bindings must map
to `gfx2d.*` in Game mode or `gfxui.*` in Shell mode.
- Render packets and public render syscalls MUST NOT expose fade fields or fade
controls.
### Composition surface (`composer`, v1) ### Composition surface (`composer`, v1)
@ -251,6 +300,11 @@ The canonical frame-orchestration public ABI uses module `composer`.
The `composer` module is a game profile surface. It is not inherited by The `composer` module is a game profile surface. It is not inherited by
`System` profile apps as their public UI/composition API. `System` profile apps as their public UI/composition API.
`composer` owns Game 2D high-level frame composition: scene binding, camera,
sprites, HUD, and Game 2D frame orchestration. It closes into a
`Game2DFramePacket` through `RenderManager`; it does not publish or present a
framebuffer directly.
Canonical operations in v1 are: Canonical operations in v1 are:
- `composer.bind_scene(bank_id) -> (status)` - `composer.bind_scene(bank_id) -> (status)`
@ -264,6 +318,8 @@ For mutating composer operations:
- `bind_scene`, `unbind_scene`, and `emit_sprite` are status-returning; - `bind_scene`, `unbind_scene`, and `emit_sprite` are status-returning;
- `set_camera` remains `void` in v1; - `set_camera` remains `void` in v1;
- no caller-provided sprite index or `active` flag is part of the canonical contract. - no caller-provided sprite index or `active` flag is part of the canonical contract.
- no fade level, fade color, or transition control is part of the canonical
composer ABI.
Fatal boundary clarification for `bind_scene`: Fatal boundary clarification for `bind_scene`: