From 4f52a65169be317a98dc2560a503971b66c17d91 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Sat, 18 Apr 2026 09:51:30 +0100 Subject: [PATCH] implements PLN-0026 --- .../prometeu-drivers/src/frame_composer.rs | 5 +++ crates/console/prometeu-drivers/src/gfx.rs | 24 +++------- crates/console/prometeu-hal/src/gfx_bridge.rs | 12 +++++ docs/specs/runtime/04-gfx-peripheral.md | 44 ++++++++++++++++--- 4 files changed, 61 insertions(+), 24 deletions(-) diff --git a/crates/console/prometeu-drivers/src/frame_composer.rs b/crates/console/prometeu-drivers/src/frame_composer.rs index 9a174e18..02a2f471 100644 --- a/crates/console/prometeu-drivers/src/frame_composer.rs +++ b/crates/console/prometeu-drivers/src/frame_composer.rs @@ -247,10 +247,15 @@ impl FrameComposer { { let update = resolver.update(scene, self.camera_x_px, self.camera_y_px); Self::apply_refresh_requests(cache, scene, &update.refresh_requests); + // `FrameComposer` owns only canonical game-frame composition. + // Deferred `gfx.*` primitives are drained later by a separate + // overlay/debug stage outside this service boundary. gfx.render_scene_from_cache(cache, &update); return; } + // No-scene frames still stop at canonical game composition. Final + // overlay/debug work remains outside `FrameComposer`. gfx.render_no_scene_frame(); } diff --git a/crates/console/prometeu-drivers/src/gfx.rs b/crates/console/prometeu-drivers/src/gfx.rs index 47b06899..e2c2534a 100644 --- a/crates/console/prometeu-drivers/src/gfx.rs +++ b/crates/console/prometeu-drivers/src/gfx.rs @@ -31,22 +31,11 @@ pub enum BlendMode { /// PROMETEU Graphics Subsystem (GFX). /// -/// Models a specialized graphics chip with a fixed resolution, double buffering, -/// and a multi-layered tile/sprite architecture. -/// -/// The GFX system works by composing several "layers" into a single 16-bit -/// RGB565 framebuffer. It supports hardware-accelerated primitives (lines, rects) -/// and specialized console features like background scrolling and sprite sorting. -/// -/// ### Layer Composition Order (back to front): -/// 1. **Priority 0 Sprites**: Objects behind everything else. -/// 2. **Tile Layer 0 + Priority 1 Sprites**: Background 0. -/// 3. **Tile Layer 1 + Priority 2 Sprites**: Background 1. -/// 4. **Tile Layer 2 + Priority 3 Sprites**: Background 2. -/// 5. **Tile Layer 3 + Priority 4 Sprites**: Foreground. -/// 6. **Scene Fade**: Global brightness/color filter. -/// 7. **HUD Layer**: Fixed UI elements (always on top). -/// 8. **HUD Fade**: Independent fade for the UI. +/// `Gfx` owns the framebuffer backend and the canonical game-frame raster path +/// consumed by `FrameComposer`. That canonical path covers scene composition, +/// sprite composition, and fades. Public `gfx.*` primitives remain valid, but +/// they do not define the canonical game composition contract; they belong to a +/// separate final overlay/debug stage. pub struct Gfx { /// Width of the internal framebuffer in pixels. w: usize, @@ -54,7 +43,8 @@ pub struct Gfx { h: usize, /// Front buffer: the "VRAM" currently being displayed by the Host window. front: Vec, - /// Back buffer: the "Work RAM" where new frames are composed. + /// Back buffer: the working buffer where canonical game frames are composed + /// before any final overlay/debug drain. back: Vec, /// Shared access to graphical memory banks (tiles and palettes). diff --git a/crates/console/prometeu-hal/src/gfx_bridge.rs b/crates/console/prometeu-hal/src/gfx_bridge.rs index 44829579..dc989f82 100644 --- a/crates/console/prometeu-hal/src/gfx_bridge.rs +++ b/crates/console/prometeu-hal/src/gfx_bridge.rs @@ -48,9 +48,21 @@ pub trait GfxBridge { fn draw_horizontal_line(&mut self, x0: i32, x1: i32, y: i32, color: Color); fn draw_vertical_line(&mut self, x: i32, y0: i32, y1: i32, color: Color); fn present(&mut self); + /// Render the canonical game frame with no bound scene. + /// + /// Deferred `gfx.*` overlay/debug primitives are intentionally outside this + /// contract and are drained by a separate final overlay stage. fn render_no_scene_frame(&mut self); + /// Render the canonical scene-backed game frame from cache/resolver state. + /// + /// Deferred `gfx.*` overlay/debug primitives are intentionally outside this + /// contract and are drained by a separate final overlay stage. fn render_scene_from_cache(&mut self, cache: &SceneViewportCache, update: &ResolverUpdate); fn load_frame_sprites(&mut self, sprites: &[Sprite]); + /// Submit text into the `gfx.*` primitive path. + /// + /// Under the accepted runtime contract this is not the canonical game + /// composition path; it belongs to the deferred final overlay/debug family. fn draw_text(&mut self, x: i32, y: i32, text: &str, color: Color); fn draw_char(&mut self, x: i32, y: i32, c: char, color: Color); diff --git a/docs/specs/runtime/04-gfx-peripheral.md b/docs/specs/runtime/04-gfx-peripheral.md index d0e5186f..8b63b63a 100644 --- a/docs/specs/runtime/04-gfx-peripheral.md +++ b/docs/specs/runtime/04-gfx-peripheral.md @@ -48,10 +48,12 @@ The GFX maintains two buffers: Per-frame flow: -1. The system draws to the back buffer -2. Calls `present()` -3. Buffers are swapped -4. The host displays the front buffer +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 This guarantees: @@ -183,7 +185,7 @@ Access: --- -## 10. Projection to the Back Buffer +## 10. Canonical Game Projection to the Back Buffer For each frame: @@ -198,6 +200,10 @@ For each frame: 3. Draw HUD layer last +This section describes only the canonical game composition path. + +`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. + --- ## 11. Drawing Order and Priority @@ -214,6 +220,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 + +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. --- @@ -258,8 +273,9 @@ Everything is: ## 14. Where Blend is Applied - Blending occurs during drawing -- The result goes directly to the back buffer -- There is no automatic post-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 +- There is no automatic GPU-style post-processing pipeline --- @@ -296,6 +312,8 @@ controls: - **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 is implemented without continuous per-pixel alpha and without floats. It uses a **discrete integer level** (0..31), which in practice produces an "almost continuous" visual result in 320×180 pixel art. @@ -566,6 +584,18 @@ Fault boundary: | `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.*` + +The public `gfx.*` primitive family remains valid in v1, but its stable operational meaning is: + +- 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`. + +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. + ### 19.2 `composer.emit_sprite` `composer.emit_sprite` returns `status:int`.