From ae85ed4efff66298449d1f845289f030ff5e0feb Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Mon, 25 May 2026 14:13:06 +0100 Subject: [PATCH] implements PLN-0080 shell ui gfxui packet --- .../src/services/vm_runtime/dispatch.rs | 28 +++++++++- .../src/services/vm_runtime/lifecycle.rs | 2 + .../src/services/vm_runtime/mod.rs | 3 +- .../src/services/vm_runtime/tests.rs | 52 +++++++++++++++++++ .../src/services/vm_runtime/tick.rs | 10 +++- discussion/index.ndjson | 2 +- ...N-0080-shell-ui-packet-and-gfxui-domain.md | 2 +- 7 files changed, 92 insertions(+), 7 deletions(-) diff --git a/crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs b/crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs index 413ff606..723ad03c 100644 --- a/crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs +++ b/crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs @@ -12,8 +12,8 @@ use prometeu_hal::sprite::Sprite; use prometeu_hal::syscalls::Syscall; use prometeu_hal::vm_fault::VmFault; use prometeu_hal::{ - AudioOpStatus, ComposerOpStatus, Gfx2dCommand, HostContext, HostReturn, NativeInterface, - SyscallId, expect_bool, expect_int, + AudioOpStatus, ComposerOpStatus, Gfx2dCommand, GfxUiCommand, HostContext, HostReturn, + NativeInterface, SyscallId, expect_bool, expect_int, }; use std::collections::HashMap; use std::ops::{Deref, DerefMut}; @@ -198,6 +198,7 @@ impl NativeInterface for VmRuntimeHost<'_> { )); } let color = self.get_color(expect_int(args, 0)?)?; + self.gfxui_commands.push(GfxUiCommand::Clear { color }); hw.gfx_mut().clear(color); Ok(()) } @@ -224,6 +225,8 @@ impl NativeInterface for VmRuntimeHost<'_> { let w = expect_int(args, 2)? as i32; let h = expect_int(args, 3)? as i32; let color = self.get_color(expect_int(args, 4)?)?; + self.gfxui_commands + .push(GfxUiCommand::FillRect { rect: Rect { x, y, w, h }, color }); hw.gfx_mut().fill_rect(x, y, w, h, color); Ok(()) } @@ -255,6 +258,13 @@ impl NativeInterface for VmRuntimeHost<'_> { let x2 = expect_int(args, 2)? as i32; let y2 = expect_int(args, 3)? as i32; let color = self.get_color(expect_int(args, 4)?)?; + self.gfxui_commands.push(GfxUiCommand::DrawLine { + x0: x1, + y0: y1, + x1: x2, + y1: y2, + color, + }); hw.gfx_mut().draw_line(x1, y1, x2, y2, color); Ok(()) } @@ -278,6 +288,7 @@ impl NativeInterface for VmRuntimeHost<'_> { let y = expect_int(args, 1)? as i32; let r = expect_int(args, 2)? as i32; let color = self.get_color(expect_int(args, 3)?)?; + self.gfxui_commands.push(GfxUiCommand::DrawCircle { x, y, radius: r, color }); hw.gfx_mut().draw_circle(x, y, r, color); Ok(()) } @@ -309,6 +320,13 @@ impl NativeInterface for VmRuntimeHost<'_> { let r = expect_int(args, 2)? as i32; let border_color = self.get_color(expect_int(args, 3)?)?; let fill_color = self.get_color(expect_int(args, 4)?)?; + self.gfxui_commands.push(GfxUiCommand::DrawDisc { + x, + y, + radius: r, + border_color, + fill_color, + }); hw.gfx_mut().draw_disc(x, y, r, border_color, fill_color); Ok(()) } @@ -340,6 +358,11 @@ impl NativeInterface for VmRuntimeHost<'_> { let h = expect_int(args, 3)? as i32; let border_color = self.get_color(expect_int(args, 4)?)?; let fill_color = self.get_color(expect_int(args, 5)?)?; + self.gfxui_commands.push(GfxUiCommand::DrawSquare { + rect: Rect { x, y, w, h }, + border_color, + fill_color, + }); hw.gfx_mut().draw_square(x, y, w, h, border_color, fill_color); Ok(()) } @@ -363,6 +386,7 @@ impl NativeInterface for VmRuntimeHost<'_> { let y = expect_int(args, 1)? as i32; let msg = expect_string(args, 2, "message")?; let color = self.get_color(expect_int(args, 3)?)?; + self.gfxui_commands.push(GfxUiCommand::DrawText { x, y, text: msg.clone(), color }); hw.gfx_mut().draw_text(x, y, &msg, color); Ok(()) } diff --git a/crates/console/prometeu-system/src/services/vm_runtime/lifecycle.rs b/crates/console/prometeu-system/src/services/vm_runtime/lifecycle.rs index 8cc35c04..8fab82b8 100644 --- a/crates/console/prometeu-system/src/services/vm_runtime/lifecycle.rs +++ b/crates/console/prometeu-system/src/services/vm_runtime/lifecycle.rs @@ -24,6 +24,7 @@ impl VirtualMachineRuntime { current_cartridge_app_mode: AppMode::Game, render_manager: RenderManager::new(AppMode::Game), gfx2d_commands: Vec::new(), + gfxui_commands: Vec::new(), logs_written_this_frame: HashMap::new(), atomic_telemetry, last_crash_report: None, @@ -120,6 +121,7 @@ impl VirtualMachineRuntime { self.current_cartridge_app_mode = AppMode::Game; self.render_manager = RenderManager::new(AppMode::Game); self.gfx2d_commands.clear(); + self.gfxui_commands.clear(); self.logs_written_this_frame.clear(); self.last_crash_report = None; diff --git a/crates/console/prometeu-system/src/services/vm_runtime/mod.rs b/crates/console/prometeu-system/src/services/vm_runtime/mod.rs index c7029314..41aeeb6f 100644 --- a/crates/console/prometeu-system/src/services/vm_runtime/mod.rs +++ b/crates/console/prometeu-system/src/services/vm_runtime/mod.rs @@ -7,10 +7,10 @@ mod tick; use crate::CrashReport; use prometeu_bytecode::string_materialization_count; -use prometeu_hal::Gfx2dCommand; use prometeu_hal::app_mode::AppMode; use prometeu_hal::log::LogService; use prometeu_hal::telemetry::{AtomicTelemetry, CertificationConfig, Certifier}; +use prometeu_hal::{Gfx2dCommand, GfxUiCommand}; use prometeu_vm::VirtualMachine; pub use render_manager::RenderManager; use std::collections::HashMap; @@ -30,6 +30,7 @@ pub struct VirtualMachineRuntime { pub current_cartridge_app_mode: AppMode, pub render_manager: RenderManager, pub gfx2d_commands: Vec, + pub gfxui_commands: Vec, pub logs_written_this_frame: HashMap, pub atomic_telemetry: Arc, pub last_crash_report: Option, diff --git a/crates/console/prometeu-system/src/services/vm_runtime/tests.rs b/crates/console/prometeu-system/src/services/vm_runtime/tests.rs index 3da461e8..acdd5dd3 100644 --- a/crates/console/prometeu-system/src/services/vm_runtime/tests.rs +++ b/crates/console/prometeu-system/src/services/vm_runtime/tests.rs @@ -437,6 +437,54 @@ fn tick_game_profile_rejects_gfxui_shell_surface() { } } +#[test] +fn tick_shell_profile_closes_gfxui_commands_into_shell_packet() { + let mut runtime = VirtualMachineRuntime::new(None); + let mut log_service = LogService::new(4096); + let mut fs = VirtualFS::new(); + let mut fs_state = FsState::Unmounted; + let mut memcard = MemcardService::new(); + let mut open_files: HashMap = HashMap::new(); + let mut next_handle = 1; + let mut vm = VirtualMachine::default(); + let mut hardware = Hardware::new(); + let signals = InputSignals::default(); + let code = assemble("PUSH_I32 287454020\nHOSTCALL 0\nFRAME_SYNC\nHALT").expect("assemble"); + let program = serialized_single_function_module( + code, + vec![SyscallDecl { + module: "gfxui".into(), + name: "clear".into(), + version: 1, + arg_slots: 1, + ret_slots: 0, + }], + ); + let cartridge = cartridge_with_program_and_mode(program, caps::GFXUI, AppMode::Shell); + + runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize"); + let report = runtime.tick( + &mut log_service, + &mut fs, + &mut fs_state, + &mut memcard, + &mut open_files, + &mut next_handle, + &mut vm, + &signals, + &mut hardware, + ); + + assert!(report.is_none()); + let submission = + runtime.render_manager.latest_complete_submission().expect("closed submission"); + let prometeu_hal::RenderSubmissionPacket::ShellUi(packet) = &submission.packet else { + panic!("expected ShellUi submission"); + }; + assert_eq!(packet.commands.len(), 1); + assert!(matches!(packet.commands[0], prometeu_hal::GfxUiCommand::Clear { .. })); +} + #[test] fn tick_system_profile_rejects_bank_game_surface() { let mut runtime = VirtualMachineRuntime::new(None); @@ -796,6 +844,9 @@ fn reset_clears_cartridge_scoped_runtime_state() { runtime.frame_start_heap_allocations = 11; runtime.frame_start_string_materializations = 22; runtime.needs_prepare_entry_call = true; + runtime + .gfxui_commands + .push(prometeu_hal::GfxUiCommand::Clear { color: Color::from_raw(0x11223344) }); runtime.reset(&mut vm); @@ -812,6 +863,7 @@ fn reset_clears_cartridge_scoped_runtime_state() { assert!(runtime.current_cartridge_app_version.is_empty()); assert_eq!(runtime.current_cartridge_app_mode, AppMode::Game); assert!(runtime.logs_written_this_frame.is_empty()); + assert!(runtime.gfxui_commands.is_empty()); assert_eq!(runtime.atomic_telemetry.logs_count.load(Ordering::Relaxed), 0); assert_eq!(runtime.atomic_telemetry.current_logs_count.load(Ordering::Relaxed), 0); assert_eq!(runtime.atomic_telemetry.frame_index.load(Ordering::Relaxed), 0); diff --git a/crates/console/prometeu-system/src/services/vm_runtime/tick.rs b/crates/console/prometeu-system/src/services/vm_runtime/tick.rs index 0184469d..43cbf5a2 100644 --- a/crates/console/prometeu-system/src/services/vm_runtime/tick.rs +++ b/crates/console/prometeu-system/src/services/vm_runtime/tick.rs @@ -5,7 +5,9 @@ use crate::fs::{FsState, VirtualFS}; use crate::services::memcard::MemcardService; use prometeu_hal::asset::{BankTelemetry, BankType}; use prometeu_hal::log::{LogLevel, LogService, LogSource}; -use prometeu_hal::{HardwareBridge, HostContext, InputSignals, RenderSubmissionPacket}; +use prometeu_hal::{ + HardwareBridge, HostContext, InputSignals, RenderSubmissionPacket, ShellUiFramePacket, +}; use prometeu_vm::LogicalFrameEndingReason; use std::collections::HashMap; use std::panic::{AssertUnwindSafe, catch_unwind}; @@ -228,7 +230,11 @@ impl VirtualMachineRuntime { .close_frame_with_packet(RenderSubmissionPacket::Game2D(packet)) .expect("game packet must match Game app mode"); } else { - self.render_manager.close_compat_frame(); + let packet = + ShellUiFramePacket::new(std::mem::take(&mut self.gfxui_commands)); + self.render_manager + .close_frame_with_packet(RenderSubmissionPacket::ShellUi(packet)) + .expect("shell packet must match Shell app mode"); } if let Err(payload) = catch_unwind(AssertUnwindSafe(|| hw.render_frame())) { diff --git a/discussion/index.ndjson b/discussion/index.ndjson index ae7c93fa..f0776047 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":"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":"done","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":"done","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":"done","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":"done","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":"done","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":"done","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":"done","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":"done","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":"done","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":"done","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":"done","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-0080-shell-ui-packet-and-gfxui-domain.md b/discussion/workflow/plans/PLN-0080-shell-ui-packet-and-gfxui-domain.md index bec557d6..87a02b3e 100644 --- a/discussion/workflow/plans/PLN-0080-shell-ui-packet-and-gfxui-domain.md +++ b/discussion/workflow/plans/PLN-0080-shell-ui-packet-and-gfxui-domain.md @@ -2,7 +2,7 @@ id: PLN-0080 ticket: render-frame-packet-boundary title: Shell UI Packet and GfxUI Domain -status: open +status: done created: 2026-05-25 ref_decisions: [DEC-0030] tags: [gfx, renderer, runtime, frame-composer, architecture, ui, pipeline]