From 96a23e541911a7e930827c4775879f34b2c0ec27 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Mon, 25 May 2026 11:35:49 +0100 Subject: [PATCH] implements PLN-0073 render contract specs --- discussion/index.ndjson | 2 +- .../plans/PLN-0073-render-contract-specs.md | 2 +- docs/specs/runtime/04-gfx-peripheral.md | 220 +++++++----------- ...ortability-and-cross-platform-execution.md | 12 +- .../specs/runtime/16-host-abi-and-syscalls.md | 78 ++++++- 5 files changed, 165 insertions(+), 149 deletions(-) diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 7819370f..44cf0312 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -1,5 +1,5 @@ {"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-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"}]} diff --git a/discussion/workflow/plans/PLN-0073-render-contract-specs.md b/discussion/workflow/plans/PLN-0073-render-contract-specs.md index b7fdbe33..e71859dc 100644 --- a/discussion/workflow/plans/PLN-0073-render-contract-specs.md +++ b/discussion/workflow/plans/PLN-0073-render-contract-specs.md @@ -2,7 +2,7 @@ id: PLN-0073 ticket: render-frame-packet-boundary title: Render Contract Specs -status: open +status: done created: 2026-05-25 ref_decisions: [DEC-0030] tags: [gfx, renderer, runtime, frame-composer, architecture, ui, pipeline] diff --git a/docs/specs/runtime/04-gfx-peripheral.md b/docs/specs/runtime/04-gfx-peripheral.md index 9783fa50..ea591aaf 100644 --- a/docs/specs/runtime/04-gfx-peripheral.md +++ b/docs/specs/runtime/04-gfx-peripheral.md @@ -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. -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 - tilemaps @@ -48,19 +70,19 @@ palette, host presentation, or compatibility contract. ## 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 - **Front Buffer** — where the frame is displayed Per-frame flow: -1. The system prepares the logical frame -2. Canonical game composition is rendered into the back buffer -3. Deferred final overlay/debug primitives are drained on top of the completed game frame -4. Calls `present()` -5. Buffers are swapped -6. The host displays the front buffer +1. Game or Shell code mutates mode-specific domain buffers during the logical frame. +2. `RenderManager` closes the active buffers into an immutable `RenderSubmission`. +3. The render surface implementation consumes the submission and rasterizes to its back buffer. +4. The render surface publishes the completed RGBA8888 surface. +5. The host displays the published surface. 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: - Rasterize visible tiles from the cache - Apply scroll, flip, and transparency - - Write to the back buffer + - Write to the render surface's working buffer 2. Draw sprites: - With priority between layers @@ -207,9 +235,11 @@ For each frame: 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 5. Sprites (by priority between layers) 6. HUD Layer -7. Scene Fade -8. HUD Fade -9. Deferred `gfx.*` overlay/debug primitives +7. Buffered `gfx2d.*` Game primitives Normative boundary: -- Items 1 through 8 belong to canonical game-frame composition. -- Item 9 is a separate overlay/debug stage. -- Deferred `gfx.*` primitives MUST NOT be interpreted as scene, sprite, or canonical HUD content. +- Items 1 through 6 belong to `composer.*` Game-frame composition. +- Item 7 belongs to `gfx2d.*`. +- `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 - 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 --- @@ -301,6 +332,7 @@ By design: - HDR - Gamma correction - RGB565 compatibility framebuffers +- fade fields, fade syscalls, or fade packet members - multi-format backend selection - 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 -controls: +The previous special fade model is not part of the canonical render contract. +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 0–3 + Sprites) -- **HUD Fade**: affects only the HUD Layer (always composed last) - -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 0–3** → 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()` +Future visual transitions between Shell and Game belong to `RenderManager`. +They are not owned by `composer.*`, `gfx2d.*`, `gfxui.*`, `Game2DFramePacket`, +or `ShellUiFramePacket`. Rules: -- Scene Fade never affects the HUD -- HUD Fade never affects the scene +- render domain packets MUST NOT contain fade fields; +- 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: -- `gfx.*` for direct drawing/backend-oriented operations; -- `composer.*` for frame orchestration operations. +- `composer.*` for Game 2D high-level frame composition; +- `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. @@ -585,29 +542,32 @@ Fault boundary: | Syscall | Return | Policy basis | | ----------------------- | ------------- | --------------------------------------------------- | -| `gfx.clear` | `void` | no real operational failure path in v1 | -| `gfx.fill_rect` | `void` | no real operational failure path in v1 | -| `gfx.draw_line` | `void` | no real operational failure path in v1 | -| `gfx.draw_circle` | `void` | no real operational failure path in v1 | -| `gfx.draw_disc` | `void` | no real operational failure path in v1 | -| `gfx.draw_square` | `void` | no real operational failure path in v1 | -| `gfx.draw_text` | `void` | no real operational failure path in v1 | +| `gfx2d.clear` | `void` | no real operational failure path in v1 | +| `gfx2d.fill_rect` | `void` | no real operational failure path in v1 | +| `gfx2d.draw_line` | `void` | no real operational failure path in v1 | +| `gfx2d.draw_circle` | `void` | no real operational failure path in v1 | +| `gfx2d.draw_disc` | `void` | no real operational failure path in v1 | +| `gfx2d.draw_square` | `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.unbind_scene` | `status:int` | explicit orchestration-domain operational result | | `composer.set_camera` | `void` | no real operational failure path in v1 | | `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; -- screen-space and pipeline-agnostic relative to `composer.*`; -- outside `FrameComposer`; -- above scene, sprites, and canonical HUD; -- drained after `hud_fade`. +- `gfx2d.*` is Game 2D primitive command buffering; +- `gfx2d.*` is screen-space and primitive-only relative to `composer.*`; +- `gfx2d.*` is outside scene, camera, sprites, HUD, and Game 2D frame orchestration; +- `gfxui.*` is Shell UI primitive command buffering; +- `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 diff --git a/docs/specs/runtime/11-portability-and-cross-platform-execution.md b/docs/specs/runtime/11-portability-and-cross-platform-execution.md index 46317f0c..4b8b8a5b 100644 --- a/docs/specs/runtime/11-portability-and-cross-platform-execution.md +++ b/docs/specs/runtime/11-portability-and-cross-platform-execution.md @@ -116,26 +116,26 @@ Hardware differences: The graphics system: -- operates on a logical RGBA8888 framebuffer +- produces typed logical render submissions that render to RGBA8888 output - uses an indexed palette - does not depend on a specific GPU The platform layer: -- only displays the framebuffer -- does not reinterpret graphics commands +- consumes closed render submissions through a render-surface implementation - **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 framebuffer format. RGB565 conversion is not part of the normal host 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: -- 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 MUST NOT invent intermediate logical frames or require continuous redraw merely to discover whether a new logical frame exists. diff --git a/docs/specs/runtime/16-host-abi-and-syscalls.md b/docs/specs/runtime/16-host-abi-and-syscalls.md index 8a0ddb34..689feada 100644 --- a/docs/specs/runtime/16-host-abi-and-syscalls.md +++ b/docs/specs/runtime/16-host-abi-and-syscalls.md @@ -220,29 +220,78 @@ For `asset.load`: - JSON-on-the-wire bank inspection payloads are not valid public ABI; - `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 -MUST NOT encode RGB565 or any other pixel format suffix. +`DEC-0030` splits logical render production from render implementation. Public +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: -- `gfx.clear(color) -> void` -- `gfx.fill_rect(x, y, w, h, color) -> void` -- `gfx.draw_line(x0, y0, x1, y1, color) -> void` -- `gfx.draw_circle(x, y, radius, color) -> void` -- `gfx.draw_disc(x, y, radius, color) -> void` -- `gfx.draw_square(x, y, size, color) -> void` -- `gfx.draw_text(x, y, text, color) -> void` +- `gfx2d.clear(color) -> void` +- `gfx2d.fill_rect(x, y, w, h, color) -> void` +- `gfx2d.draw_line(x0, y0, x1, y1, color) -> void` +- `gfx2d.draw_circle(x, y, radius, color) -> void` +- `gfx2d.draw_disc(x, y, radius, color) -> void` +- `gfx2d.draw_square(x, y, size, 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: - `color` arguments are packed RGBA8888 values using RGBA channel order. - 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. - The runtime framebuffer exposed across the host boundary is RGBA8888, not `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) @@ -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 `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: - `composer.bind_scene(bank_id) -> (status)` @@ -264,6 +318,8 @@ For mutating composer operations: - `bind_scene`, `unbind_scene`, and `emit_sprite` are status-returning; - `set_camera` remains `void` in v1; - 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`: