From 5e00f6bb0dfafbeb7e373458de4737fa5cd56347 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Mon, 25 May 2026 14:10:52 +0100 Subject: [PATCH] implements PLN-0079 gfx2d primitive buffering --- .../src/services/vm_runtime/dispatch.rs | 29 +++++++++++++++++-- .../src/services/vm_runtime/lifecycle.rs | 2 ++ .../src/services/vm_runtime/mod.rs | 2 ++ .../src/services/vm_runtime/tests.rs | 7 +++++ .../src/services/vm_runtime/tick.rs | 6 ++-- discussion/index.ndjson | 2 +- .../plans/PLN-0079-gfx2d-primitive-domain.md | 2 +- 7 files changed, 43 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 ef13ad2c..413ff606 100644 --- a/crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs +++ b/crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs @@ -7,12 +7,13 @@ use prometeu_hal::asset::{AssetId, AssetOpStatus, BankType, SlotRef}; use prometeu_hal::color::Color; use prometeu_hal::glyph::Glyph; use prometeu_hal::log::{LogLevel, LogSource}; +use prometeu_hal::primitives::Rect; use prometeu_hal::sprite::Sprite; use prometeu_hal::syscalls::Syscall; use prometeu_hal::vm_fault::VmFault; use prometeu_hal::{ - AudioOpStatus, ComposerOpStatus, HostContext, HostReturn, NativeInterface, SyscallId, - expect_bool, expect_int, + AudioOpStatus, ComposerOpStatus, Gfx2dCommand, HostContext, HostReturn, NativeInterface, + SyscallId, expect_bool, expect_int, }; use std::collections::HashMap; use std::ops::{Deref, DerefMut}; @@ -185,6 +186,7 @@ impl NativeInterface for VmRuntimeHost<'_> { Syscall::SystemRunCart => unreachable!(), Syscall::GfxClear => { let color = self.get_color(expect_int(args, 0)?)?; + self.gfx2d_commands.push(Gfx2dCommand::Clear { color }); hw.gfx_mut().clear(color); Ok(()) } @@ -205,6 +207,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.gfx2d_commands + .push(Gfx2dCommand::FillRect { rect: Rect { x, y, w, h }, color }); hw.gfx_mut().fill_rect(x, y, w, h, color); Ok(()) } @@ -229,6 +233,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.gfx2d_commands.push(Gfx2dCommand::DrawLine { + x0: x1, + y0: y1, + x1: x2, + y1: y2, + color, + }); hw.gfx_mut().draw_line(x1, y1, x2, y2, color); Ok(()) } @@ -252,6 +263,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.gfx2d_commands.push(Gfx2dCommand::DrawCircle { x, y, radius: r, color }); hw.gfx_mut().draw_circle(x, y, r, color); Ok(()) } @@ -275,6 +287,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.gfx2d_commands.push(Gfx2dCommand::DrawDisc { + x, + y, + radius: r, + border_color, + fill_color, + }); hw.gfx_mut().draw_disc(x, y, r, border_color, fill_color); Ok(()) } @@ -300,6 +319,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.gfx2d_commands.push(Gfx2dCommand::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(()) } @@ -324,6 +348,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.gfx2d_commands.push(Gfx2dCommand::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 da1da345..8cc35c04 100644 --- a/crates/console/prometeu-system/src/services/vm_runtime/lifecycle.rs +++ b/crates/console/prometeu-system/src/services/vm_runtime/lifecycle.rs @@ -23,6 +23,7 @@ impl VirtualMachineRuntime { current_cartridge_app_version: String::new(), current_cartridge_app_mode: AppMode::Game, render_manager: RenderManager::new(AppMode::Game), + gfx2d_commands: Vec::new(), logs_written_this_frame: HashMap::new(), atomic_telemetry, last_crash_report: None, @@ -118,6 +119,7 @@ impl VirtualMachineRuntime { self.current_cartridge_app_version.clear(); self.current_cartridge_app_mode = AppMode::Game; self.render_manager = RenderManager::new(AppMode::Game); + self.gfx2d_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 85bbf336..c7029314 100644 --- a/crates/console/prometeu-system/src/services/vm_runtime/mod.rs +++ b/crates/console/prometeu-system/src/services/vm_runtime/mod.rs @@ -7,6 +7,7 @@ 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}; @@ -28,6 +29,7 @@ pub struct VirtualMachineRuntime { pub current_cartridge_app_version: String, pub current_cartridge_app_mode: AppMode, pub render_manager: RenderManager, + pub gfx2d_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 7b0ec1fd..3da461e8 100644 --- a/crates/console/prometeu-system/src/services/vm_runtime/tests.rs +++ b/crates/console/prometeu-system/src/services/vm_runtime/tests.rs @@ -730,6 +730,13 @@ fn tick_draw_text_survives_no_scene_frame_path() { ); assert!(report.is_none(), "no-scene overlay text must not crash"); + let submission = + runtime.render_manager.latest_complete_submission().expect("closed submission"); + let prometeu_hal::RenderSubmissionPacket::Game2D(packet) = &submission.packet else { + panic!("expected Game2D submission"); + }; + assert_eq!(packet.gfx2d.len(), 1); + assert!(matches!(packet.gfx2d[0], prometeu_hal::Gfx2dCommand::DrawText { .. })); hardware.gfx.present(); assert_eq!(hardware.gfx.front_buffer()[0], Color::from_raw(0x11223344).raw()); } 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 43510f52..0184469d 100644 --- a/crates/console/prometeu-system/src/services/vm_runtime/tick.rs +++ b/crates/console/prometeu-system/src/services/vm_runtime/tick.rs @@ -222,10 +222,10 @@ impl VirtualMachineRuntime { { self.render_manager.set_active_app_mode(self.current_cartridge_app_mode); if self.current_cartridge_app_mode == AppMode::Game { + let mut packet = hw.close_game2d_packet(); + packet.gfx2d = std::mem::take(&mut self.gfx2d_commands); self.render_manager - .close_frame_with_packet(RenderSubmissionPacket::Game2D( - hw.close_game2d_packet(), - )) + .close_frame_with_packet(RenderSubmissionPacket::Game2D(packet)) .expect("game packet must match Game app mode"); } else { self.render_manager.close_compat_frame(); diff --git a/discussion/index.ndjson b/discussion/index.ndjson index c39fc9e5..ae7c93fa 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":"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":"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-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-0079-gfx2d-primitive-domain.md b/discussion/workflow/plans/PLN-0079-gfx2d-primitive-domain.md index 58eaea61..3979af28 100644 --- a/discussion/workflow/plans/PLN-0079-gfx2d-primitive-domain.md +++ b/discussion/workflow/plans/PLN-0079-gfx2d-primitive-domain.md @@ -2,7 +2,7 @@ id: PLN-0079 ticket: render-frame-packet-boundary title: Gfx2D Primitive Domain -status: open +status: done created: 2026-05-25 ref_decisions: [DEC-0030] tags: [gfx, renderer, runtime, frame-composer, architecture, ui, pipeline]