implements PLN-0026

This commit is contained in:
bQUARKz 2026-04-18 09:51:30 +01:00
parent b0b8bb9028
commit 4f52a65169
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
4 changed files with 61 additions and 24 deletions

View File

@ -247,10 +247,15 @@ impl FrameComposer {
{ {
let update = resolver.update(scene, self.camera_x_px, self.camera_y_px); let update = resolver.update(scene, self.camera_x_px, self.camera_y_px);
Self::apply_refresh_requests(cache, scene, &update.refresh_requests); 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); gfx.render_scene_from_cache(cache, &update);
return; return;
} }
// No-scene frames still stop at canonical game composition. Final
// overlay/debug work remains outside `FrameComposer`.
gfx.render_no_scene_frame(); gfx.render_no_scene_frame();
} }

View File

@ -31,22 +31,11 @@ pub enum BlendMode {
/// PROMETEU Graphics Subsystem (GFX). /// PROMETEU Graphics Subsystem (GFX).
/// ///
/// Models a specialized graphics chip with a fixed resolution, double buffering, /// `Gfx` owns the framebuffer backend and the canonical game-frame raster path
/// and a multi-layered tile/sprite architecture. /// consumed by `FrameComposer`. That canonical path covers scene composition,
/// /// sprite composition, and fades. Public `gfx.*` primitives remain valid, but
/// The GFX system works by composing several "layers" into a single 16-bit /// they do not define the canonical game composition contract; they belong to a
/// RGB565 framebuffer. It supports hardware-accelerated primitives (lines, rects) /// separate final overlay/debug stage.
/// 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.
pub struct Gfx { pub struct Gfx {
/// Width of the internal framebuffer in pixels. /// Width of the internal framebuffer in pixels.
w: usize, w: usize,
@ -54,7 +43,8 @@ pub struct Gfx {
h: usize, h: usize,
/// Front buffer: the "VRAM" currently being displayed by the Host window. /// Front buffer: the "VRAM" currently being displayed by the Host window.
front: Vec<u16>, front: Vec<u16>,
/// 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<u16>, back: Vec<u16>,
/// Shared access to graphical memory banks (tiles and palettes). /// Shared access to graphical memory banks (tiles and palettes).

View File

@ -48,9 +48,21 @@ pub trait GfxBridge {
fn draw_horizontal_line(&mut self, x0: i32, x1: i32, y: i32, color: Color); 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 draw_vertical_line(&mut self, x: i32, y0: i32, y1: i32, color: Color);
fn present(&mut self); 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); 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 render_scene_from_cache(&mut self, cache: &SceneViewportCache, update: &ResolverUpdate);
fn load_frame_sprites(&mut self, sprites: &[Sprite]); 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_text(&mut self, x: i32, y: i32, text: &str, color: Color);
fn draw_char(&mut self, x: i32, y: i32, c: char, color: Color); fn draw_char(&mut self, x: i32, y: i32, c: char, color: Color);

View File

@ -48,10 +48,12 @@ The GFX maintains two buffers:
Per-frame flow: Per-frame flow:
1. The system draws to the back buffer 1. The system prepares the logical frame
2. Calls `present()` 2. Canonical game composition is rendered into the back buffer
3. Buffers are swapped 3. Deferred final overlay/debug primitives are drained on top of the completed game frame
4. The host displays the front buffer 4. Calls `present()`
5. Buffers are swapped
6. The host displays the front buffer
This guarantees: This guarantees:
@ -183,7 +185,7 @@ Access:
--- ---
## 10. Projection to the Back Buffer ## 10. Canonical Game Projection to the Back Buffer
For each frame: For each frame:
@ -198,6 +200,10 @@ For each frame:
3. Draw HUD layer last 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 ## 11. Drawing Order and Priority
@ -214,6 +220,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
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 ## 14. Where Blend is Applied
- Blending occurs during drawing - Blending occurs during drawing
- The result goes directly to the back buffer - For canonical game composition, the result goes to the back buffer during composition
- There is no automatic post-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 03 + Sprites) - **Scene Fade**: affects the entire scene (Tile Layers 03 + Sprites)
- **HUD Fade**: affects only the HUD Layer (always composed last) - **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. 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 It uses a **discrete integer level** (0..31), which in practice produces an
"almost continuous" visual result in 320×180 pixel art. "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.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.*`
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` ### 19.2 `composer.emit_sprite`
`composer.emit_sprite` returns `status:int`. `composer.emit_sprite` returns `status:int`.