implements PLN-0074 HAL render submissions
This commit is contained in:
parent
96a23e5419
commit
105a6de11e
@ -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;
|
||||
|
||||
203
crates/console/prometeu-hal/src/render_submission.rs
Normal file
203
crates/console/prometeu-hal/src/render_submission.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -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"}]}
|
||||
|
||||
@ -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]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user