implements PLN-0074 HAL render submissions
All checks were successful
Intrepid/Prometeu/Runtime/pipeline/head This commit looks good
Intrepid/Prometeu/Runtime/pipeline/pr-master This commit looks good

This commit is contained in:
bQUARKz 2026-05-25 11:37:58 +01:00
parent 96a23e5419
commit 105a6de11e
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
4 changed files with 211 additions and 2 deletions

View File

@ -20,6 +20,7 @@ pub mod native_helpers;
pub mod native_interface;
pub mod pad_bridge;
pub mod primitives;
pub mod render_submission;
pub mod sample;
pub mod scene_bank;
pub mod scene_layer;
@ -45,4 +46,9 @@ pub use input_signals::InputSignals;
pub use native_helpers::{expect_bool, expect_int};
pub use native_interface::{NativeInterface, SyscallId};
pub use pad_bridge::PadBridge;
pub use render_submission::{
BoundScenePacket, Camera2D, ComposerFramePacket, FrameId, Game2DFramePacket, GameSpritePacket,
Gfx2dCommand, GfxUiCommand, HudCommand, HudPacket, RenderSubmission, RenderSubmissionPacket,
ShellUiFramePacket,
};
pub use touch_bridge::TouchBridge;

View File

@ -0,0 +1,203 @@
use crate::app_mode::AppMode;
use crate::color::Color;
use crate::primitives::Rect;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct FrameId(pub u64);
impl FrameId {
pub const ZERO: Self = Self(0);
pub const fn new(value: u64) -> Self {
Self(value)
}
pub const fn get(self) -> u64 {
self.0
}
pub const fn next(self) -> Self {
Self(self.0.wrapping_add(1))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RenderSubmission {
pub frame_id: FrameId,
pub app_mode: AppMode,
pub packet: RenderSubmissionPacket,
}
impl RenderSubmission {
pub const fn game2d(frame_id: FrameId, packet: Game2DFramePacket) -> Self {
Self { frame_id, app_mode: AppMode::Game, packet: RenderSubmissionPacket::Game2D(packet) }
}
pub const fn shell_ui(frame_id: FrameId, packet: ShellUiFramePacket) -> Self {
Self { frame_id, app_mode: AppMode::Shell, packet: RenderSubmissionPacket::ShellUi(packet) }
}
pub const fn is_coherent(&self) -> bool {
matches!(
(self.app_mode, &self.packet),
(AppMode::Game, RenderSubmissionPacket::Game2D(_))
| (AppMode::Shell, RenderSubmissionPacket::ShellUi(_))
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RenderSubmissionPacket {
Game2D(Game2DFramePacket),
ShellUi(ShellUiFramePacket),
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Game2DFramePacket {
pub composer: ComposerFramePacket,
pub gfx2d: Vec<Gfx2dCommand>,
}
impl Game2DFramePacket {
pub fn new(composer: ComposerFramePacket, gfx2d: Vec<Gfx2dCommand>) -> Self {
Self { composer, gfx2d }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ShellUiFramePacket {
pub commands: Vec<GfxUiCommand>,
}
impl ShellUiFramePacket {
pub fn new(commands: Vec<GfxUiCommand>) -> Self {
Self { commands }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ComposerFramePacket {
pub bound_scene: Option<BoundScenePacket>,
pub camera: Camera2D,
pub sprites: Vec<GameSpritePacket>,
pub hud: HudPacket,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BoundScenePacket {
pub bank_id: i32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Camera2D {
pub x: i32,
pub y: i32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GameSpritePacket {
pub glyph_id: i32,
pub palette_id: i32,
pub x: i32,
pub y: i32,
pub layer: i32,
pub bank_id: i32,
pub flip_x: bool,
pub flip_y: bool,
pub priority: i32,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct HudPacket {
pub commands: Vec<HudCommand>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HudCommand {
Clear { color: Color },
FillRect { rect: Rect, color: Color },
DrawText { x: i32, y: i32, text: String, color: Color },
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Gfx2dCommand {
Clear { color: Color },
FillRect { rect: Rect, color: Color },
DrawLine { x0: i32, y0: i32, x1: i32, y1: i32, color: Color },
DrawCircle { x: i32, y: i32, radius: i32, color: Color },
DrawDisc { x: i32, y: i32, radius: i32, border_color: Color, fill_color: Color },
DrawSquare { rect: Rect, border_color: Color, fill_color: Color },
DrawText { x: i32, y: i32, text: String, color: Color },
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GfxUiCommand {
Clear { color: Color },
FillRect { rect: Rect, color: Color },
DrawLine { x0: i32, y0: i32, x1: i32, y1: i32, color: Color },
DrawCircle { x: i32, y: i32, radius: i32, color: Color },
DrawDisc { x: i32, y: i32, radius: i32, border_color: Color, fill_color: Color },
DrawSquare { rect: Rect, border_color: Color, fill_color: Color },
DrawText { x: i32, y: i32, text: String, color: Color },
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn constructors_pin_app_mode_to_packet_kind() {
let game = RenderSubmission::game2d(FrameId::new(7), Game2DFramePacket::default());
assert_eq!(game.app_mode, AppMode::Game);
assert!(matches!(game.packet, RenderSubmissionPacket::Game2D(_)));
assert!(game.is_coherent());
let shell =
RenderSubmission::shell_ui(FrameId::new(8), ShellUiFramePacket::new(Vec::new()));
assert_eq!(shell.app_mode, AppMode::Shell);
assert!(matches!(shell.packet, RenderSubmissionPacket::ShellUi(_)));
assert!(shell.is_coherent());
}
#[test]
fn coherence_check_rejects_mismatched_envelope() {
let submission = RenderSubmission {
frame_id: FrameId::new(1),
app_mode: AppMode::Game,
packet: RenderSubmissionPacket::ShellUi(ShellUiFramePacket::new(Vec::new())),
};
assert!(!submission.is_coherent());
}
#[test]
fn closed_submission_owns_command_payload() {
let mut commands = vec![Gfx2dCommand::DrawText {
x: 1,
y: 2,
text: "before".to_string(),
color: Color::WHITE,
}];
let submission = RenderSubmission::game2d(
FrameId::new(2),
Game2DFramePacket::new(Default::default(), commands.clone()),
);
commands.push(Gfx2dCommand::Clear { color: Color::BLACK });
let RenderSubmissionPacket::Game2D(packet) = submission.packet else {
panic!("expected game packet");
};
assert_eq!(packet.gfx2d.len(), 1);
assert_eq!(
packet.gfx2d[0],
Gfx2dCommand::DrawText { x: 1, y: 2, text: "before".to_string(), color: Color::WHITE }
);
}
#[test]
fn frame_id_advances_monotonically_with_wrapping_arithmetic() {
assert_eq!(FrameId::new(41).next(), FrameId::new(42));
assert_eq!(FrameId::new(u64::MAX).next(), FrameId::ZERO);
}
}

View File

@ -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":"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-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":"done","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"}]}

View File

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