Real Render Worker Establishment
All checks were successful
Intrepid/Prometeu/Runtime/pipeline/pr-master This commit looks good
All checks were successful
Intrepid/Prometeu/Runtime/pipeline/pr-master This commit looks good
This commit is contained in:
parent
aa917cf44e
commit
21b5d59a7d
@ -123,11 +123,13 @@ mod tests {
|
||||
#[test]
|
||||
fn local_backend_renders_game2d_submission_to_owned_frame() {
|
||||
let backend = backend();
|
||||
let mut packet = Game2DFramePacket::default();
|
||||
packet.gfx2d = vec![Gfx2dCommand::FillRect {
|
||||
rect: Rect { x: 1, y: 1, w: 2, h: 2 },
|
||||
color: Color::RED,
|
||||
}];
|
||||
let packet = Game2DFramePacket {
|
||||
gfx2d: vec![Gfx2dCommand::FillRect {
|
||||
rect: Rect { x: 1, y: 1, w: 2, h: 2 },
|
||||
color: Color::RED,
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
let ownership = RenderOwnership::new(3, AppMode::Game, 9);
|
||||
let submission =
|
||||
RenderSubmission::game2d(FrameId::new(5), packet).with_ownership(ownership);
|
||||
|
||||
@ -69,10 +69,7 @@ fn resolver_rejects_removed_legacy_gfx_set_sprite_identity() {
|
||||
|
||||
let requested = [SyscallIdentity { module: "gfx2d", name: "set_sprite", version: 1 }];
|
||||
let err = resolve_program_syscalls(&requested, caps::ALL).unwrap_err();
|
||||
assert_eq!(
|
||||
err,
|
||||
LoadError::UnknownSyscall { module: "gfx2d".into(), name: "set_sprite".into(), version: 1 }
|
||||
);
|
||||
assert_eq!(err, LoadError::UnknownSyscall { module: "gfx2d", name: "set_sprite", version: 1 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -272,10 +269,7 @@ fn resolver_rejects_removed_bank_slot_info_identity() {
|
||||
|
||||
let requested = [SyscallIdentity { module: "bank", name: "slot_info", version: 1 }];
|
||||
let err = resolve_program_syscalls(&requested, caps::ALL).unwrap_err();
|
||||
assert_eq!(
|
||||
err,
|
||||
LoadError::UnknownSyscall { module: "bank".into(), name: "slot_info".into(), version: 1 }
|
||||
);
|
||||
assert_eq!(err, LoadError::UnknownSyscall { module: "bank", name: "slot_info", version: 1 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -405,11 +405,13 @@ mod tests {
|
||||
};
|
||||
let cert = Certifier::new(config);
|
||||
|
||||
let mut tel = TelemetryFrame::default();
|
||||
tel.cycles_used = 150;
|
||||
tel.syscalls = 10;
|
||||
tel.host_cpu_time_us = 500;
|
||||
tel.glyph_slots_used = 2;
|
||||
let tel = TelemetryFrame {
|
||||
cycles_used: 150,
|
||||
syscalls: 10,
|
||||
host_cpu_time_us: 500,
|
||||
glyph_slots_used: 2,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let violations = cert.evaluate(&tel, &mut ls, 1000);
|
||||
assert_eq!(violations, 3);
|
||||
|
||||
@ -11,7 +11,7 @@ pub use services::fs;
|
||||
pub use services::process;
|
||||
pub use services::task;
|
||||
pub use services::vm_runtime::{
|
||||
RenderWorkerConfig, RenderWorkerController, RenderWorkerHandoff, RenderWorkerHandoffWait,
|
||||
RenderWorkerOwnership, VirtualMachineRuntime,
|
||||
LatestRenderFrameStore, RenderWorkerConfig, RenderWorkerController, RenderWorkerHandoff,
|
||||
RenderWorkerHandoffWait, RenderWorkerOwnership, VirtualMachineRuntime,
|
||||
};
|
||||
pub use services::windows;
|
||||
|
||||
@ -5,8 +5,8 @@ use prometeu_hal::app_mode::AppMode;
|
||||
use prometeu_hal::cartridge::Cartridge;
|
||||
use prometeu_hal::telemetry::{CertificationConfig, TelemetryFrame};
|
||||
use prometeu_hal::{
|
||||
InputSignals, OwnedRgba8888Frame, RenderWorkerBackend, RenderWorkerError,
|
||||
RenderWorkerFrameSink, RuntimePlatform,
|
||||
FrameId, InputSignals, RenderWorkerBackend, RenderWorkerError, RenderWorkerFrameSink,
|
||||
RuntimePlatform,
|
||||
};
|
||||
use prometeu_vm::VirtualMachine;
|
||||
use std::sync::atomic::Ordering;
|
||||
@ -116,12 +116,8 @@ impl<'a> VmFacade<'a> {
|
||||
self.os.vm_runtime.render_worker_controller.as_ref()
|
||||
}
|
||||
|
||||
pub fn latest_render_worker_frame(&self) -> Option<OwnedRgba8888Frame> {
|
||||
self.os.vm_runtime.latest_render_worker_frame()
|
||||
}
|
||||
|
||||
pub fn repeat_latest_render_worker_frame(&self) -> Option<OwnedRgba8888Frame> {
|
||||
self.os.vm_runtime.repeat_latest_render_worker_frame()
|
||||
pub fn record_repeated_render_worker_frame(&self, frame_id: FrameId) {
|
||||
self.os.vm_runtime.record_repeated_render_worker_frame(frame_id);
|
||||
}
|
||||
|
||||
pub fn cert_config(&self) -> &CertificationConfig {
|
||||
|
||||
@ -3,9 +3,7 @@ use crate::CrashReport;
|
||||
use crate::fs::{FsBackend, FsState, VirtualFS};
|
||||
use prometeu_hal::cartridge::Cartridge;
|
||||
use prometeu_hal::log::{LogLevel, LogSource};
|
||||
use prometeu_hal::{
|
||||
OwnedRgba8888Frame, RenderWorkerBackend, RenderWorkerError, RenderWorkerFrameSink,
|
||||
};
|
||||
use prometeu_hal::{FrameId, RenderWorkerBackend, RenderWorkerError, RenderWorkerFrameSink};
|
||||
use std::collections::HashMap;
|
||||
|
||||
impl VirtualMachineRuntime {
|
||||
@ -183,24 +181,14 @@ impl VirtualMachineRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn publish_pending_render_to_worker(&mut self) -> bool {
|
||||
let Some(submission) = self.render_manager.take_pending_for_worker() else {
|
||||
return false;
|
||||
};
|
||||
self.render_worker_handoff.publish(submission);
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) fn sync_render_worker_ownership(&self) {
|
||||
self.render_worker_ownership.set(self.render_manager.active_ownership());
|
||||
}
|
||||
|
||||
pub fn latest_render_worker_frame(&self) -> Option<OwnedRgba8888Frame> {
|
||||
self.render_worker_controller.as_ref()?.latest_published_frame()
|
||||
}
|
||||
|
||||
pub fn repeat_latest_render_worker_frame(&self) -> Option<OwnedRgba8888Frame> {
|
||||
self.render_worker_controller.as_ref()?.repeat_latest_frame()
|
||||
pub fn record_repeated_render_worker_frame(&self, frame_id: FrameId) {
|
||||
if let Some(controller) = self.render_worker_controller.as_ref() {
|
||||
controller.record_repeated_frame(frame_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn initialize_vm(
|
||||
|
||||
@ -21,7 +21,9 @@ use prometeu_hal::telemetry::{AtomicTelemetry, CertificationConfig, Certifier};
|
||||
use prometeu_hal::{Gfx2dCommand, GfxUiCommand};
|
||||
use prometeu_vm::VirtualMachine;
|
||||
pub use render_manager::{RenderManager, RenderRuntimeCapabilities};
|
||||
pub use render_worker::{RenderWorkerConfig, RenderWorkerController, RenderWorkerOwnership};
|
||||
pub use render_worker::{
|
||||
LatestRenderFrameStore, RenderWorkerConfig, RenderWorkerController, RenderWorkerOwnership,
|
||||
};
|
||||
pub use render_worker_handoff::{RenderWorkerHandoff, RenderWorkerHandoffWait};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -25,7 +25,28 @@ impl Default for RenderWorkerConfig {
|
||||
struct RenderWorkerState {
|
||||
telemetry: RenderWorkerTelemetry,
|
||||
last_error: Option<RenderWorkerError>,
|
||||
latest_published_frame: Option<OwnedRgba8888Frame>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct LatestRenderFrameStore {
|
||||
latest: Arc<Mutex<Option<OwnedRgba8888Frame>>>,
|
||||
}
|
||||
|
||||
impl LatestRenderFrameStore {
|
||||
pub fn publish_frame(&self, frame: OwnedRgba8888Frame) {
|
||||
*self.latest.lock().unwrap() = Some(frame);
|
||||
}
|
||||
|
||||
pub fn latest_frame(&self) -> Option<OwnedRgba8888Frame> {
|
||||
self.latest.lock().unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderWorkerFrameSink for LatestRenderFrameStore {
|
||||
fn publish(&self, frame: OwnedRgba8888Frame) -> Result<(), RenderWorkerError> {
|
||||
self.publish_frame(frame);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -100,11 +121,11 @@ impl RenderWorkerController {
|
||||
self.handoff.request_shutdown();
|
||||
match self.done_rx.recv_timeout(self.config.shutdown_timeout) {
|
||||
Ok(result) => {
|
||||
if let Some(handle) = self.handle.take() {
|
||||
if handle.join().is_err() {
|
||||
record_error(&self.state, RenderWorkerError::WorkerPanic, FrameId::ZERO);
|
||||
return Err(RenderWorkerError::WorkerPanic);
|
||||
}
|
||||
if let Some(handle) = self.handle.take()
|
||||
&& handle.join().is_err()
|
||||
{
|
||||
record_error(&self.state, RenderWorkerError::WorkerPanic, FrameId::ZERO);
|
||||
return Err(RenderWorkerError::WorkerPanic);
|
||||
}
|
||||
result
|
||||
}
|
||||
@ -145,15 +166,9 @@ impl RenderWorkerController {
|
||||
self.ownership.snapshot()
|
||||
}
|
||||
|
||||
pub fn latest_published_frame(&self) -> Option<OwnedRgba8888Frame> {
|
||||
self.state.lock().unwrap().latest_published_frame.clone()
|
||||
}
|
||||
|
||||
pub fn repeat_latest_frame(&self) -> Option<OwnedRgba8888Frame> {
|
||||
pub fn record_repeated_frame(&self, frame_id: FrameId) {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
let frame = state.latest_published_frame.clone()?;
|
||||
state.telemetry.record_repeated_frame(frame.frame_id.get());
|
||||
Some(frame)
|
||||
state.telemetry.record_repeated_frame(frame_id.get());
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,19 +212,18 @@ fn run_worker_loop<B, S>(
|
||||
continue;
|
||||
}
|
||||
|
||||
let publish_result = catch_unwind(AssertUnwindSafe(|| sink.publish(frame.clone())))
|
||||
let publish_result = catch_unwind(AssertUnwindSafe(|| sink.publish(frame)))
|
||||
.map_err(|_| RenderWorkerError::WorkerPanic);
|
||||
match publish_result {
|
||||
Ok(Ok(())) => record_published(&state, frame),
|
||||
Ok(Ok(())) => record_published(&state, frame_id),
|
||||
Ok(Err(error)) | Err(error) => record_error(&state, error, frame_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn record_published(state: &Arc<Mutex<RenderWorkerState>>, frame: OwnedRgba8888Frame) {
|
||||
fn record_published(state: &Arc<Mutex<RenderWorkerState>>, frame_id: FrameId) {
|
||||
let mut state = state.lock().unwrap();
|
||||
state.telemetry.record_published(frame.frame_id.get());
|
||||
state.latest_published_frame = Some(frame);
|
||||
state.telemetry.record_published(frame_id.get());
|
||||
}
|
||||
|
||||
fn record_stale_discard(state: &Arc<Mutex<RenderWorkerState>>, frame_id: FrameId) {
|
||||
@ -278,6 +292,35 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn latest_render_frame_store_is_sink_and_observable_publication_source() {
|
||||
let store = LatestRenderFrameStore::default();
|
||||
let first = OwnedRgba8888Frame::packed(
|
||||
FrameId::new(1),
|
||||
RenderOwnership::new(1, AppMode::Game, 1),
|
||||
1,
|
||||
1,
|
||||
vec![0xFF0000FF],
|
||||
)
|
||||
.expect("first frame");
|
||||
let second = OwnedRgba8888Frame::packed(
|
||||
FrameId::new(2),
|
||||
RenderOwnership::new(1, AppMode::Game, 1),
|
||||
1,
|
||||
1,
|
||||
vec![0x00FF00FF],
|
||||
)
|
||||
.expect("second frame");
|
||||
|
||||
store.publish(first).expect("publish first");
|
||||
assert_eq!(store.latest_frame().expect("first latest").frame_id, FrameId::new(1));
|
||||
|
||||
store.publish(second).expect("publish second");
|
||||
let latest = store.latest_frame().expect("second latest");
|
||||
assert_eq!(latest.frame_id, FrameId::new(2));
|
||||
assert_eq!(latest.pixels, vec![0x00FF00FF]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_worker_controller_starts_and_stops_waiting_worker() {
|
||||
let handoff = Arc::new(RenderWorkerHandoff::default());
|
||||
@ -387,27 +430,37 @@ mod tests {
|
||||
fn render_worker_controller_repeats_latest_published_frame_without_rendering() {
|
||||
let handoff = Arc::new(RenderWorkerHandoff::default());
|
||||
let backend = Arc::new(FakeRenderBackend::default());
|
||||
let store = LatestRenderFrameStore::default();
|
||||
let mut controller = RenderWorkerController::start(
|
||||
RenderWorkerConfig::default(),
|
||||
Arc::clone(&handoff),
|
||||
RenderWorkerOwnership::new(RenderOwnership::new(1, AppMode::Game, 1)),
|
||||
SharedFakeBackend(Arc::clone(&backend)),
|
||||
SharedFakeBackend(Arc::clone(&backend)),
|
||||
store.clone(),
|
||||
);
|
||||
|
||||
handoff.publish(game_submission(22, 1, 1));
|
||||
assert_eq!(controller.stop(), Ok(()));
|
||||
|
||||
let latest = controller.latest_published_frame().expect("latest frame");
|
||||
let repeated = controller.repeat_latest_frame().expect("repeated frame");
|
||||
let latest = store.latest_frame().expect("latest frame");
|
||||
controller.record_repeated_frame(latest.frame_id);
|
||||
let repeated = store.latest_frame().expect("repeated frame");
|
||||
|
||||
assert_eq!(latest, repeated);
|
||||
assert_eq!(backend.published_frames().len(), 1);
|
||||
assert!(backend.published_frames().is_empty());
|
||||
assert_eq!(controller.telemetry().repeated_frames, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_publishes_game_submissions_to_worker_without_waiting_for_render() {
|
||||
fn publish_pending(runtime: &mut VirtualMachineRuntime) -> bool {
|
||||
let Some(submission) = runtime.render_manager.take_pending_for_worker() else {
|
||||
return false;
|
||||
};
|
||||
runtime.render_worker_handoff.publish(submission);
|
||||
true
|
||||
}
|
||||
|
||||
let gate = RenderGate::default();
|
||||
let backend = Arc::new(FakeRenderBackend::with_gate(gate.clone()));
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
@ -423,7 +476,7 @@ mod tests {
|
||||
Game2DFramePacket::default(),
|
||||
))
|
||||
.expect("game packet should close");
|
||||
assert!(runtime.publish_pending_render_to_worker());
|
||||
assert!(publish_pending(&mut runtime));
|
||||
gate.wait_for_entries(1);
|
||||
|
||||
runtime
|
||||
@ -432,14 +485,14 @@ mod tests {
|
||||
Game2DFramePacket::default(),
|
||||
))
|
||||
.expect("second game packet should close");
|
||||
assert!(runtime.publish_pending_render_to_worker());
|
||||
assert!(publish_pending(&mut runtime));
|
||||
runtime
|
||||
.render_manager
|
||||
.close_frame_with_packet(prometeu_hal::RenderSubmissionPacket::Game2D(
|
||||
Game2DFramePacket::default(),
|
||||
))
|
||||
.expect("third game packet should close");
|
||||
assert!(runtime.publish_pending_render_to_worker());
|
||||
assert!(publish_pending(&mut runtime));
|
||||
|
||||
assert_eq!(runtime.render_worker_handoff.telemetry().replaced_before_consume, 1);
|
||||
|
||||
|
||||
@ -121,6 +121,7 @@ impl VirtualMachineRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn tick(
|
||||
&mut self,
|
||||
log_service: &mut LogService,
|
||||
@ -276,11 +277,7 @@ impl VirtualMachineRuntime {
|
||||
if self.render_worker_controller.is_some()
|
||||
&& self.current_cartridge_app_mode == AppMode::Game =>
|
||||
{
|
||||
if self.publish_pending_render_to_worker() {
|
||||
RenderConsumeOutcome::NoSubmission
|
||||
} else {
|
||||
RenderConsumeOutcome::NoSubmission
|
||||
}
|
||||
RenderConsumeOutcome::NoSubmission
|
||||
}
|
||||
RenderConsumerPath::RealWorker => {
|
||||
self.render_manager.consume_latest(&mut surface)
|
||||
|
||||
@ -548,17 +548,17 @@ mod tests {
|
||||
// replace with arrays containing cross-references. Since our simple
|
||||
// heap doesn't support in-place element edits via API, simulate by
|
||||
// directly editing stored objects.
|
||||
if let Some(slot) = heap.objects.get_mut(a.0 as usize) {
|
||||
if let Some(obj) = slot.as_mut() {
|
||||
obj.array_elems = Some(vec![Value::HeapRef(b)]);
|
||||
obj.header.payload_len = 1;
|
||||
}
|
||||
if let Some(slot) = heap.objects.get_mut(a.0 as usize)
|
||||
&& let Some(obj) = slot.as_mut()
|
||||
{
|
||||
obj.array_elems = Some(vec![Value::HeapRef(b)]);
|
||||
obj.header.payload_len = 1;
|
||||
}
|
||||
if let Some(slot) = heap.objects.get_mut(b.0 as usize) {
|
||||
if let Some(obj) = slot.as_mut() {
|
||||
obj.array_elems = Some(vec![Value::HeapRef(a)]);
|
||||
obj.header.payload_len = 1;
|
||||
}
|
||||
if let Some(slot) = heap.objects.get_mut(b.0 as usize)
|
||||
&& let Some(obj) = slot.as_mut()
|
||||
{
|
||||
obj.array_elems = Some(vec![Value::HeapRef(a)]);
|
||||
obj.header.payload_len = 1;
|
||||
}
|
||||
|
||||
// Mark from A; should terminate and mark both.
|
||||
@ -720,17 +720,17 @@ mod tests {
|
||||
let b = heap.allocate_array(vec![]);
|
||||
|
||||
// Make A point to B and B point to A.
|
||||
if let Some(slot) = heap.objects.get_mut(a.0 as usize) {
|
||||
if let Some(obj) = slot.as_mut() {
|
||||
obj.array_elems = Some(vec![Value::HeapRef(b)]);
|
||||
obj.header.payload_len = 1;
|
||||
}
|
||||
if let Some(slot) = heap.objects.get_mut(a.0 as usize)
|
||||
&& let Some(obj) = slot.as_mut()
|
||||
{
|
||||
obj.array_elems = Some(vec![Value::HeapRef(b)]);
|
||||
obj.header.payload_len = 1;
|
||||
}
|
||||
if let Some(slot) = heap.objects.get_mut(b.0 as usize) {
|
||||
if let Some(obj) = slot.as_mut() {
|
||||
obj.array_elems = Some(vec![Value::HeapRef(a)]);
|
||||
obj.header.payload_len = 1;
|
||||
}
|
||||
if let Some(slot) = heap.objects.get_mut(b.0 as usize)
|
||||
&& let Some(obj) = slot.as_mut()
|
||||
{
|
||||
obj.array_elems = Some(vec![Value::HeapRef(a)]);
|
||||
obj.header.payload_len = 1;
|
||||
}
|
||||
|
||||
// No roots: perform sweep directly; both should be reclaimed.
|
||||
|
||||
@ -1052,12 +1052,13 @@ mod tests {
|
||||
// 21: Jmp 27
|
||||
// 27: Nop
|
||||
|
||||
let mut code = Vec::new();
|
||||
code.push(OpCode::PushBool as u8);
|
||||
code.push(0x00);
|
||||
code.push(1); // 0: PushBool (3 bytes)
|
||||
code.push(OpCode::JmpIfTrue as u8);
|
||||
code.push(0x00);
|
||||
let mut code = vec![
|
||||
OpCode::PushBool as u8,
|
||||
0x00,
|
||||
1, // 0: PushBool (3 bytes)
|
||||
OpCode::JmpIfTrue as u8,
|
||||
0x00,
|
||||
];
|
||||
code.extend_from_slice(&15u32.to_le_bytes()); // 3: JmpIfTrue (6 bytes)
|
||||
code.push(OpCode::Jmp as u8);
|
||||
code.push(0x00);
|
||||
@ -1247,9 +1248,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_function_without_terminator_is_rejected() {
|
||||
// Single NOP with no RET/JMP/TRAP/HALT at the end → fallthrough to end
|
||||
let mut code = Vec::new();
|
||||
code.push(OpCode::Nop as u8);
|
||||
code.push(0x00);
|
||||
let code = vec![OpCode::Nop as u8, 0x00];
|
||||
|
||||
let functions = vec![FunctionMeta { code_offset: 0, code_len: 2, ..Default::default() }];
|
||||
let res = Verifier::verify(&code, &functions);
|
||||
@ -1259,9 +1258,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_function_with_proper_terminator_passes() {
|
||||
// Minimal function that returns immediately
|
||||
let mut code = Vec::new();
|
||||
code.push(OpCode::Ret as u8);
|
||||
code.push(0x00);
|
||||
let code = vec![OpCode::Ret as u8, 0x00];
|
||||
|
||||
let functions = vec![FunctionMeta {
|
||||
code_offset: 0,
|
||||
@ -1277,9 +1274,7 @@ mod tests {
|
||||
fn test_verifier_ret_too_few_slots() {
|
||||
// Function declares 1 return slot but returns nothing
|
||||
// 0: Ret
|
||||
let mut code = Vec::new();
|
||||
code.push(OpCode::Ret as u8);
|
||||
code.push(0x00);
|
||||
let code = vec![OpCode::Ret as u8, 0x00];
|
||||
|
||||
let functions = vec![FunctionMeta {
|
||||
code_offset: 0,
|
||||
|
||||
@ -1596,9 +1596,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_push_f64_immediate() {
|
||||
let value = 3.125f64;
|
||||
let mut rom = Vec::new();
|
||||
rom.extend_from_slice(&(OpCode::PushF64 as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&3.14f64.to_le_bytes());
|
||||
rom.extend_from_slice(&value.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
@ -1606,7 +1607,7 @@ mod tests {
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
vm.step(&mut native, &mut ctx).unwrap();
|
||||
assert_eq!(vm.peek().unwrap(), &Value::Float(3.14));
|
||||
assert_eq!(vm.peek().unwrap(), &Value::Float(value));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -2537,9 +2538,8 @@ mod tests {
|
||||
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||
|
||||
// Any non-trap outcome is considered success here
|
||||
match report.reason {
|
||||
LogicalFrameEndingReason::Trap(trap) => panic!("Unexpected trap: {:?}", trap),
|
||||
_ => {}
|
||||
if let LogicalFrameEndingReason::Trap(trap) = report.reason {
|
||||
panic!("Unexpected trap: {:?}", trap);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2672,8 +2672,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_loader_hardening_successful_init() {
|
||||
let mut vm = VirtualMachine::default();
|
||||
vm.pc = 123; // Pollution
|
||||
let mut vm = VirtualMachine { pc: 123, ..Default::default() };
|
||||
let code = assemble("HALT").expect("assemble");
|
||||
|
||||
let header = prometeu_bytecode::model::BytecodeModule {
|
||||
@ -3978,26 +3977,26 @@ mod tests {
|
||||
let mut a_href = None;
|
||||
let mut b_href = None;
|
||||
// Consider currently running coroutine
|
||||
if let Some(cur) = vm.current_coro {
|
||||
if let Some(f) = vm.call_stack.last() {
|
||||
if f.func_idx == 1 {
|
||||
a_href = Some(cur);
|
||||
}
|
||||
if f.func_idx == 2 {
|
||||
b_href = Some(cur);
|
||||
}
|
||||
if let Some(cur) = vm.current_coro
|
||||
&& let Some(f) = vm.call_stack.last()
|
||||
{
|
||||
if f.func_idx == 1 {
|
||||
a_href = Some(cur);
|
||||
}
|
||||
if f.func_idx == 2 {
|
||||
b_href = Some(cur);
|
||||
}
|
||||
}
|
||||
// And also consider suspended (Ready/Sleeping) coroutines
|
||||
for h in vm.heap.suspended_coroutine_handles() {
|
||||
if let Some(co) = vm.heap.coroutine_data(h) {
|
||||
if let Some(f) = co.frames.last() {
|
||||
if f.func_idx == 1 {
|
||||
a_href = Some(h);
|
||||
}
|
||||
if f.func_idx == 2 {
|
||||
b_href = Some(h);
|
||||
}
|
||||
if let Some(co) = vm.heap.coroutine_data(h)
|
||||
&& let Some(f) = co.frames.last()
|
||||
{
|
||||
if f.func_idx == 1 {
|
||||
a_href = Some(h);
|
||||
}
|
||||
if f.func_idx == 2 {
|
||||
b_href = Some(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4108,25 +4107,25 @@ mod tests {
|
||||
// Identify A and B coroutine handles (consider both running and suspended)
|
||||
let mut a_href = None;
|
||||
let mut b_href = None;
|
||||
if let Some(cur) = vm.current_coro {
|
||||
if let Some(f) = vm.call_stack.last() {
|
||||
if f.func_idx == 1 {
|
||||
a_href = Some(cur);
|
||||
}
|
||||
if f.func_idx == 2 {
|
||||
b_href = Some(cur);
|
||||
}
|
||||
if let Some(cur) = vm.current_coro
|
||||
&& let Some(f) = vm.call_stack.last()
|
||||
{
|
||||
if f.func_idx == 1 {
|
||||
a_href = Some(cur);
|
||||
}
|
||||
if f.func_idx == 2 {
|
||||
b_href = Some(cur);
|
||||
}
|
||||
}
|
||||
for h in vm.heap.suspended_coroutine_handles() {
|
||||
if let Some(co) = vm.heap.coroutine_data(h) {
|
||||
if let Some(f) = co.frames.last() {
|
||||
if f.func_idx == 1 {
|
||||
a_href = Some(h);
|
||||
}
|
||||
if f.func_idx == 2 {
|
||||
b_href = Some(h);
|
||||
}
|
||||
if let Some(co) = vm.heap.coroutine_data(h)
|
||||
&& let Some(f) = co.frames.last()
|
||||
{
|
||||
if f.func_idx == 1 {
|
||||
a_href = Some(h);
|
||||
}
|
||||
if f.func_idx == 2 {
|
||||
b_href = Some(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4253,26 +4252,26 @@ mod tests {
|
||||
let mut a = None;
|
||||
let mut b = None;
|
||||
// running
|
||||
if let Some(cur) = vm.current_coro {
|
||||
if let Some(f) = vm.call_stack.last() {
|
||||
if f.func_idx == 1 {
|
||||
a = Some(cur);
|
||||
}
|
||||
if f.func_idx == 2 {
|
||||
b = Some(cur);
|
||||
}
|
||||
if let Some(cur) = vm.current_coro
|
||||
&& let Some(f) = vm.call_stack.last()
|
||||
{
|
||||
if f.func_idx == 1 {
|
||||
a = Some(cur);
|
||||
}
|
||||
if f.func_idx == 2 {
|
||||
b = Some(cur);
|
||||
}
|
||||
}
|
||||
// suspended
|
||||
for h in vm.heap.suspended_coroutine_handles() {
|
||||
if let Some(co) = vm.heap.coroutine_data(h) {
|
||||
if let Some(f) = co.frames.last() {
|
||||
if f.func_idx == 1 {
|
||||
a = Some(h);
|
||||
}
|
||||
if f.func_idx == 2 {
|
||||
b = Some(h);
|
||||
}
|
||||
if let Some(co) = vm.heap.coroutine_data(h)
|
||||
&& let Some(f) = co.frames.last()
|
||||
{
|
||||
if f.func_idx == 1 {
|
||||
a = Some(h);
|
||||
}
|
||||
if f.func_idx == 2 {
|
||||
b = Some(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,7 +168,7 @@ fn path_segment_violation(path: &Path) -> Option<String> {
|
||||
}
|
||||
|
||||
fn is_forbidden_ident(tok: &str) -> bool {
|
||||
FORBIDDEN_IDENT_TOKENS.iter().any(|&bad| bad == tok)
|
||||
FORBIDDEN_IDENT_TOKENS.contains(&tok)
|
||||
}
|
||||
|
||||
fn tokenize_identifiers(text: &str) -> Vec<(String, usize, usize)> {
|
||||
|
||||
@ -10,9 +10,9 @@ use pixels::{Pixels, PixelsBuilder, SurfaceTexture};
|
||||
use prometeu_drivers::hardware::Hardware;
|
||||
use prometeu_drivers::{AudioCommand, LocalFramebufferRenderBackend, MemoryBanks};
|
||||
use prometeu_firmware::{BootTarget, Firmware, FirmwareState};
|
||||
use prometeu_hal::RuntimePlatform;
|
||||
use prometeu_hal::telemetry::CertificationConfig;
|
||||
use prometeu_hal::{OwnedRgba8888Frame, RenderWorkerError, RenderWorkerFrameSink, RuntimePlatform};
|
||||
use prometeu_system::RenderWorkerConfig;
|
||||
use prometeu_system::{LatestRenderFrameStore, RenderWorkerConfig};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use winit::application::ApplicationHandler;
|
||||
@ -24,15 +24,6 @@ use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
const IDLE_HOST_POLL_DT: Duration = Duration::from_millis(100);
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct HostWorkerFrameSink;
|
||||
|
||||
impl RenderWorkerFrameSink for HostWorkerFrameSink {
|
||||
fn publish(&self, _frame: OwnedRgba8888Frame) -> Result<(), RenderWorkerError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct PresentationState {
|
||||
latest_published_frame: u64,
|
||||
@ -147,6 +138,8 @@ pub struct HostRunner {
|
||||
audio: HostAudio,
|
||||
/// Last known pause state to sync with audio.
|
||||
last_paused_state: bool,
|
||||
/// Worker-published frame store used as the single observable publication source.
|
||||
render_frame_store: LatestRenderFrameStore,
|
||||
/// Tracks whether a new logical frame or host-only surface invalidation requires presentation.
|
||||
presentation: PresentationState,
|
||||
}
|
||||
@ -177,10 +170,11 @@ impl HostRunner {
|
||||
memory_banks,
|
||||
hardware.assets.glyph_asset_slot_index(),
|
||||
);
|
||||
let render_frame_store = LatestRenderFrameStore::default();
|
||||
firmware.os.vm().start_render_worker(
|
||||
RenderWorkerConfig::default(),
|
||||
worker_backend,
|
||||
HostWorkerFrameSink,
|
||||
render_frame_store.clone(),
|
||||
);
|
||||
|
||||
Self {
|
||||
@ -199,6 +193,7 @@ impl HostRunner {
|
||||
debugger: HostDebugger::new(),
|
||||
audio: HostAudio::new(),
|
||||
last_paused_state: false,
|
||||
render_frame_store,
|
||||
presentation: PresentationState::default(),
|
||||
}
|
||||
}
|
||||
@ -252,7 +247,9 @@ impl ApplicationHandler for HostRunner {
|
||||
|
||||
let window = event_loop.create_window(attrs).expect("failed to create window");
|
||||
|
||||
// 🔥 Leak: Window becomes &'static Window (bootstrap)
|
||||
// `pixels` stores a surface tied to the window lifetime. The current
|
||||
// winit runner owns one process-lifetime window, so we intentionally
|
||||
// promote it to a static reference for the duration of the host process.
|
||||
let window: &'static Window = Box::leak(Box::new(window));
|
||||
self.window = Some(window);
|
||||
|
||||
@ -261,7 +258,7 @@ impl ApplicationHandler for HostRunner {
|
||||
|
||||
let (display_w, display_h) = self.hardware.display_size();
|
||||
let mut pixels = PixelsBuilder::new(display_w as u32, display_h as u32, surface_texture)
|
||||
.present_mode(PresentMode::Fifo) // activate vsync
|
||||
.present_mode(PresentMode::Fifo)
|
||||
.build()
|
||||
.expect("failed to create Pixels");
|
||||
|
||||
@ -298,23 +295,24 @@ impl ApplicationHandler for HostRunner {
|
||||
}
|
||||
|
||||
WindowEvent::RedrawRequested => {
|
||||
// Get Pixels directly from the field (not via helper that gets the entire &mut self)
|
||||
let pixels = self.pixels.as_mut().expect("pixels not initialized");
|
||||
|
||||
{
|
||||
// Mutable borrow of the frame (lasts only within this block)
|
||||
let frame = pixels.frame_mut();
|
||||
|
||||
if should_present_worker_frame(&self.firmware.state)
|
||||
&& let Some(worker_frame) =
|
||||
self.firmware.os.vm().repeat_latest_render_worker_frame()
|
||||
&& let Some(worker_frame) = self.render_frame_store.latest_frame()
|
||||
{
|
||||
self.firmware
|
||||
.os
|
||||
.vm()
|
||||
.record_repeated_render_worker_frame(worker_frame.frame_id);
|
||||
draw_owned_rgba8888_frame_to_rgba8(&worker_frame, frame);
|
||||
} else {
|
||||
let src = self.hardware.gfx.front_buffer();
|
||||
draw_rgba8888_to_rgba8(src, frame);
|
||||
}
|
||||
} // <- frame borrow ends here
|
||||
}
|
||||
|
||||
if pixels.render().is_err() {
|
||||
event_loop.exit();
|
||||
@ -378,7 +376,7 @@ impl ApplicationHandler for HostRunner {
|
||||
self.last_frame_time = now;
|
||||
self.accumulator += frame_delta;
|
||||
|
||||
// 🔥 Logic Update Loop: consumes time in exact 60Hz (16.66ms) slices.
|
||||
// Consume time in exact 60Hz slices.
|
||||
while self.accumulator >= self.frame_target_dt {
|
||||
// Unless the debugger is waiting for a 'start' command, advance the system.
|
||||
if !self.debugger.waiting_for_start {
|
||||
@ -405,7 +403,7 @@ impl ApplicationHandler for HostRunner {
|
||||
}
|
||||
|
||||
if should_present_worker_frame(&self.firmware.state)
|
||||
&& let Some(worker_frame) = self.firmware.os.vm().latest_render_worker_frame()
|
||||
&& let Some(worker_frame) = self.render_frame_store.latest_frame()
|
||||
{
|
||||
self.presentation.note_published_frame(worker_frame.frame_id.get());
|
||||
} else {
|
||||
@ -453,8 +451,10 @@ mod tests {
|
||||
use prometeu_firmware::firmware::firmware_state::{
|
||||
GameRunningStep, HubHomeStep, ShellRunningStep,
|
||||
};
|
||||
use prometeu_hal::app_mode::AppMode;
|
||||
use prometeu_hal::debugger_protocol::DEVTOOLS_PROTOCOL_VERSION;
|
||||
use prometeu_hal::telemetry::{CertificationConfig, TelemetryFrame};
|
||||
use prometeu_hal::{FrameId, OwnedRgba8888Frame, RenderOwnership, RenderWorkerFrameSink};
|
||||
use prometeu_system::task::TaskId;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::TcpStream;
|
||||
@ -534,6 +534,30 @@ mod tests {
|
||||
assert!(!should_present_worker_frame(&FirmwareState::HubHome(HubHomeStep)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn host_presentation_observes_worker_publication_store() {
|
||||
let store = LatestRenderFrameStore::default();
|
||||
let frame = OwnedRgba8888Frame::packed(
|
||||
FrameId::new(9),
|
||||
RenderOwnership::new(1, AppMode::Game, 1),
|
||||
1,
|
||||
1,
|
||||
vec![0xFF00FFFF],
|
||||
)
|
||||
.expect("test frame");
|
||||
|
||||
store.publish(frame).expect("store publish");
|
||||
|
||||
let mut presentation = PresentationState::default();
|
||||
presentation.mark_presented();
|
||||
let published = store.latest_frame().expect("published frame");
|
||||
presentation.note_published_frame(published.frame_id.get());
|
||||
|
||||
assert!(presentation.should_request_redraw());
|
||||
presentation.mark_presented();
|
||||
assert_eq!(presentation.last_presented_frame, Some(9));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn host_debugger_maps_cert_events_from_host_owned_sources() {
|
||||
let telemetry = TelemetryFrame {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,107 @@
|
||||
---
|
||||
id: LSN-0049
|
||||
ticket: real-render-worker-establishment
|
||||
title: Render Workers Publish Frames, Hosts Present Them
|
||||
created: 2026-06-20
|
||||
tags: [runtime, renderer, worker, host, publication, concurrency]
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
The real render worker work moved Prometeu from local, synchronous rendering toward an asynchronous worker path. The important architectural question was not only how to create a thread, but where ownership of render work, frame publication, and native presentation should live.
|
||||
|
||||
The final model keeps the VM producer, render worker, and desktop presenter separated:
|
||||
|
||||
- the VM closes logical render submissions;
|
||||
- the worker consumes owned submissions through a latest-wins handoff;
|
||||
- the worker publishes an owned `OwnedRgba8888Frame`;
|
||||
- the desktop host uploads and presents the latest published frame through native window APIs.
|
||||
|
||||
This boundary is what makes the worker real without making it desktop-specific.
|
||||
|
||||
## Key Decisions
|
||||
|
||||
### Platform Boundary Before Worker Boundary
|
||||
|
||||
**What:** Prometeu removed the monolithic hardware bridge from runtime-facing contracts and introduced explicit platform services before establishing the real worker.
|
||||
|
||||
**Why:** A render worker cannot safely consume `&mut Hardware`, `&mut Gfx`, live `FrameComposer`, or mutable VM state. Those are single-threaded runtime concerns. The worker needs closed data plus read-only resource access.
|
||||
|
||||
**Trade-offs:** This required broad preparatory work before the worker could exist. The payoff is that the worker boundary is now testable without a native window and does not depend on host presentation details.
|
||||
|
||||
### Closed Submissions Are The Worker Input
|
||||
|
||||
**What:** The VM producer closes render work into owned `RenderSubmission` values and publishes them through a single-slot latest-wins handoff.
|
||||
|
||||
**Why:** The worker must not observe partially built render state or preserve an unbounded queue of historical frames. If the producer outruns the consumer, the newest complete submission wins.
|
||||
|
||||
**Trade-offs:** Some produced frames are intentionally dropped before consumption. That is correct for this runtime because the contract is visual freshness and VM progress, not rendering every intermediate state.
|
||||
|
||||
### Publication Store Is The Observable Frame Boundary
|
||||
|
||||
**What:** The sink that receives worker-published `OwnedRgba8888Frame` values is also the source the host reads for presentation.
|
||||
|
||||
**Why:** A no-op sink with a separate controller-owned latest-frame cache makes publication ambiguous. The controller should own lifecycle, shutdown, handoff, errors, and telemetry. The publication store should own the latest observable frame.
|
||||
|
||||
**Trade-offs:** Repeat telemetry remains a runtime concern and is recorded by frame id. Pixel retention stays in the publication store so the host can present without depending on controller internals.
|
||||
|
||||
### Host Presentation Remains Native And Single-Threaded
|
||||
|
||||
**What:** The worker never imports or calls winit, pixels, SDL, swapchain, native texture, or window APIs. The host event loop remains responsible for upload and present.
|
||||
|
||||
**Why:** Native presentation APIs often have thread affinity and backend-specific constraints. Keeping them in the host event loop avoids leaking host constraints into the runtime worker contract.
|
||||
|
||||
**Trade-offs:** The worker publishes memory frames rather than presenting directly. The host still performs the final copy/upload step, but the runtime remains portable and testable.
|
||||
|
||||
## Patterns and Algorithms
|
||||
|
||||
### Latest-Wins Handoff
|
||||
|
||||
Use a single pending slot plus a condition variable:
|
||||
|
||||
- producer publish replaces pending work if the worker has not consumed it;
|
||||
- replacement increments telemetry;
|
||||
- worker wait takes the latest pending submission;
|
||||
- in-flight work is separate from pending work;
|
||||
- producer publication never waits for render completion.
|
||||
|
||||
This is a bounded backpressure model. It prevents queue growth while keeping VM progress independent from render speed.
|
||||
|
||||
### Ownership/Epoch Stale Discard
|
||||
|
||||
Each submission and frame carries render ownership metadata. Before publication, the worker compares the completed frame ownership with the active ownership snapshot. If the frame is stale, it is discarded and counted.
|
||||
|
||||
This prevents obsolete pixels from being presented after an app, mode, or ownership transition.
|
||||
|
||||
### Store-Backed Publication
|
||||
|
||||
The publication sink stores the latest `OwnedRgba8888Frame` in owned memory. The desktop host clones or reads that frame before copying pixels into the native framebuffer.
|
||||
|
||||
The store is intentionally simpler than the controller. It does not own worker lifecycle. It only answers: "what is the latest worker-published frame?"
|
||||
|
||||
## Pitfalls
|
||||
|
||||
### A Worker Thread Alone Is Not A Worker Contract
|
||||
|
||||
Moving code to another thread is not sufficient. The contract must define input ownership, resource access, publication semantics, shutdown, stale-frame behavior, and telemetry.
|
||||
|
||||
### Native Presentation Must Not Leak Back Into Runtime
|
||||
|
||||
It is tempting to let the worker call the host presenter directly. That couples runtime execution to desktop window rules and makes deterministic tests harder. Publish owned frames first; present later in the host.
|
||||
|
||||
### No-Op Sinks Hide Boundary Ambiguity
|
||||
|
||||
A sink that accepts a frame and discards it while another component stores the actual latest frame is a design smell. The object that receives `publish(frame)` should be the observable publication boundary, or the controller should be explicitly documented as that store. Prometeu chose the store-backed sink model.
|
||||
|
||||
### Concurrency Tests Must Publish Before Waiting
|
||||
|
||||
A deterministic gate only works after work has actually reached the worker. Waiting for backend entry before publishing to the handoff causes a pipeline hang. Tests should explicitly publish pending work before waiting on worker-side synchronization.
|
||||
|
||||
## Takeaways
|
||||
|
||||
- Render workers should consume closed, owned submissions, not live runtime state.
|
||||
- Latest-wins handoff is a deliberate bounded backpressure model, not a queue shortcut.
|
||||
- In-flight render work must be validated against current ownership before publication.
|
||||
- Worker publication and host presentation are different responsibilities.
|
||||
- The sink/store that receives published frames should be the single observable source for presentation.
|
||||
- Deterministic concurrency tests should synchronize on real state transitions, not sleeps or assumed scheduling.
|
||||
@ -1,191 +0,0 @@
|
||||
---
|
||||
id: AGD-0042
|
||||
ticket: real-render-worker-establishment
|
||||
title: Host Hardware and Render Boundary Preparation
|
||||
status: accepted
|
||||
created: 2026-06-06
|
||||
resolved:
|
||||
decision: DEC-0032
|
||||
tags: [runtime, renderer, hardware, host, hal, boundary, architecture]
|
||||
---
|
||||
|
||||
## Contexto
|
||||
|
||||
`DSC-0040` fechou a arquitetura base para separar VM/logical execution de render consumption. A implementacao atual ainda usa `HardwareBridge` como uma trait agregadora grande: render, composer, `Gfx`, audio, input, touch e assets ficam expostos juntos para runtime, firmware e syscalls.
|
||||
|
||||
Para estabelecer um worker real, precisamos antes preparar essa fronteira. A direcao agora nao e mais transformar `HardwareBridge` em uma interface host-implemented permanente. A direcao e reformular o runtime em uma platform layer com servicos explicitos e eliminar completamente o `HardwareBridge` monolitico como contrato final.
|
||||
|
||||
O worker real deve depender apenas de uma sub-abstracao minima de render, nunca de `&mut Hardware` inteiro.
|
||||
|
||||
Esta agenda (`AGD-0042`) discute a preparacao/refatoracao de fronteiras. A agenda seguinte (`AGD-0043`) dentro da mesma `DSC-0042` discutira o estabelecimento do worker real propriamente dito.
|
||||
|
||||
## Problema
|
||||
|
||||
Hoje o sistema ja tem uma trait (`HardwareBridge`), mas ela ainda permite acoplamentos que impedem um worker real limpo:
|
||||
|
||||
- `HostContext` entrega `&mut dyn HardwareBridge` para syscalls;
|
||||
- syscalls `gfx2d.*` e `gfxui.*` ainda fazem buffering de comandos e tambem desenham imediatamente via `hw.gfx_mut()`;
|
||||
- composer state (`bind_scene`, `set_camera`, `emit_sprite`, `close_game2d_packet`) vive dentro da mesma abstracao que render/present;
|
||||
- firmware e Hub publicam diretamente via `hw.publish_render_submission`;
|
||||
- host desktop instancia `prometeu_drivers::Hardware` diretamente e usa constantes concretas como `Hardware::W/H`;
|
||||
- testes dependem fortemente do `Hardware` concreto.
|
||||
|
||||
Se introduzirmos o worker real agora, a implementacao provavelmente acabara passando `&mut Hardware`, `&mut Gfx` ou `FrameComposer` vivo para a fronteira do worker por conveniencia. Isso quebraria o contrato definido em `DEC-0031`.
|
||||
|
||||
## Pontos Criticos
|
||||
|
||||
### 0. Escopo da migracao completa
|
||||
|
||||
Podemos migrar render primeiro para reduzir risco e desbloquear o worker real, mas a mudanca nao deve parar no render. Audio, input, touch, assets/storage, clock/pacing e telemetry tambem devem sair do `HardwareBridge` monolitico para servicos/facades explicitos.
|
||||
|
||||
`HardwareBridge` deve morrer ao final desta mudanca. Qualquer uso intermediario e scaffold de migracao, nao contrato final.
|
||||
|
||||
### 1. Eliminacao do `HardwareBridge`
|
||||
|
||||
`HardwareBridge` pode existir apenas como detalhe transitorio durante a migracao, se isso reduzir risco de implementacao. Ele nao deve sobreviver como compat layer permanente nem como contrato publico do runtime.
|
||||
|
||||
O estado final deve remover `HardwareBridge` e substituir o acesso monolitico por servicos explicitos de plataforma.
|
||||
|
||||
### 2. Render submission sink
|
||||
|
||||
O primeiro corte provavelmente deve extrair algo como `RenderSubmissionSink`, separando `submit/publish` de `HardwareBridge`.
|
||||
|
||||
### 3. Remocao de render imediato das syscalls
|
||||
|
||||
As syscalls de `gfx2d`/`gfxui` devem apenas gravar comandos nos buffers que fecham o packet. O desenho imediato via `hw.gfx_mut()` deve sair antes do worker real.
|
||||
|
||||
### 4. Composer como dominio logico
|
||||
|
||||
`bind_scene`, `set_camera`, `emit_sprite` e `close_game2d_packet` pertencem ao fechamento logico do frame. Precisamos separar essa capacidade do backend que rasteriza/presenta.
|
||||
|
||||
### 5. Host-owned concrete hardware
|
||||
|
||||
`prometeu_drivers::Hardware` deve continuar existindo como implementacao local/default, mas nao como a arquitetura. O host desktop pode owns essa implementacao ou uma composicao de facades.
|
||||
|
||||
### 6. Test migration
|
||||
|
||||
Muitos testes instanciam `Hardware::new()`. A migracao deve preservar fixtures simples, talvez com um `TestHardware` ou `LocalHardware` que implemente as novas facades.
|
||||
|
||||
## Opcoes
|
||||
|
||||
### Opcao A - Quebrar `HardwareBridge` em uma unica grande refatoracao
|
||||
|
||||
**Abordagem:** substituir `HardwareBridge` por facades menores em runtime, firmware, host e testes de uma vez.
|
||||
|
||||
**Pros:**
|
||||
- resultado final mais limpo imediatamente;
|
||||
- remove rapidamente o risco de acoplamento.
|
||||
|
||||
**Contras:**
|
||||
- alto blast radius;
|
||||
- muitos testes mudam ao mesmo tempo;
|
||||
- dificil isolar regressao funcional de regressao arquitetural.
|
||||
|
||||
**Manutenibilidade:** boa no destino, arriscada durante a transicao.
|
||||
|
||||
### Opcao B - Extrair facades incrementalmente e eliminar `HardwareBridge` no final
|
||||
|
||||
**Abordagem:** criar facades menores (`RenderSubmissionSink`, composer/logical frame facade, audio/input/assets), usar `HardwareBridge` apenas como scaffold temporario se necessario, migrar callers por etapas e remover `HardwareBridge` ao final.
|
||||
|
||||
**Pros:**
|
||||
- menor risco;
|
||||
- permite commits pequenos;
|
||||
- preserva testes e host desktop enquanto a fronteira e endurecida;
|
||||
- facilita medir quando o worker real ja pode nascer.
|
||||
|
||||
**Contras:**
|
||||
- periodo intermediario com duas camadas;
|
||||
- exige disciplina para nao continuar usando `gfx_mut()` no caminho errado.
|
||||
- requer criterio explicito de remocao para evitar que o scaffold vire legado.
|
||||
|
||||
**Manutenibilidade:** melhor equilibrio para o estado atual do repo.
|
||||
|
||||
### Opcao C - Criar apenas `RenderSubmissionSink` agora e adiar o resto
|
||||
|
||||
**Abordagem:** extrair somente a publicacao de submissions e manter composer/gfx/audio/input/assets no `HardwareBridge` por enquanto.
|
||||
|
||||
**Pros:**
|
||||
- menor mudanca inicial;
|
||||
- desbloqueia parte do worker path.
|
||||
|
||||
**Contras:**
|
||||
- ainda deixa syscalls e composer acoplados ao hardware grande;
|
||||
- worker real ainda pode esbarrar no viewport cache/composer vivo;
|
||||
- risco de adiar o problema central.
|
||||
|
||||
**Manutenibilidade:** aceitavel como primeiro PR, insuficiente como preparacao completa.
|
||||
|
||||
## Sugestao / Recomendacao
|
||||
|
||||
Recomendo a **Opcao B**, em fases, com uma restricao forte: **`HardwareBridge` deve ser eliminado no estado final**.
|
||||
|
||||
1. Criar `RenderSubmissionSink` com erro tipado minimo, mantendo implementacao local em `prometeu_drivers::Hardware`.
|
||||
2. Trocar publication local para depender de `RenderSubmissionSink`, nao do `HardwareBridge` inteiro.
|
||||
3. Remover writes imediatos de `gfx_mut()` nas syscalls `gfx2d`/`gfxui`; syscalls devem apenas bufferizar comandos.
|
||||
4. Extrair uma facade de composer/logical frame closure para `bind_scene`, `set_camera`, `emit_sprite`, `close_game2d_packet`.
|
||||
5. Migrar firmware, Hub, runtime, host desktop e testes para as facades novas.
|
||||
6. Remover `HardwareBridge` e qualquer dependencia nova dele.
|
||||
7. Atualizar fixtures/testes para dependerem das facades certas.
|
||||
|
||||
O criterio tecnico: antes da agenda do worker real virar plano, nenhum caminho de render worker deve precisar de `&mut Hardware`, `&mut Gfx`, `FrameComposer` vivo ou estado mutavel da VM.
|
||||
|
||||
O criterio arquitetural: antes desta agenda fechar, `HardwareBridge` deve estar definido como artefato a ser removido, nao como compatibilidade preservada.
|
||||
|
||||
## Perguntas em Aberto
|
||||
|
||||
- [x] `HardwareBridge` deve sobreviver como compatibilidade final?
|
||||
- Resposta: nao. Ele deve ser completamente eliminado no destino.
|
||||
- [x] Se houver transicao incremental, `HardwareBridge` deve herdar facades menores ou apenas expor accessors temporarios?
|
||||
- Resposta: se existir durante a migracao, deve usar accessors/adapters temporarios explicitos. Nao deve herdar facades menores, para nao parecer o novo contrato arquitetural.
|
||||
- [x] `RenderSubmissionSink` deve receber `&RenderSubmission` ou owned `RenderSubmission`?
|
||||
- Resposta: owned `RenderSubmission`. O contrato deve nascer pronto para handoff/worker; o caminho local pode adaptar internamente quando necessario.
|
||||
- [x] O erro tipado minimo entra ja nesta preparacao ou fica para a agenda do worker real?
|
||||
- Resposta: entra ja nesta preparacao, em forma minima. A API nao deve nascer infalivel para ser quebrada logo na agenda do worker real.
|
||||
- [x] Qual e a primeira syscall/teste que prova que `gfx2d` nao desenha mais imediatamente?
|
||||
- Resposta: comecar por `gfx2d.clear` e `gfx2d.draw_text`. Os testes devem provar que a syscall apenas bufferiza comando e que pixels mudam somente apos fechamento/publicacao da submission.
|
||||
- [x] Composer deve ficar no runtime/logical side ou continuar em `prometeu_drivers::Hardware` enquanto nao houver worker?
|
||||
- Resposta: deve ficar no logical side/runtime service. A migracao pode ser faseada, mas `bind_scene`, `set_camera`, `emit_sprite` e `close_game2d_packet` sao preparacao de frame logico, nao backend fisico.
|
||||
- [x] Como preservar testes simples que hoje usam `Hardware::new()`?
|
||||
- Resposta: criar `TestPlatform` como fixture explicita para testes. Nao preservar `HardwareBridge` como compatibilidade.
|
||||
- [x] Que nomes vamos usar: `Hardware`, `HostHardware`, `LocalHardware`, `RuntimeHardware`, `RenderDevice`, `RenderSubmissionSink`?
|
||||
- Resposta: usar `Platform`/`RuntimePlatform` para o agregado de servicos, `TestPlatform` para fixtures de teste, `RenderSubmissionSink` para submissao, `RenderBackend` para raster/present, e `Game2DFrameComposer` para o composer logico de Game2D. Evitar `Hardware` como nome arquitetural central.
|
||||
- [x] Onde os contratos da platform layer devem viver?
|
||||
- Resposta: contratos/facades em `prometeu-hal`; implementacoes locais/test em `prometeu-drivers`; integracao e ownership operacional no `prometeu-system` e nos hosts.
|
||||
- [x] `Game2DFrameComposer` pertence ao runtime/logical side ou ao backend de render?
|
||||
- Resposta: pertence ao logical side. O contrato deve ficar separado do backend renderizavel. A implementacao inicial pode migrar em fases, mas nao deve continuar sendo tratada como parte do hardware fisico.
|
||||
- [x] Qual e o criterio para remover `HardwareBridge`?
|
||||
- Resposta: remover quando runtime, firmware, Hub, host desktop e testes tiverem migrado para facades explicitas. A remocao deve ser plano proprio ou criterio final de um plano de migracao, nao compatibilidade opcional.
|
||||
- [x] Aceitamos a quebra intencional de render imediato em `gfx2d`/`gfxui`?
|
||||
- Resposta: sim. Syscalls devem bufferizar comandos; pixels devem mudar somente no fechamento/publicacao da submission. Testes que dependem de desenho imediato devem ser atualizados para o novo contrato.
|
||||
|
||||
## Criterio para Encerrar
|
||||
|
||||
Esta agenda pode virar decision quando tivermos:
|
||||
|
||||
- sequencia clara de refatoracao para preparar a fronteira;
|
||||
- definicao das facades minimas;
|
||||
- estrategia para remover render imediato das syscalls;
|
||||
- decisao de remocao do `HardwareBridge` e estrategia de transicao, se houver;
|
||||
- estrategia de testes/fixtures;
|
||||
- criterio objetivo de pronto para iniciar `AGD-0043`.
|
||||
|
||||
## Discussion
|
||||
|
||||
- 2026-06-06: Agenda reescopada. A `DSC-0042` passa a ter duas agendas: esta para preparacao da fronteira host/hardware/render, e `AGD-0043` para o worker real.
|
||||
- 2026-06-06: Direcao levantada: `Hardware` deve ser uma abstracao/contrato implementado pelo host, nao um objeto concreto owned pelo runtime que cruza a fronteira do worker.
|
||||
- 2026-06-06: Avaliacao inicial de impacto: mudanca media/alta, mas fatiavel. O acoplamento principal esta em `HardwareBridge`, `HostContext`, syscalls de `dispatch`, firmware, Hub, host desktop e testes.
|
||||
- 2026-06-06: Reformulacao de objetivo: a intencao inicial era expor `Hardware` como interface consumida pelo runtime e implementada pelo host. Com a evolucao do sistema, isso pode estar limitado demais. O novo objetivo e reformular o runtime para um padrao mais proximo de uma plataforma handheld: runtime/OS como dono de lifecycle, scheduling, recursos e contratos; host/board support package como implementacao de dispositivos; render/audio/input/assets expostos por facades separadas e testaveis, nao por uma unica abstracao monolitica de hardware.
|
||||
- 2026-06-06: Direcao cravada: `HardwareBridge` nao deve ser preservado como compatibilidade. No estado final da reformulacao ele deve ser completamente eliminado, substituido por servicos/facades explicitos de platform layer.
|
||||
- 2026-06-06: Perguntas abertas respondidas: transicao por adapters/accessors temporarios, `RenderSubmissionSink` owned, erro tipado minimo ja na preparacao, primeiros testes em `gfx2d.clear`/`gfx2d.draw_text`, composer no logical side, fixtures via `TestPlatform`, e nomes preferidos `Platform`/`RuntimePlatform`, `RenderSubmissionSink`, `RenderBackend`, `Game2DFrameComposer`.
|
||||
- 2026-06-06: Escopo confirmado: render pode migrar primeiro, mas a migracao deve cobrir todos os dominios atualmente presos ao `HardwareBridge`. O objetivo final inclui a remocao completa do `HardwareBridge`, nao apenas reduzir seu uso no render.
|
||||
- 2026-06-06: Pontos adicionais aceitos: facades em `prometeu-hal`, implementacoes local/test em `prometeu-drivers`, integracao em `prometeu-system`/hosts; `Game2DFrameComposer` no logical side; `HardwareBridge` removido apos migracao completa dos callers; e quebra intencional do desenho imediato das syscalls `gfx2d`/`gfxui`.
|
||||
|
||||
## Resolution
|
||||
|
||||
`AGD-0042` resolve que o destino arquitetural nao e uma interface monolitica `HardwareBridge` implementada pelo host. O destino e uma platform layer composta por servicos/facades explicitos, com contratos em `prometeu-hal`, implementacoes local/test em `prometeu-drivers`, e integracao operacional em `prometeu-system` e nos hosts.
|
||||
|
||||
`HardwareBridge` pode existir apenas como scaffold temporario de migracao. Ele deve ser completamente eliminado ao final, sem compatibilidade permanente. A migracao pode comecar por render, mas deve cobrir todos os dominios atualmente presos ao bridge: render, composer/frame, audio, input/touch, assets/storage, clock/pacing e telemetry quando aplicavel.
|
||||
|
||||
O primeiro corte deve preparar o caminho do render worker sem implementa-lo ainda: introduzir `RenderSubmissionSink` owned com erro tipado minimo, remover desenho imediato das syscalls `gfx2d`/`gfxui`, mover `Game2DFrameComposer` para o logical side/runtime service, e substituir fixtures baseadas em `Hardware::new()` por `TestPlatform`.
|
||||
|
||||
Esta agenda estara pronta para decision quando a decision puder definir a sequencia de migracao e os criterios de remocao final do `HardwareBridge` sem reabrir o contrato de render worker da `DSC-0040`.
|
||||
@ -1,241 +0,0 @@
|
||||
---
|
||||
id: AGD-0043
|
||||
ticket: real-render-worker-establishment
|
||||
title: Real Render Worker Establishment
|
||||
status: accepted
|
||||
created: 2026-06-06
|
||||
resolved:
|
||||
decision:
|
||||
tags: [runtime, renderer, worker, concurrency, host, hal, architecture]
|
||||
---
|
||||
|
||||
## Contexto
|
||||
|
||||
`DEC-0031` definiu o contrato arquitetural da fronteira VM/render. `AGD-0042` preparou a fronteira host/hardware/render para que `Hardware` deixe de ser um objeto concreto atravessando o worker e passe a ser contrato/facades implementadas pelo host.
|
||||
|
||||
Esta agenda discute a implementacao do worker real propriamente dito, assumindo que a preparacao da fronteira ja foi resolvida antes da execucao.
|
||||
|
||||
### Estado Atual Depois da `AGD-0042`
|
||||
|
||||
A preparacao de fronteira foi executada pelos planos `PLN-0098` a `PLN-0109`:
|
||||
|
||||
- `HardwareBridge` foi removido do codigo e deixou de ser contrato publico;
|
||||
- `RuntimePlatform` passou a ser a fronteira runtime-facing para render, input, audio, assets e telemetry;
|
||||
- `RenderSubmissionSink` aceita submissao owned;
|
||||
- `Game2DFrameComposer` separa fechamento logico de frame do backend de render;
|
||||
- syscalls `gfx2d.*` e `gfxui.*` bufferizam comandos e nao desenham imediatamente via `Gfx`;
|
||||
- firmware, Hub, runtime e host desktop passam por servicos de plataforma;
|
||||
- testes de runtime/firmware usam `TestPlatform` como fixture local;
|
||||
- specs de GFX/portabilidade documentam que o worker/render consumer nao deve depender de `&mut Hardware`, `&mut Gfx`, `FrameComposer` vivo ou estado mutavel da VM.
|
||||
|
||||
O estado atual ainda nao tem worker real. O `LocalRenderWorker` continua sendo o caminho local/cooperativo que consome `RenderSubmission` no mesmo processo. O proximo passo desta agenda e decidir onde vive o controller do worker real, qual handoff thread-safe ele usa, e qual backend renderizavel ele owns ou recebe.
|
||||
|
||||
## Problema
|
||||
|
||||
O `LocalRenderWorker` atual e cooperativo/local. Ele prova o roteamento de policy, mas nao prova:
|
||||
|
||||
- thread/core real;
|
||||
- ownership do backend renderizavel;
|
||||
- present loop;
|
||||
- stop token para current work;
|
||||
- bounded shutdown/join;
|
||||
- stale epoch durante rasterizacao;
|
||||
- erro tipado de render/present;
|
||||
- repeat real do ultimo frame valido;
|
||||
- ausencia de bloqueio da VM sob atraso do render.
|
||||
|
||||
## Pontos Criticos
|
||||
|
||||
### 1. Ownership e lifecycle do worker
|
||||
|
||||
Precisamos decidir quem cria, inicia, para e reinicia o worker: runtime controller, host/HAL, ou ambos em contrato dividido.
|
||||
|
||||
### 2. Handoff real
|
||||
|
||||
O single-slot latest-wins ja existe como abstracao local. O worker real precisa de uma implementacao thread-safe, sem fila crescente e sem bloquear a VM.
|
||||
|
||||
### 3. Backend renderizavel
|
||||
|
||||
O worker deve owns um backend minimo ou receber capability host-owned. Esse backend deve consumir `RenderSubmission`, resolver recursos read-only e produzir/presentar frame sem `&mut Hardware`.
|
||||
|
||||
### 4. Present cadence
|
||||
|
||||
Precisamos decidir se o worker roda a 60Hz, se o host event loop dirige present, ou se ha uma autoridade separada de display cadence. Repeated present deve virar comportamento real, nao apenas hook.
|
||||
|
||||
### 5. Shutdown/current work
|
||||
|
||||
Quando houver uma submission em rasterizacao, shutdown e owner transition precisam impedir present obsoleto e terminar em tempo bounded.
|
||||
|
||||
### 6. Error model
|
||||
|
||||
Precisamos trocar o modelo infalivel/panic containment por erro tipado para render/present/worker failure.
|
||||
|
||||
### 7. Testes de concorrencia
|
||||
|
||||
Precisamos provar o contrato sem depender de janela nativa nem sleeps frageis.
|
||||
|
||||
## Opcoes
|
||||
|
||||
### Opcao A - Worker thread no runtime com backend mockavel
|
||||
|
||||
**Abordagem:** runtime cria um worker thread generico que recebe um backend implementando trait testavel.
|
||||
|
||||
**Pros:**
|
||||
- testes de contrato mais diretos;
|
||||
- menor dependencia do host desktop.
|
||||
|
||||
**Contras:**
|
||||
- runtime passa a owns detalhes de thread;
|
||||
- precisa cuidado para nao absorver politica de host/window.
|
||||
|
||||
**Manutenibilidade:** boa se o backend for limpo; ruim se virar acoplamento de host dentro do runtime.
|
||||
|
||||
### Opcao B - Worker thread no host desktop primeiro
|
||||
|
||||
**Abordagem:** implementar o primeiro worker no host winit/pixels e adaptar o runtime a submit/policy.
|
||||
|
||||
**Pros:**
|
||||
- valida o caso real visual;
|
||||
- encaixa com surface/present/winit.
|
||||
|
||||
**Contras:**
|
||||
- mais dificil testar sem janela;
|
||||
- risco de contrato ficar host-specific;
|
||||
- portabilidade fica menos comprovada.
|
||||
|
||||
**Manutenibilidade:** boa para desktop, menos boa para hardware proprio.
|
||||
|
||||
### Opcao C - Worker controller runtime + backend host/HAL
|
||||
|
||||
**Abordagem:** criar um controller de worker que implementa handoff, stop token, epoch check e telemetry; o backend concreto vem de host/HAL e pode ser mockado em testes.
|
||||
|
||||
**Pros:**
|
||||
- separa contrato de backend;
|
||||
- testavel sem janela;
|
||||
- mapeia para thread desktop, outro core ou fallback;
|
||||
- preserva `RenderManager` como coordenador.
|
||||
|
||||
**Contras:**
|
||||
- desenho inicial mais exigente;
|
||||
- depende da preparacao da `AGD-0042`.
|
||||
|
||||
**Manutenibilidade:** melhor opcao se queremos worker real sem amarrar a winit.
|
||||
|
||||
## Sugestao / Recomendacao
|
||||
|
||||
Recomendo a **Opcao C - Worker controller runtime + backend host/HAL**.
|
||||
|
||||
O primeiro worker real deveria:
|
||||
|
||||
- consumir um handoff thread-safe single-slot;
|
||||
- ter stop token e shutdown bounded;
|
||||
- checar ownership/epoch antes de present;
|
||||
- expor erros tipados;
|
||||
- repetir o ultimo frame valido em cadence definida;
|
||||
- sincronizar telemetry sem alterar semantica da VM;
|
||||
- ter backend fake/mocked para testes de concorrencia.
|
||||
|
||||
O host desktop pode ser a primeira integracao concreta, mas nao deve ser o unico lugar onde o contrato e testado.
|
||||
|
||||
## Perguntas em Aberto
|
||||
|
||||
### 1. Onde vive o worker controller?
|
||||
|
||||
**Pergunta:** O worker controller vive em `prometeu-system`, `prometeu-hal`, `prometeu-drivers`, ou host?
|
||||
|
||||
**Sugestao:** colocar o controller contratual em `prometeu-system`, proximo de `RenderManager`, e manter traits pequenas em `prometeu-hal` quando forem superficie compartilhada. `prometeu-drivers` deve continuar oferecendo implementacoes locais/testaveis; host desktop deve integrar o controller, nao definir o contrato.
|
||||
|
||||
**Motivo:** o `RenderManager` ja owns policy de AppMode, ownership/epoch, latest-wins e telemetry. Se o controller nascer apenas no host, a semantica de worker vira desktop-specific e fica mais dificil provar o contrato sem janela nativa.
|
||||
|
||||
### 2. Qual handoff thread-safe usar?
|
||||
|
||||
**Pergunta:** O handoff thread-safe sera `Mutex<Option<...>>`, canal bounded, atomic slot, ou estrutura propria?
|
||||
|
||||
**Sugestao:** comecar com uma estrutura propria simples baseada em `Mutex<Option<RenderSubmission>> + Condvar`, encapsulada como single-slot latest-wins. O produtor substitui a pending submission sem bloquear em rasterizacao; o consumidor espera por nova submission ou shutdown.
|
||||
|
||||
**Motivo:** canal bounded tende a preservar historico ou bloquear produtor se nao for usado com cuidado. Um slot explicito modela diretamente a regra ja aceita: nao existe fila crescente, a ultima submissao completa vence.
|
||||
|
||||
### 3. Como modelar current work, shutdown e stale owner?
|
||||
|
||||
**Pergunta:** Como modelar current work para shutdown e stale owner?
|
||||
|
||||
**Sugestao:** separar `pending` de `in_flight`. Ao tomar uma submission, o worker carimba `in_flight` localmente, renderiza, e checa ownership/epoch contra um snapshot atomico ou handle compartilhado antes de present. Shutdown sinaliza stop, acorda o worker e faz join bounded; se houver trabalho em andamento, ele pode terminar rasterizacao, mas nao pode apresentar se o owner/epoch estiver obsoleto ou o stop ja exigir descarte. O timeout deve ser configuravel; como default inicial, usar `250ms`, que e longo o bastante para evitar falso positivo em desktop comum e curto o bastante para detectar worker preso sem travar encerramento.
|
||||
|
||||
**Motivo:** isso evita que shutdown dependa de cancelar mid-raster em codigo que talvez nao seja cancelavel, mas ainda impede present obsoleto.
|
||||
|
||||
### 4. Quem chama present?
|
||||
|
||||
**Pergunta:** Quem chama present: worker, host event loop, ou display cadence service?
|
||||
|
||||
**Sugestao:** no primeiro corte, o worker deve produzir/publish uma superficie pronta como buffer RGBA8888 owned, por exemplo `OwnedRgba8888Frame`, definido em `prometeu-hal`. O host event loop continua sendo autoridade de apresentacao da janela nativa. O "repeat ultimo frame valido" deve repetir o ultimo buffer publicado, nao recompor nem exigir que o worker conheca a API nativa de janela/swap.
|
||||
|
||||
**Motivo:** RGBA8888 ja e o formato logico canonico nas specs. Um frame owned com `FrameId`, ownership/epoch, largura, altura, `stride_pixels` e pixels RGBA8888 em `Vec<u32>` deixa o contrato testavel sem janela e evita acoplamento a winit/pixels, SDL, swapchain ou textura nativa. Colocar o swap real dentro do worker pode funcionar em alguns hosts e quebrar portabilidade. O contrato importante e que a VM nao bloqueie e que o host tenha uma superficie publicada coerente.
|
||||
|
||||
### 5. Como o worker recebe recursos read-only?
|
||||
|
||||
**Pergunta:** Como o worker recebe recursos read-only preparados na `AGD-0042`?
|
||||
|
||||
**Sugestao:** o primeiro backend deve receber `RenderSubmission` owned e uma interface compacta de acesso read-only aos banks por id. Banks sao read-only por definicao durante o consumo de render; o worker resolve recursos por ids estaveis, sem copiar bank, sem snapshot de bank e sem depender de `Arc` como requisito do contrato.
|
||||
|
||||
**Motivo:** `AGD-0042` eliminou o bridge monolitico exatamente para impedir que o worker dependa do hardware inteiro. A proxima decisao precisa declarar quais recursos sao lidos pelo render backend e como sua estabilidade e garantida durante uma submissao em voo. A estabilidade vem da regra de leitura: o worker ve banks por id via interface read-only, enquanto instalacao/mutacao de bank permanece fora do consumo renderizavel em voo.
|
||||
|
||||
### 6. Qual erro tipado minimo?
|
||||
|
||||
**Pergunta:** Qual erro tipado minimo precisamos no primeiro worker?
|
||||
|
||||
**Sugestao:** introduzir um erro pequeno, por exemplo `RenderWorkerError`, cobrindo pelo menos: backend unavailable, render failed, present/publish failed, shutdown timeout, stale submission discarded. Panics continuam sendo capturados como falha interna, mas nao devem ser o modelo operacional normal.
|
||||
|
||||
**Motivo:** `LocalRenderWorker` hoje prova policy, mas nao diferencia falhas reais de render/present. O worker real precisa reportar falhas sem contaminar a VM com detalhes de host.
|
||||
|
||||
### 7. Como provar que a VM nao bloqueia?
|
||||
|
||||
**Pergunta:** Como provar que a VM nao bloqueia quando o worker atrasa?
|
||||
|
||||
**Sugestao:** criar testes deterministas com backend fake controlado por barreiras/condvars, nao sleeps. O teste deve segurar o worker em rasterizacao, produzir frames adicionais no runtime, verificar substituicao latest-wins/drop telemetry, e provar que o tick da VM retorna sem esperar o consumo terminar.
|
||||
|
||||
**Motivo:** sleeps tornam teste de concorrencia fragil. Barreiras permitem provar especificamente a propriedade desejada: produtor nao bloqueia em raster/present lento.
|
||||
|
||||
### 8. Qual primeiro backend real?
|
||||
|
||||
**Pergunta:** Qual sera o primeiro backend real: desktop `Gfx`/pixels, framebuffer local, ou fake backend?
|
||||
|
||||
**Sugestao:** implementar primeiro um backend fake/mocked para contrato e um backend local framebuffer para integracao sem janela. A integracao desktop vem em seguida como consumidor concreto, usando a mesma trait. Ao final desta agenda, o worker deve estar funcionando completamente no caminho host real; a divisao em planos serve para controlar risco, nao para reduzir o alvo final.
|
||||
|
||||
**Motivo:** se o primeiro backend for desktop/winit, o contrato corre risco de nascer acoplado a detalhes de janela. Se o primeiro for fake/local, a semantica do worker fica testavel antes da integracao visual. Ainda assim, a agenda so deve ser considerada concluida quando a integracao host real tambem estiver operando sobre o worker.
|
||||
|
||||
## Criterio para Encerrar
|
||||
|
||||
Esta agenda pode virar decision quando tivermos:
|
||||
|
||||
- local/camada do worker controller;
|
||||
- estrutura thread-safe de handoff;
|
||||
- trait/backend de render/present;
|
||||
- present cadence/repeat;
|
||||
- shutdown/current work;
|
||||
- error model;
|
||||
- test matrix de concorrencia;
|
||||
- escopo do primeiro worker real.
|
||||
|
||||
## Discussion
|
||||
|
||||
- 2026-06-06: Agenda criada dentro da `DSC-0042` para separar worker real da preparacao de fronteira discutida em `AGD-0042`.
|
||||
- 2026-06-15: Apos a conclusao dos planos `PLN-0098` a `PLN-0109`, a direcao da agenda foi alinhada em torno da Opcao C: controller em `prometeu-system`, handoff single-slot thread-safe, backend fake/local primeiro, e publicacao de frame owned RGBA8888 antes da integracao desktop concreta.
|
||||
- 2026-06-15: Perguntas restantes alinhadas: `OwnedRgba8888Frame` deve viver em `prometeu-hal` com pixels `Vec<u32>` RGBA8888; banks sao acessados por interface compacta read-only via ids, sem copia/snapshot/`Arc` como contrato; shutdown deve ter timeout configuravel com default inicial sugerido de `250ms`; a agenda pode virar varios plans, mas so fecha quando o worker funcionar no host real.
|
||||
|
||||
## Resolution
|
||||
|
||||
Direcao proposta para decisao:
|
||||
|
||||
- `RenderManager` continua como autoridade de policy, ownership/epoch, latest-wins e telemetry;
|
||||
- o worker controller contratual deve viver em `prometeu-system`;
|
||||
- traits compartilhadas minimas podem viver em `prometeu-hal`;
|
||||
- o handoff real deve ser single-slot latest-wins com sincronizacao explicita, inicialmente `Mutex<Option<RenderSubmission>> + Condvar`;
|
||||
- o worker separa `pending` de `in_flight` e checa stop/ownership/epoch antes de publicar;
|
||||
- shutdown deve ser bounded e configuravel; default inicial sugerido: `250ms`;
|
||||
- o worker publica um `OwnedRgba8888Frame` em `prometeu-hal`, contendo frame id, owner/epoch, dimensoes, `stride_pixels` e pixels RGBA8888 owned em `Vec<u32>`;
|
||||
- o worker acessa banks por ids estaveis atraves de uma interface compacta read-only; nao copia banks, nao depende de snapshots de bank e nao usa `Arc` como parte obrigatoria do contrato;
|
||||
- o host event loop faz o upload/present nativo a partir do ultimo frame publicado;
|
||||
- o primeiro backend deve ser fake/local para provar contrato e concorrencia sem janela;
|
||||
- a integracao desktop concreta vem depois, usando a mesma trait/backend contract;
|
||||
- a agenda pode ser executada em varios plans, mas o criterio final e ter o worker funcionando completamente no host real;
|
||||
- testes devem usar barreiras/condvars para provar ausencia de bloqueio da VM, latest-wins, stale discard, shutdown bounded e telemetry.
|
||||
@ -1,198 +0,0 @@
|
||||
---
|
||||
id: DEC-0032
|
||||
ticket: real-render-worker-establishment
|
||||
title: Platform Layer and HardwareBridge Elimination
|
||||
status: accepted
|
||||
created: 2026-06-06
|
||||
accepted:
|
||||
agenda: AGD-0042
|
||||
plans: [PLN-0098, PLN-0099, PLN-0100, PLN-0101, PLN-0102, PLN-0103, PLN-0104, PLN-0105, PLN-0106, PLN-0107, PLN-0108, PLN-0109]
|
||||
tags: [runtime, platform, hardware, host, hal, renderer, architecture]
|
||||
---
|
||||
|
||||
## Status
|
||||
|
||||
Decision draft emitted from accepted agenda `AGD-0042`.
|
||||
|
||||
This decision is ready for review. Once accepted, it becomes the normative contract for plans that prepare Prometeu's platform layer before the real render worker work in `AGD-0043`.
|
||||
|
||||
## Contexto
|
||||
|
||||
`DEC-0031` established the VM/render boundary: closed render submissions, single-slot handoff, frame pacing, AppMode policy, ownership epoch, and render telemetry.
|
||||
|
||||
The current implementation still depends on `HardwareBridge`, a monolithic trait that exposes render, composer, `Gfx`, audio, input, touch, assets, frame begin, and presentation through one runtime-facing object. That bridge was useful as an early abstraction, but it is no longer sufficient for a high-quality handheld-style runtime.
|
||||
|
||||
The goal is no longer to make `HardwareBridge` the permanent host-implemented interface. The goal is to replace it with an explicit platform layer made of domain services/facades. Runtime code should depend on stable platform contracts; host/board code should provide concrete implementations.
|
||||
|
||||
This decision prepares the platform boundary. It does not implement the real render worker. Worker establishment remains scoped to `AGD-0043`.
|
||||
|
||||
## Decisao
|
||||
|
||||
Prometeu SHALL replace the monolithic `HardwareBridge` model with a platform layer composed of explicit domain services/facades.
|
||||
|
||||
`HardwareBridge` SHALL NOT survive as a final compatibility contract. It MAY exist only as temporary migration scaffold. The end state MUST remove `HardwareBridge` and all production/runtime dependencies on it.
|
||||
|
||||
The migration MAY begin with render, because render blocks the real worker path, but it MUST NOT stop there. The migration MUST cover every domain currently coupled through `HardwareBridge`: render, Game2D frame composition, audio, input/touch, assets/storage, clock/pacing, and telemetry where applicable.
|
||||
|
||||
The first implementation wave SHALL prepare render-worker-safe boundaries without introducing the real worker yet:
|
||||
|
||||
- introduce an owned `RenderSubmissionSink`;
|
||||
- introduce minimal typed render submission errors;
|
||||
- remove immediate framebuffer writes from `gfx2d` and `gfxui` syscalls;
|
||||
- move Game2D frame composition into a logical-side `Game2DFrameComposer` service/facade;
|
||||
- replace test fixtures centered on `Hardware::new()` with `TestPlatform`.
|
||||
|
||||
## Rationale
|
||||
|
||||
A handheld-quality runtime needs clear separation between platform policy and board/host implementation.
|
||||
|
||||
The runtime/OS owns:
|
||||
|
||||
- lifecycle and AppMode policy;
|
||||
- VM execution;
|
||||
- frame pacing;
|
||||
- render ownership and handoff;
|
||||
- logical frame composition;
|
||||
- command buffers;
|
||||
- resource visibility policy;
|
||||
- telemetry semantics.
|
||||
|
||||
The host/board implementation owns:
|
||||
|
||||
- window/surface or physical framebuffer;
|
||||
- concrete present implementation;
|
||||
- physical input events;
|
||||
- audio backend;
|
||||
- IO/storage backend;
|
||||
- host threads, cores, DMA, or coprocessor mapping.
|
||||
|
||||
A single `HardwareBridge` encourages accidental coupling. It lets VM syscalls, firmware, Hub, renderer, audio, input, assets, and present share the same mutable object. That model makes it too easy for future worker work to pass `&mut Hardware`, `&mut Gfx`, or live `FrameComposer` state across the render boundary.
|
||||
|
||||
Explicit facades make dependencies visible and testable. They also let each domain evolve with the lifecycle, threading, and error semantics it actually needs.
|
||||
|
||||
## Invariantes / Contrato
|
||||
|
||||
### 1. No final `HardwareBridge`
|
||||
|
||||
`HardwareBridge` MUST be removed from the final architecture of this migration.
|
||||
|
||||
During migration, code MAY use adapters or temporary accessors to bridge old and new APIs. Those adapters MUST be treated as implementation scaffolding, not as new public/runtime architecture.
|
||||
|
||||
`HardwareBridge` MUST NOT inherit the new facades as a way to preserve itself as a permanent aggregate contract.
|
||||
|
||||
### 2. Facades live at the right layer
|
||||
|
||||
Contracts/facades SHALL live in `prometeu-hal`.
|
||||
|
||||
Local/default and test implementations SHALL live in `prometeu-drivers` unless a domain has a stronger existing home.
|
||||
|
||||
Operational integration and ownership orchestration SHALL live in `prometeu-system` and host crates.
|
||||
|
||||
### 3. Render submission is owned
|
||||
|
||||
`RenderSubmissionSink` SHALL accept owned `RenderSubmission` values.
|
||||
|
||||
The owned contract is required because asynchronous handoff transfers ownership to a consumer. Local/synchronous paths MAY adapt internally, but the facade MUST be compatible with future worker handoff.
|
||||
|
||||
### 4. Render submission errors are typed
|
||||
|
||||
The preparation work SHALL introduce a minimal typed error surface for submission/render publication.
|
||||
|
||||
The API MUST NOT be born infallible if the next worker stage will immediately need typed failure. Panic containment may remain as a safety net, but normal backend failure should have an explicit result type.
|
||||
|
||||
### 5. `gfx2d` and `gfxui` syscalls buffer commands only
|
||||
|
||||
`gfx2d.*` and `gfxui.*` syscalls MUST NOT mutate the framebuffer immediately.
|
||||
|
||||
Those syscalls SHALL record commands into the appropriate logical command buffer. Pixels change only when a closed render submission is consumed/published by the render path.
|
||||
|
||||
The first regression tests MUST include `gfx2d.clear` and `gfx2d.draw_text` proving that syscalls buffer commands and presentation happens only after frame closure/publication.
|
||||
|
||||
### 6. Game2D composition is logical-side state
|
||||
|
||||
`bind_scene`, `set_camera`, `emit_sprite`, and `close_game2d_packet` are logical frame preparation, not physical backend behavior.
|
||||
|
||||
The migration SHALL introduce `Game2DFrameComposer` as the logical-side service/facade for Game2D frame composition. Its implementation may move in phases, but it MUST NOT remain conceptually part of a physical render backend.
|
||||
|
||||
### 7. Tests use `TestPlatform`
|
||||
|
||||
Tests that need a full local platform fixture SHALL migrate toward `TestPlatform`.
|
||||
|
||||
`TestPlatform` SHOULD keep tests ergonomic while making dependencies explicit through the new facades. It MUST NOT preserve `HardwareBridge` as hidden compatibility.
|
||||
|
||||
### 8. Render may migrate first, but the migration is complete only when every domain is moved
|
||||
|
||||
The render path MAY be migrated first.
|
||||
|
||||
The architecture is not complete until audio, input/touch, assets/storage, clock/pacing, telemetry where applicable, firmware, Hub, runtime, host desktop, and tests no longer depend on `HardwareBridge`.
|
||||
|
||||
## Impactos
|
||||
|
||||
### Spec
|
||||
|
||||
Specs need to describe the platform layer as explicit services/facades rather than a monolithic hardware bridge.
|
||||
|
||||
Render specifications must state that `gfx2d`/`gfxui` syscalls buffer commands and that framebuffer mutation occurs through render submission consumption/publication.
|
||||
|
||||
### Runtime
|
||||
|
||||
Runtime code must stop depending on `&mut dyn HardwareBridge`.
|
||||
|
||||
VM dispatch must stop using `hw.gfx_mut()` for immediate drawing. Runtime frame closure must depend on logical services such as `Game2DFrameComposer` and publication facades such as `RenderSubmissionSink`.
|
||||
|
||||
### HAL
|
||||
|
||||
`prometeu-hal` must define the stable contracts for the platform layer.
|
||||
|
||||
At minimum, the first wave needs `RenderSubmissionSink` and its typed error. Later waves should define or refine composer, audio, input/touch, assets/storage, clock/pacing, and telemetry facades.
|
||||
|
||||
### Drivers
|
||||
|
||||
`prometeu-drivers` should provide local/default implementations and `TestPlatform`.
|
||||
|
||||
The existing `prometeu_drivers::Hardware` may be used as a migration source, but it must not remain the final runtime-facing architecture.
|
||||
|
||||
### Host
|
||||
|
||||
Host crates must move from directly owning and exposing `Hardware` as the runtime's universal bridge toward implementing platform services.
|
||||
|
||||
The desktop host may remain the first concrete integration, but the contracts must remain portable to handheld board support, emulator, web, or coprocessor/DMA implementations.
|
||||
|
||||
### Firmware / Hub
|
||||
|
||||
Firmware and Hub must migrate away from `publish_render_submission` on `HardwareBridge` and toward explicit render submission publication/platform services.
|
||||
|
||||
### Tests
|
||||
|
||||
Tests must migrate from direct `Hardware::new()` dependence to `TestPlatform` or narrower facade-specific fixtures.
|
||||
|
||||
Tests must be updated where they assume immediate framebuffer mutation from `gfx2d`/`gfxui` syscalls.
|
||||
|
||||
## Referencias
|
||||
|
||||
- `AGD-0042`: Host Hardware and Render Boundary Preparation.
|
||||
- `AGD-0043`: Real Render Worker Establishment.
|
||||
- `DEC-0031`: VM and Render Parallel Execution Boundary.
|
||||
- `LSN-0048`: Render Workers Need a Closed Packet Contract.
|
||||
|
||||
## Propagacao Necessaria
|
||||
|
||||
Create implementation plans before editing specs or code.
|
||||
|
||||
Recommended plan sequence:
|
||||
|
||||
1. introduce `RenderSubmissionSink` and minimal typed submit error;
|
||||
2. migrate local render publication away from `HardwareBridge`;
|
||||
3. remove immediate framebuffer writes from `gfx2d`/`gfxui` syscalls and update tests;
|
||||
4. introduce `Game2DFrameComposer` as logical-side service/facade;
|
||||
5. introduce `RuntimePlatform`/`Platform` and `TestPlatform`;
|
||||
6. migrate firmware, Hub, runtime, host desktop, and tests to explicit facades;
|
||||
7. migrate remaining domains: audio, input/touch, assets/storage, clock/pacing, telemetry as needed;
|
||||
8. remove `HardwareBridge`;
|
||||
9. update specs and lessons after implementation publishes the new structure.
|
||||
|
||||
`AGD-0043` SHOULD NOT move to implementation until this decision has produced enough platform boundary work that a render worker can be built without `&mut Hardware`, `&mut Gfx`, live `FrameComposer`, or mutable VM state crossing into the worker.
|
||||
|
||||
## Revision Log
|
||||
|
||||
- 2026-06-06: Initial decision draft generated from accepted `AGD-0042`.
|
||||
@ -1,296 +0,0 @@
|
||||
---
|
||||
id: DEC-0033
|
||||
ticket: real-render-worker-establishment
|
||||
title: Real Render Worker Contract
|
||||
status: accepted
|
||||
created: 2026-06-15
|
||||
accepted:
|
||||
agenda: AGD-0043
|
||||
plans: [PLN-0110, PLN-0111, PLN-0112, PLN-0113, PLN-0114, PLN-0115, PLN-0116, PLN-0117, PLN-0118, PLN-0119, PLN-0120, PLN-0121]
|
||||
tags: [runtime, renderer, worker, concurrency, host, hal, architecture]
|
||||
---
|
||||
|
||||
## Status
|
||||
|
||||
Decision accepted from agenda `AGD-0043`.
|
||||
|
||||
This is the normative contract for implementation plans that establish the real render worker for Prometeu.
|
||||
|
||||
## Contexto
|
||||
|
||||
`DEC-0031` established the VM/render boundary around closed render submissions, single-slot handoff, frame pacing, AppMode policy, ownership epoch, and render telemetry.
|
||||
|
||||
`DEC-0032` and plans `PLN-0098` through `PLN-0109` prepared the platform boundary required for a real worker:
|
||||
|
||||
- `HardwareBridge` was removed from production/runtime contracts;
|
||||
- `RuntimePlatform` became the runtime-facing platform service boundary;
|
||||
- `RenderSubmissionSink` accepts owned render submissions;
|
||||
- `Game2DFrameComposer` separates logical Game 2D frame closure from backend rendering;
|
||||
- `gfx2d.*` and `gfxui.*` syscalls buffer commands instead of mutating `Gfx` immediately;
|
||||
- firmware, Hub, runtime, host desktop, and tests moved to platform services;
|
||||
- specs now state that worker/render consumers must not depend on `&mut Hardware`, `&mut Gfx`, live `FrameComposer`, or mutable VM state.
|
||||
|
||||
The remaining work is to replace the cooperative/local `LocalRenderWorker` proof with a real worker that drains render submissions without blocking VM progress and publishes complete RGBA8888 frames for host presentation.
|
||||
|
||||
## Decisao
|
||||
|
||||
Prometeu SHALL implement the real render worker as a runtime-owned worker controller with host-provided backend integration.
|
||||
|
||||
The worker controller SHALL live in `prometeu-system`, near `RenderManager`, because `RenderManager` owns AppMode policy, ownership/epoch, latest-wins handoff semantics, and render telemetry.
|
||||
|
||||
Shared contract types and traits SHALL live in `prometeu-hal` when they cross runtime/host/driver boundaries. Local/default and test implementations SHALL live in `prometeu-drivers` or host crates as appropriate.
|
||||
|
||||
The worker SHALL consume owned `RenderSubmission` values through a thread-safe single-slot latest-wins handoff. The VM producer MUST NOT block on rasterization, publication, presentation, or worker completion.
|
||||
|
||||
The worker SHALL publish owned RGBA8888 frames, not native window/swapchain objects. The initial shared frame type SHALL be `OwnedRgba8888Frame` in `prometeu-hal`.
|
||||
|
||||
The host event loop SHALL remain responsible for native window upload/present. The worker MUST NOT depend on winit, pixels, SDL, swapchain handles, native textures, or host window APIs.
|
||||
|
||||
The agenda may be implemented through multiple plans, but `AGD-0043` is complete only when the worker functions on the real host path, not merely in fake/local tests.
|
||||
|
||||
## Rationale
|
||||
|
||||
The real worker must prove the architecture promised by `DEC-0031`: VM execution and render consumption can proceed independently without crossing mutable runtime state into the renderer.
|
||||
|
||||
Putting the controller in `prometeu-system` keeps render policy in the same layer that already owns frame lifecycle, ownership epochs, latest-wins behavior, and telemetry. Putting it only in the host would make the contract desktop-specific and harder to test without a window.
|
||||
|
||||
Publishing an owned RGBA8888 frame keeps the worker host-agnostic. The host can upload that frame to any native presentation backend while the worker stays testable through ordinary memory comparisons.
|
||||
|
||||
Using a single explicit pending slot directly models the accepted backpressure rule. Channels or queues can accidentally preserve history or block producers. The runtime contract is not "render every produced frame"; it is "the latest complete submission wins and the VM does not wait for the consumer."
|
||||
|
||||
Read-only bank access by id preserves the no-copy resource boundary. Banks are render resources, not payloads to duplicate into each submission. The worker should resolve resource ids through a compact read-only interface without taking `&mut Hardware`, `&mut Gfx`, a live `FrameComposer`, or VM state.
|
||||
|
||||
## Invariantes / Contrato
|
||||
|
||||
### 1. Runtime controller ownership
|
||||
|
||||
The worker controller MUST live in `prometeu-system`.
|
||||
|
||||
`RenderManager` SHALL remain the authority for:
|
||||
|
||||
- AppMode render policy;
|
||||
- ownership/epoch;
|
||||
- latest-wins handoff semantics;
|
||||
- render telemetry;
|
||||
- stale submission discard;
|
||||
- shutdown/request-stop coordination visible to runtime policy.
|
||||
|
||||
Host crates MAY instantiate or wire the controller, but they MUST NOT redefine worker semantics.
|
||||
|
||||
### 2. Shared HAL contracts
|
||||
|
||||
`prometeu-hal` SHALL define shared types/traits that cross the runtime/host/driver boundary.
|
||||
|
||||
At minimum, the worker work SHALL introduce:
|
||||
|
||||
- `OwnedRgba8888Frame`;
|
||||
- compact read-only render resource access contracts for bank lookup by id;
|
||||
- typed worker/backend error contracts where they are shared across crates.
|
||||
|
||||
### 3. Owned RGBA8888 frame publication
|
||||
|
||||
`OwnedRgba8888Frame` SHALL represent a completed logical frame ready for host upload/present.
|
||||
|
||||
It MUST include:
|
||||
|
||||
- `FrameId`;
|
||||
- render ownership/epoch metadata;
|
||||
- width;
|
||||
- height;
|
||||
- `stride_pixels`;
|
||||
- owned RGBA8888 pixel data as `Vec<u32>`.
|
||||
|
||||
The pixel data MUST use the canonical RGBA8888 raw value contract already used by the runtime specs. It MUST NOT be described as native-endian host pixels, native texture data, or backend-specific surface memory.
|
||||
|
||||
Repeating the last valid frame SHALL mean reusing the last published `OwnedRgba8888Frame`, not recomposing from VM state or live composer state.
|
||||
|
||||
### 4. Thread-safe latest-wins handoff
|
||||
|
||||
The real handoff SHALL be a thread-safe single-slot latest-wins structure.
|
||||
|
||||
The initial implementation SHOULD use an explicit slot based on `Mutex<Option<RenderSubmission>> + Condvar`.
|
||||
|
||||
Producer behavior:
|
||||
|
||||
- publish replaces the pending submission if one already exists;
|
||||
- replacement increments drop/replacement telemetry;
|
||||
- publish MUST NOT wait for rasterization or present;
|
||||
- publish MUST wake the consumer when needed.
|
||||
|
||||
Consumer behavior:
|
||||
|
||||
- wait for pending submission or shutdown;
|
||||
- take ownership of the latest pending submission;
|
||||
- leave the pending slot empty while work is in flight;
|
||||
- discard stale work before publication if ownership/epoch no longer matches.
|
||||
|
||||
### 5. Pending and in-flight are separate
|
||||
|
||||
The worker MUST distinguish pending submissions from in-flight work.
|
||||
|
||||
Once the worker takes a submission, that submission is in flight. New producer submissions may replace the pending slot while the worker is still rasterizing the previous in-flight submission.
|
||||
|
||||
Before publishing an in-flight frame, the worker MUST check:
|
||||
|
||||
- stop/shutdown state;
|
||||
- render owner;
|
||||
- render epoch/generation.
|
||||
|
||||
If the frame is stale, the worker MUST discard it and update telemetry. It MUST NOT publish stale pixels.
|
||||
|
||||
### 6. Shutdown is bounded
|
||||
|
||||
Worker shutdown MUST be bounded and observable.
|
||||
|
||||
The shutdown timeout MUST be configurable. The initial default SHOULD be `250ms`.
|
||||
|
||||
On shutdown:
|
||||
|
||||
- stop state MUST be signaled;
|
||||
- the worker MUST be woken if it is waiting;
|
||||
- join MUST complete within the configured bound or return/report a `ShutdownTimeout` failure;
|
||||
- in-flight work MAY finish rasterization, but MUST NOT publish after stop/epoch invalidation says it is stale.
|
||||
|
||||
### 7. Read-only bank access by id
|
||||
|
||||
The render backend SHALL resolve render resources through a compact read-only interface by stable ids.
|
||||
|
||||
Banks are read-only by definition during render consumption. The worker MUST NOT copy entire banks into submissions. The worker MUST NOT require bank snapshots. The worker MUST NOT require `Arc` as part of the public contract.
|
||||
|
||||
Implementation may use any internal storage strategy that satisfies the public contract, but the render worker contract is ids plus read-only access.
|
||||
|
||||
Resource installation, mutation, and residency changes remain outside in-flight render consumption.
|
||||
|
||||
### 8. Backend and host presentation split
|
||||
|
||||
The worker backend SHALL consume `RenderSubmission` plus read-only resource access and produce `OwnedRgba8888Frame`.
|
||||
|
||||
The host event loop SHALL upload/present the latest published frame through its native API.
|
||||
|
||||
The worker MUST NOT call native window present directly unless a future decision revises the host presentation model.
|
||||
|
||||
### 9. Typed error model
|
||||
|
||||
The worker work SHALL introduce a minimal typed error model, such as `RenderWorkerError`.
|
||||
|
||||
The error model MUST cover at least:
|
||||
|
||||
- backend unavailable;
|
||||
- render failed;
|
||||
- publish/present handoff failed;
|
||||
- shutdown timeout;
|
||||
- stale submission discarded;
|
||||
- worker panic captured as internal failure.
|
||||
|
||||
Panic containment MAY remain as a safety net. Ordinary worker/backend failure MUST NOT rely on panic as the normal reporting path.
|
||||
|
||||
### 10. Telemetry
|
||||
|
||||
Worker telemetry SHALL preserve and extend the existing render telemetry model.
|
||||
|
||||
At minimum, telemetry SHOULD include:
|
||||
|
||||
- submissions produced;
|
||||
- pending submissions replaced/dropped;
|
||||
- submissions consumed;
|
||||
- frames published/presented;
|
||||
- stale submissions discarded;
|
||||
- render failures;
|
||||
- repeated-frame count;
|
||||
- shutdown timeout count.
|
||||
|
||||
Telemetry MUST NOT change VM semantics.
|
||||
|
||||
### 11. Test contract
|
||||
|
||||
The worker contract MUST be proven without a native window.
|
||||
|
||||
Tests SHALL use deterministic synchronization primitives such as barriers and condvars, not timing sleeps as the primary proof mechanism.
|
||||
|
||||
The test matrix MUST prove:
|
||||
|
||||
- VM producer does not block when worker rasterization is held;
|
||||
- latest-wins replacement occurs while a frame is in flight;
|
||||
- stale owner/epoch prevents publication;
|
||||
- shutdown is bounded;
|
||||
- typed errors and telemetry are emitted;
|
||||
- repeat uses the last published `OwnedRgba8888Frame`;
|
||||
- fake/local backend behavior matches the contract before desktop integration.
|
||||
|
||||
### 12. Final scope
|
||||
|
||||
The implementation MAY be split into multiple plans.
|
||||
|
||||
However, the agenda is not complete until the real host path uses the worker. A fake/local backend is required for contract tests, but it is not sufficient as the final state of `AGD-0043`.
|
||||
|
||||
## Impactos
|
||||
|
||||
### Spec
|
||||
|
||||
Runtime specs must document:
|
||||
|
||||
- `OwnedRgba8888Frame`;
|
||||
- the thread-safe single-slot latest-wins worker handoff;
|
||||
- the worker/host presentation split;
|
||||
- read-only bank access by id;
|
||||
- shutdown and typed error behavior;
|
||||
- telemetry expectations.
|
||||
|
||||
### Runtime
|
||||
|
||||
`prometeu-system` must add the worker controller and integrate it with `RenderManager`.
|
||||
|
||||
The VM tick path must submit render work without blocking on worker consumption.
|
||||
|
||||
### HAL
|
||||
|
||||
`prometeu-hal` must expose shared worker contracts:
|
||||
|
||||
- owned RGBA8888 frame type;
|
||||
- render resource read-only access traits;
|
||||
- worker/backend error types as needed.
|
||||
|
||||
### Drivers
|
||||
|
||||
`prometeu-drivers` must provide fake/local implementations suitable for deterministic tests and non-window integration.
|
||||
|
||||
Concrete local rendering may continue to use existing GFX machinery internally, but must expose the worker-facing contract without leaking `&mut Hardware`, `&mut Gfx`, or live `FrameComposer`.
|
||||
|
||||
### Host
|
||||
|
||||
The desktop host must integrate the real worker path and upload/present the latest published `OwnedRgba8888Frame` through its native window backend.
|
||||
|
||||
The host owns native presentation; the worker owns render consumption and frame publication.
|
||||
|
||||
### Tests
|
||||
|
||||
Tests must cover concurrency behavior deterministically and must include final host-path integration evidence.
|
||||
|
||||
## Referencias
|
||||
|
||||
- `AGD-0043`: Real Render Worker Establishment.
|
||||
- `AGD-0042`: Host Hardware and Render Boundary Preparation.
|
||||
- `DEC-0031`: VM and Render Parallel Execution Boundary.
|
||||
- `DEC-0032`: Platform Layer and HardwareBridge Elimination.
|
||||
- `LSN-0048`: Render Workers Need a Closed Packet Contract.
|
||||
- `docs/specs/runtime/04-gfx-peripheral.md`.
|
||||
- `docs/specs/runtime/11-portability-and-cross-platform-execution.md`.
|
||||
|
||||
## Propagacao Necessaria
|
||||
|
||||
Create implementation plans before editing specs or code.
|
||||
|
||||
Recommended plan sequence:
|
||||
|
||||
1. define `OwnedRgba8888Frame`, read-only bank access contracts, and worker error types in HAL;
|
||||
2. introduce the thread-safe latest-wins handoff and deterministic fake/local worker tests;
|
||||
3. implement worker controller lifecycle, stop token, bounded shutdown, pending/in-flight separation, and telemetry in `prometeu-system`;
|
||||
4. implement fake/local backend that consumes `RenderSubmission` and produces `OwnedRgba8888Frame`;
|
||||
5. integrate runtime render submission path with the worker without blocking VM ticks;
|
||||
6. add stale owner/epoch discard and repeat-last-published-frame behavior;
|
||||
7. integrate desktop host upload/present from the latest published `OwnedRgba8888Frame`;
|
||||
8. update specs and final validation tests.
|
||||
|
||||
## Revision Log
|
||||
|
||||
- 2026-06-15: Initial decision draft generated from accepted `AGD-0043`.
|
||||
@ -1,81 +0,0 @@
|
||||
---
|
||||
id: PLN-0098
|
||||
ticket: real-render-worker-establishment
|
||||
title: Define Platform Facade Contracts
|
||||
status: done
|
||||
created: 2026-06-06
|
||||
completed:
|
||||
ref_decisions: [DEC-0032]
|
||||
tags: [runtime, platform, hal, facades]
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
Define the first `prometeu-hal` platform facade contracts that will replace the monolithic `HardwareBridge` model.
|
||||
|
||||
## Background
|
||||
|
||||
`DEC-0032` requires Prometeu to move from one runtime-facing `HardwareBridge` to explicit platform services/facades. This first plan creates the stable contract surface without migrating callers yet.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- Add facade modules/types in `crates/console/prometeu-hal`.
|
||||
- Define names and module ownership for `RuntimePlatform`, `RenderSubmissionSink`, `RenderBackend`, `Game2DFrameComposer`, and `TestPlatform` integration points.
|
||||
- Add minimal documentation explaining that `HardwareBridge` is legacy migration scaffold.
|
||||
|
||||
### Excluded
|
||||
|
||||
- Do not remove `HardwareBridge`.
|
||||
- Do not migrate syscalls, firmware, Hub, host, or tests yet.
|
||||
- Do not implement the real render worker.
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1 - Add platform facade module
|
||||
|
||||
**What:** Create a `platform` module in `prometeu-hal`.
|
||||
**How:** Add facade trait declarations and re-exports without changing existing call sites.
|
||||
**File(s):** `crates/console/prometeu-hal/src/platform.rs`, `crates/console/prometeu-hal/src/lib.rs`.
|
||||
|
||||
### Step 2 - Declare domain facade boundaries
|
||||
|
||||
**What:** Define initial placeholder traits for render submission, render backend, Game2D composer, audio, input/touch, assets/storage, clock/pacing, and telemetry.
|
||||
**How:** Keep non-render traits minimal or marker-like when detailed APIs are not yet migrated.
|
||||
**File(s):** `crates/console/prometeu-hal/src/platform.rs`.
|
||||
|
||||
### Step 3 - Document migration status
|
||||
|
||||
**What:** Mark `HardwareBridge` as legacy scaffold.
|
||||
**How:** Add module-level comments stating it must be removed by later plans.
|
||||
**File(s):** `crates/console/prometeu-hal/src/hardware_bridge.rs`.
|
||||
|
||||
## Test Requirements
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- Compile-only tests or trait object assertions where useful.
|
||||
|
||||
### Integration Tests
|
||||
|
||||
- Existing HAL/system/driver tests must continue to pass.
|
||||
|
||||
### Manual Verification
|
||||
|
||||
- Confirm `HardwareBridge` has no new dependencies on the platform facades.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Platform facade module exists in `prometeu-hal`.
|
||||
- [ ] `HardwareBridge` is explicitly documented as migration scaffold.
|
||||
- [ ] No runtime behavior changes.
|
||||
- [ ] Existing tests pass.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `DEC-0032`.
|
||||
|
||||
## Risks
|
||||
|
||||
- Introducing too much API too early can freeze poor names. Keep this plan narrow.
|
||||
@ -1,82 +0,0 @@
|
||||
---
|
||||
id: PLN-0099
|
||||
ticket: real-render-worker-establishment
|
||||
title: Introduce Owned RenderSubmissionSink
|
||||
status: done
|
||||
created: 2026-06-06
|
||||
completed:
|
||||
ref_decisions: [DEC-0032]
|
||||
tags: [runtime, renderer, hal, errors]
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
Introduce an owned `RenderSubmissionSink` facade with minimal typed submission errors.
|
||||
|
||||
## Background
|
||||
|
||||
`DEC-0032` requires render publication to move away from `HardwareBridge` and to accept owned `RenderSubmission` values so the contract is compatible with async handoff.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- Define `RenderSubmissionSink`.
|
||||
- Define minimal `RenderSubmitError`.
|
||||
- Implement the facade for the current local driver path.
|
||||
|
||||
### Excluded
|
||||
|
||||
- Do not migrate all publication call sites yet.
|
||||
- Do not spawn a render worker.
|
||||
- Do not remove `HardwareBridge::publish_render_submission`.
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1 - Define sink and error
|
||||
|
||||
**What:** Add `RenderSubmissionSink` and `RenderSubmitError`.
|
||||
**How:** `submit_render_submission(&mut self, submission: RenderSubmission) -> Result<(), RenderSubmitError>`.
|
||||
**File(s):** `crates/console/prometeu-hal/src/platform.rs` or a render-specific HAL module.
|
||||
|
||||
### Step 2 - Implement local sink
|
||||
|
||||
**What:** Implement the sink for the current local driver implementation.
|
||||
**How:** Adapt owned submission to the existing local `Hardware` publication path.
|
||||
**File(s):** `crates/console/prometeu-drivers/src/hardware.rs`.
|
||||
|
||||
### Step 3 - Add tests
|
||||
|
||||
**What:** Prove owned submission is consumed by the local sink.
|
||||
**How:** Add driver test using `RenderSubmission::game2d` and `RenderSubmission::shell_ui`.
|
||||
**File(s):** `crates/console/prometeu-drivers/src/hardware.rs` or platform test module.
|
||||
|
||||
## Test Requirements
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- Local sink accepts owned Game2D and ShellUi submissions.
|
||||
- Error type is constructible and debuggable.
|
||||
|
||||
### Integration Tests
|
||||
|
||||
- `cargo test -p prometeu-hal -p prometeu-drivers`.
|
||||
|
||||
### Manual Verification
|
||||
|
||||
- Confirm the sink does not take `&RenderSubmission`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `RenderSubmissionSink` takes owned `RenderSubmission`.
|
||||
- [ ] Minimal typed error exists.
|
||||
- [ ] Local driver implements the sink.
|
||||
- [ ] Existing publication behavior remains intact.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `PLN-0098`.
|
||||
|
||||
## Risks
|
||||
|
||||
- Local paths may still need temporary borrow adaptation. Keep it internal to the implementation.
|
||||
@ -1,80 +0,0 @@
|
||||
---
|
||||
id: PLN-0100
|
||||
ticket: real-render-worker-establishment
|
||||
title: Migrate Local Render Publication to RenderSubmissionSink
|
||||
status: done
|
||||
created: 2026-06-06
|
||||
completed:
|
||||
ref_decisions: [DEC-0032]
|
||||
tags: [runtime, renderer, platform]
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
Move local render publication call sites from `HardwareBridge::publish_render_submission` to `RenderSubmissionSink`.
|
||||
|
||||
## Background
|
||||
|
||||
The sink must become the publication boundary before a real worker can replace the local path. Runtime, firmware, and Hub currently publish through the monolithic bridge.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- Runtime local render surface uses `RenderSubmissionSink`.
|
||||
- Firmware splash/crash publication uses the sink.
|
||||
- Hub Shell UI publication uses the sink or a temporary adapter.
|
||||
|
||||
### Excluded
|
||||
|
||||
- Do not remove `HardwareBridge`.
|
||||
- Do not migrate non-render domains.
|
||||
- Do not change command buffering semantics yet.
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1 - Adapt runtime tick publication
|
||||
|
||||
**What:** Replace `HardwareRenderSurface` dependency on `HardwareBridge::publish_render_submission`.
|
||||
**How:** Route submission through `RenderSubmissionSink`.
|
||||
**File(s):** `crates/console/prometeu-system/src/services/vm_runtime/tick.rs`.
|
||||
|
||||
### Step 2 - Adapt firmware and Hub publication
|
||||
|
||||
**What:** Stop direct calls to `hw.publish_render_submission`.
|
||||
**How:** Use the sink through explicit platform/render facade access or temporary adapter.
|
||||
**File(s):** `crates/console/prometeu-firmware/src/firmware_step_splash_screen.rs`, `crates/console/prometeu-firmware/src/firmware_step_crash_screen.rs`, `crates/console/prometeu-system/src/programs/prometeu_hub/prometeu_hub.rs`.
|
||||
|
||||
### Step 3 - Update tests
|
||||
|
||||
**What:** Adjust tests that call `HardwareBridge::publish_render_submission`.
|
||||
**How:** Use `RenderSubmissionSink` where render publication is the target.
|
||||
**File(s):** `crates/console/prometeu-system/src/services/vm_runtime/tests.rs`, driver tests.
|
||||
|
||||
## Test Requirements
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- Existing render manager tests pass.
|
||||
|
||||
### Integration Tests
|
||||
|
||||
- `cargo test -p prometeu-system -p prometeu-firmware -p prometeu-drivers`.
|
||||
|
||||
### Manual Verification
|
||||
|
||||
- `rg "publish_render_submission" crates/console` shows only legacy implementation or intentionally unmigrated scaffold.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Runtime publication uses `RenderSubmissionSink`.
|
||||
- [ ] Firmware/Hub publication no longer depend directly on the monolithic bridge.
|
||||
- [ ] Tests pass.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `PLN-0099`.
|
||||
|
||||
## Risks
|
||||
|
||||
- Firmware code may need a small context shape change. Keep compatibility adapters temporary and explicit.
|
||||
@ -1,82 +0,0 @@
|
||||
---
|
||||
id: PLN-0101
|
||||
ticket: real-render-worker-establishment
|
||||
title: Remove Immediate Gfx2D and GfxUI Syscall Rendering
|
||||
status: done
|
||||
created: 2026-06-06
|
||||
completed:
|
||||
ref_decisions: [DEC-0032]
|
||||
tags: [runtime, gfx, syscalls, renderer]
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
Make `gfx2d.*` and `gfxui.*` syscalls buffer commands only, without mutating the framebuffer immediately.
|
||||
|
||||
## Background
|
||||
|
||||
`DEC-0032` requires framebuffer mutation to occur through render submission consumption/publication, not inside VM syscall dispatch. This is required before render can run independently.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- Remove `hw.gfx_mut().*` immediate draw calls from VM dispatch for `gfx2d` and `gfxui`.
|
||||
- Add regression tests for `gfx2d.clear` and `gfx2d.draw_text`.
|
||||
- Update tests that assumed immediate pixel mutation.
|
||||
|
||||
### Excluded
|
||||
|
||||
- Do not remove `GfxBridge`.
|
||||
- Do not migrate composer in this plan.
|
||||
- Do not implement worker.
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1 - Remove immediate Game gfx writes
|
||||
|
||||
**What:** Delete direct `hw.gfx_mut()` rendering in `Gfx*` syscall handlers.
|
||||
**How:** Keep command buffering into `gfx2d_commands`.
|
||||
**File(s):** `crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs`.
|
||||
|
||||
### Step 2 - Remove immediate Shell UI writes
|
||||
|
||||
**What:** Delete direct `hw.gfx_mut()` rendering in `GfxUi*` syscall handlers.
|
||||
**How:** Keep command buffering into `gfxui_commands`.
|
||||
**File(s):** `crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs`.
|
||||
|
||||
### Step 3 - Add regression tests
|
||||
|
||||
**What:** Prove syscalls do not affect pixels until publication.
|
||||
**How:** Tests for `gfx2d.clear` and `gfx2d.draw_text` should inspect command buffers and frame publication.
|
||||
**File(s):** `crates/console/prometeu-system/src/services/vm_runtime/tests.rs`.
|
||||
|
||||
## Test Requirements
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- `gfx2d.clear` buffers only.
|
||||
- `gfx2d.draw_text` buffers only.
|
||||
- Shell UI commands buffer only.
|
||||
|
||||
### Integration Tests
|
||||
|
||||
- Stress cartridge still renders after frame publication.
|
||||
|
||||
### Manual Verification
|
||||
|
||||
- `rg "gfx_mut\\(\\).*draw|gfx_mut\\(\\).*clear|gfx_mut\\(\\).*fill" dispatch.rs` returns no immediate render calls.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Gfx syscalls no longer mutate framebuffer immediately.
|
||||
- [ ] Pixels change only after render submission publication.
|
||||
- [ ] Tests updated to the new contract.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `PLN-0100`.
|
||||
|
||||
## Risks
|
||||
|
||||
- Debug workflows may have relied on immediate drawing. Tests must make the new frame boundary explicit.
|
||||
@ -1,82 +0,0 @@
|
||||
---
|
||||
id: PLN-0102
|
||||
ticket: real-render-worker-establishment
|
||||
title: Introduce Game2DFrameComposer Logical Service
|
||||
status: done
|
||||
created: 2026-06-06
|
||||
completed:
|
||||
ref_decisions: [DEC-0032]
|
||||
tags: [runtime, composer, game2d, platform]
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
Introduce `Game2DFrameComposer` as the logical-side service/facade for Game2D frame preparation.
|
||||
|
||||
## Background
|
||||
|
||||
`DEC-0032` states that `bind_scene`, `set_camera`, `emit_sprite`, and `close_game2d_packet` are logical frame preparation, not physical backend behavior.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- Define a `Game2DFrameComposer` facade.
|
||||
- Move or wrap existing `FrameComposer` behavior under logical-side naming.
|
||||
- Migrate composer syscalls to use the facade rather than generic hardware.
|
||||
|
||||
### Excluded
|
||||
|
||||
- Do not rewrite scene cache internals.
|
||||
- Do not move viewport cache across threads.
|
||||
- Do not implement worker.
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1 - Define composer facade
|
||||
|
||||
**What:** Add `Game2DFrameComposer` trait or service contract.
|
||||
**How:** Include `begin_frame`, `bind_scene`, `unbind_scene`, `set_camera`, `emit_sprite`, `close_game2d_packet`, and required read checks.
|
||||
**File(s):** `crates/console/prometeu-hal/src/platform.rs` or composer-specific HAL module.
|
||||
|
||||
### Step 2 - Implement using current composer
|
||||
|
||||
**What:** Make current local driver composer satisfy the new logical service.
|
||||
**How:** Reuse `FrameComposer` implementation under adapter/implementation.
|
||||
**File(s):** `crates/console/prometeu-drivers/src/frame_composer.rs`, `crates/console/prometeu-drivers/src/hardware.rs`.
|
||||
|
||||
### Step 3 - Migrate composer syscalls
|
||||
|
||||
**What:** Dispatch composer syscalls through `Game2DFrameComposer`.
|
||||
**How:** Remove dependency on `HardwareBridge` methods for composer.
|
||||
**File(s):** `crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs`.
|
||||
|
||||
## Test Requirements
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- Composer facade closes packets equivalent to previous `FrameComposer`.
|
||||
- Invalid scene/bank behavior remains unchanged.
|
||||
|
||||
### Integration Tests
|
||||
|
||||
- Existing scene/composer runtime tests pass.
|
||||
|
||||
### Manual Verification
|
||||
|
||||
- Composer syscalls no longer require `&mut dyn HardwareBridge`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `Game2DFrameComposer` exists as logical service/facade.
|
||||
- [ ] Composer syscalls route through it.
|
||||
- [ ] Existing Game2D scene rendering remains correct.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `PLN-0098`.
|
||||
- `PLN-0101`.
|
||||
|
||||
## Risks
|
||||
|
||||
- Scene cache ownership can be accidentally moved too early. Keep cache behavior local while changing the boundary.
|
||||
@ -1,82 +0,0 @@
|
||||
---
|
||||
id: PLN-0103
|
||||
ticket: real-render-worker-establishment
|
||||
title: Introduce RuntimePlatform and TestPlatform
|
||||
status: done
|
||||
created: 2026-06-06
|
||||
completed:
|
||||
ref_decisions: [DEC-0032]
|
||||
tags: [runtime, platform, tests]
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
Introduce `RuntimePlatform`/`Platform` as the aggregate of explicit platform services, and `TestPlatform` as the default test fixture.
|
||||
|
||||
## Background
|
||||
|
||||
`DEC-0032` requires tests and runtime integration to depend on explicit facades rather than `Hardware::new()` and `HardwareBridge`.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- Define aggregate platform access shape.
|
||||
- Add `TestPlatform` in `prometeu-drivers`.
|
||||
- Start migrating representative VM runtime tests.
|
||||
|
||||
### Excluded
|
||||
|
||||
- Do not migrate all tests in this plan.
|
||||
- Do not remove `HardwareBridge`.
|
||||
- Do not migrate all host code.
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1 - Define platform aggregate
|
||||
|
||||
**What:** Add `RuntimePlatform` or `Platform` aggregate facade.
|
||||
**How:** Expose accessors for render sink, Game2D composer, audio, input/touch, assets/storage, clock/pacing, and telemetry placeholders.
|
||||
**File(s):** `crates/console/prometeu-hal/src/platform.rs`.
|
||||
|
||||
### Step 2 - Add TestPlatform
|
||||
|
||||
**What:** Provide ergonomic test fixture implementing the initial platform facades.
|
||||
**How:** Reuse current local driver components internally without exposing `HardwareBridge`.
|
||||
**File(s):** `crates/console/prometeu-drivers/src/test_platform.rs`, `crates/console/prometeu-drivers/src/lib.rs`.
|
||||
|
||||
### Step 3 - Migrate representative tests
|
||||
|
||||
**What:** Move a small set of runtime tests from `Hardware::new()` to `TestPlatform`.
|
||||
**How:** Choose tests covering render, composer, and asset surfaces.
|
||||
**File(s):** `crates/console/prometeu-system/src/services/vm_runtime/tests.rs`, asset/memcard test modules as appropriate.
|
||||
|
||||
## Test Requirements
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- `TestPlatform` initializes all required facades.
|
||||
|
||||
### Integration Tests
|
||||
|
||||
- Migrated runtime tests pass with `TestPlatform`.
|
||||
|
||||
### Manual Verification
|
||||
|
||||
- New tests do not import `prometeu_drivers::Hardware` directly.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `RuntimePlatform`/`Platform` aggregate exists.
|
||||
- [ ] `TestPlatform` exists and is used by representative tests.
|
||||
- [ ] Test ergonomics remain acceptable.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `PLN-0098`.
|
||||
- `PLN-0099`.
|
||||
- `PLN-0102`.
|
||||
|
||||
## Risks
|
||||
|
||||
- Overly broad aggregate can recreate `HardwareBridge`. Keep domain access explicit and named.
|
||||
@ -1,79 +0,0 @@
|
||||
---
|
||||
id: PLN-0104
|
||||
ticket: real-render-worker-establishment
|
||||
title: Migrate VM Runtime HostContext to Platform Services
|
||||
status: done
|
||||
created: 2026-06-06
|
||||
completed:
|
||||
ref_decisions: [DEC-0032]
|
||||
tags: [runtime, vm, platform, hostcontext]
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
Change VM runtime execution from receiving `&mut dyn HardwareBridge` to receiving explicit platform services.
|
||||
|
||||
## Background
|
||||
|
||||
`HostContext` currently carries `Option<&mut dyn HardwareBridge>`. That shape lets every syscall see the entire bridge. `DEC-0032` requires explicit services instead.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- Introduce a platform-aware `HostContext` shape.
|
||||
- Migrate VM runtime tick/dispatch to use platform services.
|
||||
- Preserve VM tests and fault behavior.
|
||||
|
||||
### Excluded
|
||||
|
||||
- Do not migrate firmware/Hub in this plan.
|
||||
- Do not remove `HardwareBridge` yet.
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1 - Add platform host context
|
||||
|
||||
**What:** Extend or replace `HostContext` with platform-service access.
|
||||
**How:** Add methods for required VM syscall domains instead of `require_hw()`.
|
||||
**File(s):** `crates/console/prometeu-hal/src/host_context.rs`.
|
||||
|
||||
### Step 2 - Migrate runtime tick
|
||||
|
||||
**What:** Tick uses platform services for begin-frame, composer frame closure, render sink, audio clear, and telemetry reads.
|
||||
**How:** Replace direct bridge access with explicit facade calls.
|
||||
**File(s):** `crates/console/prometeu-system/src/services/vm_runtime/tick.rs`.
|
||||
|
||||
### Step 3 - Migrate dispatch
|
||||
|
||||
**What:** VM syscall dispatch uses explicit facade methods.
|
||||
**How:** Remove `ctx.require_hw()` as the central dispatch dependency.
|
||||
**File(s):** `crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs`.
|
||||
|
||||
## Test Requirements
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- VM dispatch tests pass with platform context.
|
||||
|
||||
### Integration Tests
|
||||
|
||||
- `cargo test -p prometeu-system`.
|
||||
|
||||
### Manual Verification
|
||||
|
||||
- `rg "require_hw|HardwareBridge" vm_runtime` shows no production VM runtime dependency except temporary compatibility adapters explicitly marked.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] VM runtime dispatch no longer depends on the monolithic bridge.
|
||||
- [ ] Tick uses explicit platform services.
|
||||
- [ ] Existing VM fault behavior remains unchanged.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `PLN-0103`.
|
||||
|
||||
## Risks
|
||||
|
||||
- `HostContext` is used in VM unit tests. Migration must keep no-hardware test contexts ergonomic.
|
||||
@ -1,81 +0,0 @@
|
||||
---
|
||||
id: PLN-0105
|
||||
ticket: real-render-worker-establishment
|
||||
title: Migrate Firmware and Hub to Platform Services
|
||||
status: done
|
||||
created: 2026-06-06
|
||||
completed:
|
||||
ref_decisions: [DEC-0032]
|
||||
tags: [firmware, hub, platform, render]
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
Move firmware screens and Hub UI away from `HardwareBridge` and onto explicit platform services.
|
||||
|
||||
## Background
|
||||
|
||||
Firmware splash/crash and Hub currently receive `&mut dyn HardwareBridge` and publish Shell UI directly through it. `DEC-0032` requires migration to platform facades.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- Firmware context uses platform services.
|
||||
- Splash/crash screens publish via render sink.
|
||||
- Hub input/render uses input/render facades.
|
||||
|
||||
### Excluded
|
||||
|
||||
- Do not implement worker.
|
||||
- Do not change Hub UX or firmware state machine semantics.
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1 - Update firmware context
|
||||
|
||||
**What:** Replace `PrometeuContext` hardware bridge field with platform services.
|
||||
**How:** Expose only the services firmware needs: input, assets, render sink.
|
||||
**File(s):** `crates/console/prometeu-firmware/src/firmware/prometeu_context.rs`, `firmware.rs`.
|
||||
|
||||
### Step 2 - Migrate firmware screens
|
||||
|
||||
**What:** Splash and crash screens publish through `RenderSubmissionSink`.
|
||||
**How:** Build Shell UI packet and submit owned submission.
|
||||
**File(s):** `crates/console/prometeu-firmware/src/firmware_step_splash_screen.rs`, `firmware_step_crash_screen.rs`.
|
||||
|
||||
### Step 3 - Migrate Hub
|
||||
|
||||
**What:** Hub uses platform input/render services instead of `HardwareBridge`.
|
||||
**How:** Replace activation input and render publication dependencies.
|
||||
**File(s):** `crates/console/prometeu-system/src/programs/prometeu_hub/prometeu_hub.rs`.
|
||||
|
||||
## Test Requirements
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- Firmware state tests pass.
|
||||
- Hub UI tests pass.
|
||||
|
||||
### Integration Tests
|
||||
|
||||
- `cargo test -p prometeu-firmware -p prometeu-system`.
|
||||
|
||||
### Manual Verification
|
||||
|
||||
- Firmware/Hub no longer call `publish_render_submission` on `HardwareBridge`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Firmware context no longer exposes the monolithic bridge.
|
||||
- [ ] Splash/crash/Hub render via render sink.
|
||||
- [ ] Hub input uses platform/input facade.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `PLN-0103`.
|
||||
- `PLN-0104`.
|
||||
|
||||
## Risks
|
||||
|
||||
- Firmware code is lifecycle-sensitive. Keep behavior changes out of scope.
|
||||
@ -1,79 +0,0 @@
|
||||
---
|
||||
id: PLN-0106
|
||||
ticket: real-render-worker-establishment
|
||||
title: Migrate Desktop Host to Platform Services
|
||||
status: done
|
||||
created: 2026-06-06
|
||||
completed:
|
||||
ref_decisions: [DEC-0032]
|
||||
tags: [host, desktop, platform]
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
Move the desktop host from directly exposing `prometeu_drivers::Hardware` as the runtime bridge to owning a platform implementation.
|
||||
|
||||
## Background
|
||||
|
||||
The host currently creates `Hardware::new()` and passes it into firmware/runtime paths. `DEC-0032` requires hosts to implement platform services, not to expose a monolithic bridge.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- Introduce desktop/local platform ownership in the host.
|
||||
- Replace host usage of `Hardware::W/H` with platform display constants or framebuffer descriptor.
|
||||
- Keep the same visual/audio/input behavior.
|
||||
|
||||
### Excluded
|
||||
|
||||
- Do not implement render worker thread.
|
||||
- Do not change windowing library or presentation policy.
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1 - Add host platform field
|
||||
|
||||
**What:** Host runner owns a platform implementation rather than raw `Hardware`.
|
||||
**How:** Wrap current local driver implementation behind platform facades.
|
||||
**File(s):** `crates/host/prometeu-host-desktop-winit/src/runner.rs`.
|
||||
|
||||
### Step 2 - Replace direct hardware constants
|
||||
|
||||
**What:** Stop reading `Hardware::W/H` directly in host rendering/input.
|
||||
**How:** Use a framebuffer/display descriptor exposed by platform/render service.
|
||||
**File(s):** `crates/host/prometeu-host-desktop-winit/src/runner.rs`, `input.rs`.
|
||||
|
||||
### Step 3 - Update debugger host setup
|
||||
|
||||
**What:** Debugger paths instantiate platform fixture/implementation.
|
||||
**How:** Replace `Hardware::new()` in debugger setup.
|
||||
**File(s):** `crates/host/prometeu-host-desktop-winit/src/debugger.rs`.
|
||||
|
||||
## Test Requirements
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- Host crate tests compile.
|
||||
|
||||
### Integration Tests
|
||||
|
||||
- `cargo test -p prometeu-host-desktop-winit`.
|
||||
|
||||
### Manual Verification
|
||||
|
||||
- Desktop host still runs stress cartridge.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Desktop host owns platform services.
|
||||
- [ ] Host no longer treats `Hardware` as runtime bridge.
|
||||
- [ ] Display dimensions come from platform/render descriptor.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `PLN-0105`.
|
||||
|
||||
## Risks
|
||||
|
||||
- Desktop host mixes runtime and window concerns. Keep worker threading out of this plan.
|
||||
@ -1,89 +0,0 @@
|
||||
---
|
||||
id: PLN-0107
|
||||
ticket: real-render-worker-establishment
|
||||
title: Migrate Remaining Platform Domains
|
||||
status: done
|
||||
created: 2026-06-06
|
||||
completed:
|
||||
ref_decisions: [DEC-0032]
|
||||
tags: [platform, audio, input, assets, clock, telemetry]
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
Migrate non-render domains out of `HardwareBridge` into explicit platform services.
|
||||
|
||||
## Background
|
||||
|
||||
`DEC-0032` permits render to migrate first but requires the full migration to cover audio, input/touch, assets/storage, clock/pacing, and telemetry where applicable.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- Audio service/facade migration.
|
||||
- Input/touch service/facade migration.
|
||||
- Assets/storage service/facade migration.
|
||||
- Clock/pacing and telemetry surface migration where currently coupled.
|
||||
|
||||
### Excluded
|
||||
|
||||
- Do not change domain semantics.
|
||||
- Do not implement async IO unless already supported by existing services.
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1 - Migrate audio
|
||||
|
||||
**What:** Replace `hw.audio()`/`hw.audio_mut()` production dependencies.
|
||||
**How:** Route through explicit audio platform service.
|
||||
**File(s):** `crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs`, firmware/host integration files.
|
||||
|
||||
### Step 2 - Migrate input and touch
|
||||
|
||||
**What:** Replace `hw.pad()`/`hw.touch()` dependencies.
|
||||
**How:** Expose input snapshots through platform input service.
|
||||
**File(s):** `crates/console/prometeu-system`, `crates/console/prometeu-firmware`, host input modules.
|
||||
|
||||
### Step 3 - Migrate assets/storage
|
||||
|
||||
**What:** Replace `hw.assets()`/`hw.assets_mut()` dependencies.
|
||||
**How:** Route through asset/storage services while preserving commit and bank visibility semantics.
|
||||
**File(s):** `crates/console/prometeu-system/src/services/vm_runtime`, `crates/console/prometeu-firmware`.
|
||||
|
||||
### Step 4 - Migrate clock/pacing/telemetry coupling
|
||||
|
||||
**What:** Move any platform-facing clock or telemetry coupling out of `HardwareBridge`.
|
||||
**How:** Use explicit runtime/platform services.
|
||||
**File(s):** `crates/console/prometeu-system/src/services/vm_runtime`, HAL telemetry modules.
|
||||
|
||||
## Test Requirements
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- Audio/input/asset syscall tests pass.
|
||||
|
||||
### Integration Tests
|
||||
|
||||
- `cargo test -p prometeu-system -p prometeu-firmware -p prometeu-drivers`.
|
||||
|
||||
### Manual Verification
|
||||
|
||||
- `rg "audio_mut|pad_mut|touch_mut|assets_mut" crates/console` does not show production bridge dependencies.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Audio migrated to platform service.
|
||||
- [ ] Input/touch migrated to platform service.
|
||||
- [ ] Assets/storage migrated to platform service.
|
||||
- [ ] Clock/pacing/telemetry bridge coupling removed where applicable.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `PLN-0104`.
|
||||
- `PLN-0105`.
|
||||
- `PLN-0106`.
|
||||
|
||||
## Risks
|
||||
|
||||
- Asset visibility semantics are high risk. Keep behavior-preserving tests broad.
|
||||
@ -1,81 +0,0 @@
|
||||
---
|
||||
id: PLN-0108
|
||||
ticket: real-render-worker-establishment
|
||||
title: Migrate Tests to TestPlatform
|
||||
status: done
|
||||
created: 2026-06-06
|
||||
completed:
|
||||
ref_decisions: [DEC-0032]
|
||||
tags: [tests, platform, fixtures]
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
Replace remaining test dependence on `Hardware::new()` and `HardwareBridge` with `TestPlatform` or narrower facade fixtures.
|
||||
|
||||
## Background
|
||||
|
||||
`DEC-0032` requires tests that need a full local platform fixture to use `TestPlatform`, not hidden `HardwareBridge` compatibility.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- VM runtime tests.
|
||||
- Firmware tests.
|
||||
- Driver integration tests that still use monolithic hardware only as fixture.
|
||||
- Host debugger tests where applicable.
|
||||
|
||||
### Excluded
|
||||
|
||||
- Do not remove production bridge until tests are migrated.
|
||||
- Do not change behavior under test.
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1 - Inventory test dependencies
|
||||
|
||||
**What:** Find all direct `Hardware::new()` and `HardwareBridge` test uses.
|
||||
**How:** Use `rg` and classify by domain.
|
||||
**File(s):** test modules across `crates/console` and `crates/host`.
|
||||
|
||||
### Step 2 - Migrate full-platform tests
|
||||
|
||||
**What:** Replace `Hardware::new()` with `TestPlatform`.
|
||||
**How:** Use explicit facade accessors in assertions.
|
||||
**File(s):** `crates/console/prometeu-system/src/services/vm_runtime/tests*.rs`, firmware tests.
|
||||
|
||||
### Step 3 - Migrate narrow tests
|
||||
|
||||
**What:** Replace full platform with narrower fixtures where only one domain is under test.
|
||||
**How:** Use render/composer/audio/input/asset fixtures directly.
|
||||
**File(s):** driver and runtime test modules.
|
||||
|
||||
## Test Requirements
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- All migrated tests pass.
|
||||
|
||||
### Integration Tests
|
||||
|
||||
- `cargo test --workspace` or relevant workspace subset if full workspace is too broad.
|
||||
|
||||
### Manual Verification
|
||||
|
||||
- `rg "Hardware::new\\(|HardwareBridge" crates --glob '*test*'` shows no test dependency except tests explicitly covering legacy removal.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `TestPlatform` is the default full platform fixture.
|
||||
- [ ] Tests no longer preserve `HardwareBridge` by habit.
|
||||
- [ ] Narrow tests use narrower fixtures.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `PLN-0103`.
|
||||
- `PLN-0107`.
|
||||
|
||||
## Risks
|
||||
|
||||
- Large mechanical changes can hide behavior regressions. Keep commits grouped by test domain.
|
||||
@ -1,91 +0,0 @@
|
||||
---
|
||||
id: PLN-0109
|
||||
ticket: real-render-worker-establishment
|
||||
title: Remove HardwareBridge and Update Specs
|
||||
status: done
|
||||
created: 2026-06-06
|
||||
completed:
|
||||
ref_decisions: [DEC-0032]
|
||||
tags: [platform, cleanup, specs, hal]
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
Remove `HardwareBridge` from the codebase and publish the new platform layer contract in specs.
|
||||
|
||||
## Background
|
||||
|
||||
`DEC-0032` explicitly forbids preserving `HardwareBridge` as final compatibility. After all domains and tests migrate, the bridge must be deleted.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- Delete `HardwareBridge`.
|
||||
- Remove exports/imports and adapters that only preserve bridge compatibility.
|
||||
- Update canonical specs in English.
|
||||
- Run validation and broad tests.
|
||||
|
||||
### Excluded
|
||||
|
||||
- Do not implement the real render worker.
|
||||
- Do not change platform service semantics beyond cleanup.
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1 - Delete bridge
|
||||
|
||||
**What:** Remove `HardwareBridge` trait and module.
|
||||
**How:** Delete source file and remove exports/imports.
|
||||
**File(s):** `crates/console/prometeu-hal/src/hardware_bridge.rs`, `crates/console/prometeu-hal/src/lib.rs`, all remaining imports.
|
||||
|
||||
### Step 2 - Remove compatibility adapters
|
||||
|
||||
**What:** Delete migration-only adapters.
|
||||
**How:** Replace final references with direct platform services or remove dead code.
|
||||
**File(s):** HAL, drivers, system, firmware, host modules touched by previous plans.
|
||||
|
||||
### Step 3 - Update specs
|
||||
|
||||
**What:** Document platform layer and removal of monolithic hardware bridge.
|
||||
**How:** Update runtime/host/render specs in English.
|
||||
**File(s):** `docs/specs/runtime/*.md` and related canonical specs.
|
||||
|
||||
### Step 4 - Validate and test
|
||||
|
||||
**What:** Run discussion validation and broad test suite.
|
||||
**How:** Use `discussion validate` and cargo tests for affected crates.
|
||||
**File(s):** repository root.
|
||||
|
||||
## Test Requirements
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- HAL, drivers, firmware, system tests pass.
|
||||
|
||||
### Integration Tests
|
||||
|
||||
- Host desktop crate compiles/tests.
|
||||
- Stress cartridge still runs through local platform path.
|
||||
|
||||
### Manual Verification
|
||||
|
||||
- `rg "HardwareBridge"` returns no production references.
|
||||
- `rg "Hardware::new\\("` returns no runtime-facing dependency; any remaining use is local implementation detail or removed.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `HardwareBridge` is deleted.
|
||||
- [ ] No production/runtime dependencies remain.
|
||||
- [ ] Specs describe platform services/facades.
|
||||
- [ ] Tests pass.
|
||||
- [ ] `AGD-0043` can proceed without needing `&mut Hardware`, `&mut Gfx`, live `FrameComposer`, or mutable VM state in the worker.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `PLN-0107`.
|
||||
- `PLN-0108`.
|
||||
|
||||
## Risks
|
||||
|
||||
- Removing the bridge too early will cause broad breakage. Only execute after dependency searches show all call sites have migrated.
|
||||
@ -1,79 +0,0 @@
|
||||
---
|
||||
id: PLN-0110
|
||||
ticket: real-render-worker-establishment
|
||||
title: Define Owned RGBA8888 Frame Contract
|
||||
status: done
|
||||
created: 2026-06-15
|
||||
completed:
|
||||
ref_decisions: [DEC-0033]
|
||||
tags: [runtime, renderer, worker, hal, frame-contract]
|
||||
---
|
||||
|
||||
## Briefing
|
||||
|
||||
`DEC-0033` requires the worker to publish an owned RGBA8888 frame that is independent of native window APIs. This plan defines that shared HAL contract before any worker implementation depends on it.
|
||||
|
||||
## Decisions de Origem
|
||||
|
||||
- `DEC-0033`: Real Render Worker Contract.
|
||||
|
||||
## Alvo
|
||||
|
||||
Introduce `OwnedRgba8888Frame` in `prometeu-hal` as the canonical worker-to-host published frame type.
|
||||
|
||||
## Escopo
|
||||
|
||||
- Add the shared frame type to HAL.
|
||||
- Include frame id, ownership/epoch metadata, dimensions, stride, and owned `Vec<u32>` RGBA8888 pixels.
|
||||
- Add constructors and validation helpers that keep dimensions, stride, and pixel length coherent.
|
||||
- Add unit tests for layout and validation.
|
||||
|
||||
## Fora de Escopo
|
||||
|
||||
- No worker thread.
|
||||
- No host upload/present integration.
|
||||
- No resource bank access.
|
||||
- No spec updates beyond code-facing rustdoc if useful.
|
||||
|
||||
## Plano de Execucao
|
||||
|
||||
### Step 1 - Add HAL frame module
|
||||
|
||||
**What:** Add `OwnedRgba8888Frame`.
|
||||
**How:** Create a HAL module, likely `crates/console/prometeu-hal/src/owned_frame.rs` or equivalent, and export it from `lib.rs`.
|
||||
**File(s):** `crates/console/prometeu-hal/src/lib.rs`, new HAL frame module.
|
||||
|
||||
### Step 2 - Define required fields
|
||||
|
||||
**What:** Encode the DEC-0033 frame contract.
|
||||
**How:** Include `FrameId`, render ownership/epoch metadata compatible with `RenderSubmission`, `width`, `height`, `stride_pixels`, and `pixels: Vec<u32>`.
|
||||
**File(s):** HAL frame module, existing render ownership types if reuse is needed.
|
||||
|
||||
### Step 3 - Add constructors and validation
|
||||
|
||||
**What:** Prevent malformed frame buffers from entering the worker-host boundary.
|
||||
**How:** Add checked constructor(s) that reject zero dimensions, too-small stride, and pixel buffers shorter than `stride_pixels * height`.
|
||||
**File(s):** HAL frame module.
|
||||
|
||||
### Step 4 - Add tests
|
||||
|
||||
**What:** Pin layout semantics.
|
||||
**How:** Test valid construction, invalid stride, invalid pixel length, and that raw pixel values are preserved as RGBA8888 `u32`.
|
||||
**File(s):** HAL frame module tests.
|
||||
|
||||
## Criterios de Aceite
|
||||
|
||||
- [ ] `OwnedRgba8888Frame` is exported by `prometeu-hal`.
|
||||
- [ ] The type contains `FrameId`, ownership/epoch, width, height, `stride_pixels`, and `Vec<u32>` pixels.
|
||||
- [ ] Invalid dimensions/stride/pixel length cannot be silently accepted by checked constructors.
|
||||
- [ ] No native host/window/pixels/winit/SDL type appears in the HAL frame contract.
|
||||
|
||||
## Tests / Validacao
|
||||
|
||||
- `cargo test -p prometeu-hal`
|
||||
- `rg "OwnedRgba8888Frame" crates/console/prometeu-hal -n`
|
||||
|
||||
## Riscos
|
||||
|
||||
- Reusing native pixel terminology could blur the logical RGBA8888 contract.
|
||||
- Overfitting the frame type to current desktop upload code would leak host details into HAL.
|
||||
@ -1,79 +0,0 @@
|
||||
---
|
||||
id: PLN-0111
|
||||
ticket: real-render-worker-establishment
|
||||
title: Define Read-Only Render Resource Access
|
||||
status: done
|
||||
created: 2026-06-15
|
||||
completed:
|
||||
ref_decisions: [DEC-0033]
|
||||
tags: [runtime, renderer, worker, hal, resources]
|
||||
---
|
||||
|
||||
## Briefing
|
||||
|
||||
`DEC-0033` requires the worker backend to resolve render resources by stable ids through compact read-only access, without copying banks, snapshotting banks, requiring `Arc` in the public contract, or accessing mutable `Hardware`/`Gfx`/`FrameComposer`.
|
||||
|
||||
## Decisions de Origem
|
||||
|
||||
- `DEC-0033`: Real Render Worker Contract.
|
||||
|
||||
## Alvo
|
||||
|
||||
Define shared read-only render resource traits in HAL and implement them over existing local bank storage without changing bank ownership semantics.
|
||||
|
||||
## Escopo
|
||||
|
||||
- Define compact HAL traits for read-only glyph and scene bank access by id.
|
||||
- Keep the public contract id-based and read-only.
|
||||
- Implement the traits in the local driver/resource owner currently used by rendering.
|
||||
- Add tests proving banks are read through the new interface.
|
||||
|
||||
## Fora de Escopo
|
||||
|
||||
- No worker thread.
|
||||
- No bank copying or snapshotting.
|
||||
- No public `Arc` requirement.
|
||||
- No asset residency policy changes.
|
||||
|
||||
## Plano de Execucao
|
||||
|
||||
### Step 1 - Define HAL read-only traits
|
||||
|
||||
**What:** Add worker-facing resource access contracts.
|
||||
**How:** Define traits for resolving glyph and scene banks by stable ids. Return borrowed read-only references or compact read-only views, not mutable handles.
|
||||
**File(s):** `crates/console/prometeu-hal/src/*`, likely a new render resource module.
|
||||
|
||||
### Step 2 - Implement local resource access
|
||||
|
||||
**What:** Expose current local bank storage through the new traits.
|
||||
**How:** Implement the read-only traits for the existing memory bank/resource owner used by `prometeu-drivers`.
|
||||
**File(s):** `crates/console/prometeu-drivers/src/memory_banks.rs`, `frame_composer.rs`, or adjacent resource modules.
|
||||
|
||||
### Step 3 - Thread access through backend-facing APIs
|
||||
|
||||
**What:** Make future backends able to receive resource access without `Hardware`.
|
||||
**How:** Add narrow parameters/types where needed so a backend can be given read-only resources independently from the full platform.
|
||||
**File(s):** HAL contracts and driver modules touched above.
|
||||
|
||||
### Step 4 - Add resource access tests
|
||||
|
||||
**What:** Prove id lookup and read-only behavior.
|
||||
**How:** Add tests for resident/missing glyph and scene banks through the new traits and verify no mutable API is exposed.
|
||||
**File(s):** HAL/driver tests.
|
||||
|
||||
## Criterios de Aceite
|
||||
|
||||
- [ ] Worker-facing render resource access is defined in `prometeu-hal`.
|
||||
- [ ] Resource access is by stable id and read-only.
|
||||
- [ ] The public contract does not require `Arc`, bank copies, bank snapshots, `Hardware`, `Gfx`, or `FrameComposer`.
|
||||
- [ ] Local driver storage implements the traits.
|
||||
|
||||
## Tests / Validacao
|
||||
|
||||
- `cargo test -p prometeu-hal -p prometeu-drivers`
|
||||
- `rg "Arc<.*RenderResource|snapshot.*bank|&mut Hardware|&mut Gfx|FrameComposer" crates/console/prometeu-hal crates/console/prometeu-drivers -n`
|
||||
|
||||
## Riscos
|
||||
|
||||
- Returning concrete bank internals may overexpose driver implementation.
|
||||
- Accidentally requiring `Arc` in the trait would make a storage strategy part of the public contract.
|
||||
@ -1,78 +0,0 @@
|
||||
---
|
||||
id: PLN-0112
|
||||
ticket: real-render-worker-establishment
|
||||
title: Define Render Worker Errors and Telemetry
|
||||
status: done
|
||||
created: 2026-06-15
|
||||
completed:
|
||||
ref_decisions: [DEC-0033]
|
||||
tags: [runtime, renderer, worker, hal, telemetry]
|
||||
---
|
||||
|
||||
## Briefing
|
||||
|
||||
`DEC-0033` requires typed worker/backend errors and telemetry that observes worker behavior without changing VM semantics.
|
||||
|
||||
## Decisions de Origem
|
||||
|
||||
- `DEC-0033`: Real Render Worker Contract.
|
||||
|
||||
## Alvo
|
||||
|
||||
Introduce the shared error and telemetry vocabulary required by the real worker.
|
||||
|
||||
## Escopo
|
||||
|
||||
- Define `RenderWorkerError` or equivalent shared error type.
|
||||
- Define worker telemetry counters/snapshot shape.
|
||||
- Integrate the new telemetry shape with existing render telemetry types where appropriate.
|
||||
- Add tests for error/debug/copy/equality behavior and telemetry defaults.
|
||||
|
||||
## Fora de Escopo
|
||||
|
||||
- No worker lifecycle implementation.
|
||||
- No thread-safe handoff.
|
||||
- No host integration.
|
||||
|
||||
## Plano de Execucao
|
||||
|
||||
### Step 1 - Add worker error type
|
||||
|
||||
**What:** Define typed worker errors.
|
||||
**How:** Add variants for backend unavailable, render failed, publish/present handoff failed, shutdown timeout, stale submission discarded, and worker panic/internal failure.
|
||||
**File(s):** `crates/console/prometeu-hal/src/*` or `prometeu-system` if the type is not shared; prefer HAL if host/tests need it.
|
||||
|
||||
### Step 2 - Add telemetry snapshot
|
||||
|
||||
**What:** Define counters required by DEC-0033.
|
||||
**How:** Include produced, replaced/dropped, consumed, published/presented, stale discarded, render failures, repeated frames, and shutdown timeouts.
|
||||
**File(s):** existing telemetry/render manager modules or new worker telemetry module.
|
||||
|
||||
### Step 3 - Preserve existing telemetry compatibility
|
||||
|
||||
**What:** Avoid breaking existing render telemetry tests.
|
||||
**How:** Map or extend current render telemetry without changing VM semantics or current counters unexpectedly.
|
||||
**File(s):** `crates/console/prometeu-system/src/services/vm_runtime/render_manager.rs`, HAL telemetry modules as needed.
|
||||
|
||||
### Step 4 - Add tests
|
||||
|
||||
**What:** Pin type behavior.
|
||||
**How:** Test default zero telemetry, counter increments through helper methods if added, and error formatting/debug behavior.
|
||||
**File(s):** HAL/system tests.
|
||||
|
||||
## Criterios de Aceite
|
||||
|
||||
- [ ] Worker/backend failure has a typed error surface.
|
||||
- [ ] Telemetry includes all DEC-0033 minimum counters.
|
||||
- [ ] Existing render telemetry tests still pass.
|
||||
- [ ] No worker/backend failure path relies on panic as the normal reporting model.
|
||||
|
||||
## Tests / Validacao
|
||||
|
||||
- `cargo test -p prometeu-hal -p prometeu-system`
|
||||
- `rg "RenderWorkerError|shutdown_timeout|stale.*discard|repeat" crates/console -n`
|
||||
|
||||
## Riscos
|
||||
|
||||
- Splitting telemetry between too many structs may make final worker accounting inconsistent.
|
||||
- Adding errors before call sites exist may create unused-code warnings if not scoped carefully.
|
||||
@ -1,80 +0,0 @@
|
||||
---
|
||||
id: PLN-0113
|
||||
ticket: real-render-worker-establishment
|
||||
title: Implement Thread-Safe Latest-Wins Handoff
|
||||
status: done
|
||||
created: 2026-06-15
|
||||
completed:
|
||||
ref_decisions: [DEC-0033]
|
||||
tags: [runtime, renderer, worker, concurrency, handoff]
|
||||
---
|
||||
|
||||
## Briefing
|
||||
|
||||
`DEC-0033` requires a thread-safe single-slot latest-wins handoff. The producer must replace pending work without waiting for rasterization or present, and the consumer must take ownership of the latest pending submission.
|
||||
|
||||
## Decisions de Origem
|
||||
|
||||
- `DEC-0033`: Real Render Worker Contract.
|
||||
|
||||
## Alvo
|
||||
|
||||
Add the real worker handoff primitive in `prometeu-system`, using explicit synchronization and deterministic tests.
|
||||
|
||||
## Escopo
|
||||
|
||||
- Implement a single-slot pending submission structure.
|
||||
- Use `Mutex<Option<RenderSubmission>> + Condvar` or an equivalent explicit structure.
|
||||
- Add producer publish/replace behavior and consumer wait/take behavior.
|
||||
- Track replacement/drop telemetry.
|
||||
|
||||
## Fora de Escopo
|
||||
|
||||
- No worker thread controller lifecycle.
|
||||
- No backend rendering.
|
||||
- No host integration.
|
||||
- No stale epoch publish behavior beyond storing ownership data already present in submissions.
|
||||
|
||||
## Plano de Execucao
|
||||
|
||||
### Step 1 - Add handoff module
|
||||
|
||||
**What:** Create the worker handoff primitive.
|
||||
**How:** Add a module under `crates/console/prometeu-system/src/services/vm_runtime/`, for example `render_worker_handoff.rs`.
|
||||
**File(s):** new system module, `mod.rs` exports.
|
||||
|
||||
### Step 2 - Implement publish replacement
|
||||
|
||||
**What:** Producer publishes owned `RenderSubmission`.
|
||||
**How:** Lock the slot, replace any pending submission, increment replacement/drop counters when applicable, notify the condvar, and return immediately.
|
||||
**File(s):** handoff module.
|
||||
|
||||
### Step 3 - Implement consumer wait/take
|
||||
|
||||
**What:** Consumer waits for work or shutdown signal.
|
||||
**How:** Add wait/take APIs that block only the consumer side and return owned submissions.
|
||||
**File(s):** handoff module.
|
||||
|
||||
### Step 4 - Add deterministic tests
|
||||
|
||||
**What:** Prove latest-wins and blocking boundaries.
|
||||
**How:** Use threads, barriers, and condvars to prove producer replacement and consumer wake behavior without sleeps.
|
||||
**File(s):** handoff module tests or `async_render_contract_tests.rs`.
|
||||
|
||||
## Criterios de Aceite
|
||||
|
||||
- [ ] Producer publish does not wait for consumer rasterization.
|
||||
- [ ] Multiple pending publishes leave only the newest submission available.
|
||||
- [ ] Replacements increment telemetry.
|
||||
- [ ] Consumer can wait for work and is woken by publish.
|
||||
- [ ] Tests do not use sleep as their primary synchronization proof.
|
||||
|
||||
## Tests / Validacao
|
||||
|
||||
- `cargo test -p prometeu-system render_worker_handoff`
|
||||
- `cargo test -p prometeu-system`
|
||||
|
||||
## Riscos
|
||||
|
||||
- Holding the mutex while doing heavy work would violate non-blocking producer behavior.
|
||||
- Condvar wait loops must handle spurious wakeups.
|
||||
@ -1,78 +0,0 @@
|
||||
---
|
||||
id: PLN-0114
|
||||
ticket: real-render-worker-establishment
|
||||
title: Add Deterministic Render Worker Test Harness
|
||||
status: done
|
||||
created: 2026-06-15
|
||||
completed:
|
||||
ref_decisions: [DEC-0033]
|
||||
tags: [runtime, renderer, worker, tests, concurrency]
|
||||
---
|
||||
|
||||
## Briefing
|
||||
|
||||
`DEC-0033` requires worker behavior to be proven without a native window and without fragile sleeps. This plan creates reusable deterministic test fixtures for the remaining worker plans.
|
||||
|
||||
## Decisions de Origem
|
||||
|
||||
- `DEC-0033`: Real Render Worker Contract.
|
||||
|
||||
## Alvo
|
||||
|
||||
Add a fake backend and synchronization harness capable of blocking render at controlled points, observing publication, and proving non-blocking VM/producer behavior.
|
||||
|
||||
## Escopo
|
||||
|
||||
- Create test-only synchronization helpers using barriers/condvars.
|
||||
- Create a fake render backend shape that can block/fail/publish deterministically.
|
||||
- Add helper functions for constructing owned render submissions and frames.
|
||||
- Reuse the harness in later worker tests.
|
||||
|
||||
## Fora de Escopo
|
||||
|
||||
- No production worker lifecycle.
|
||||
- No desktop host integration.
|
||||
- No real GFX rasterization.
|
||||
|
||||
## Plano de Execucao
|
||||
|
||||
### Step 1 - Add test support module
|
||||
|
||||
**What:** Introduce deterministic worker test fixtures.
|
||||
**How:** Add test-only helpers under `prometeu-system` worker tests or a local test module.
|
||||
**File(s):** `crates/console/prometeu-system/src/services/vm_runtime/*`.
|
||||
|
||||
### Step 2 - Add controllable fake backend
|
||||
|
||||
**What:** Provide backend behavior for tests.
|
||||
**How:** Fake backend can block at render start, unblock by test signal, return errors, and record published frames.
|
||||
**File(s):** worker test module.
|
||||
|
||||
### Step 3 - Add submission/frame builders
|
||||
|
||||
**What:** Make tests concise and consistent.
|
||||
**How:** Build Game/Shell submissions with explicit frame ids and ownership metadata; build `OwnedRgba8888Frame` fixtures.
|
||||
**File(s):** worker test module.
|
||||
|
||||
### Step 4 - Add initial harness self-tests
|
||||
|
||||
**What:** Prove harness itself is deterministic.
|
||||
**How:** Verify a test can block and release backend render without timing sleeps.
|
||||
**File(s):** worker test module.
|
||||
|
||||
## Criterios de Aceite
|
||||
|
||||
- [ ] Tests can block worker backend render using deterministic synchronization.
|
||||
- [ ] Tests can observe published `OwnedRgba8888Frame` values.
|
||||
- [ ] Harness can inject backend errors.
|
||||
- [ ] Harness helpers are scoped to tests or clearly internal.
|
||||
|
||||
## Tests / Validacao
|
||||
|
||||
- `cargo test -p prometeu-system render_worker`
|
||||
- `rg "sleep\\(|thread::sleep" crates/console/prometeu-system/src/services/vm_runtime -n`
|
||||
|
||||
## Riscos
|
||||
|
||||
- Test harness code can become too coupled to implementation internals.
|
||||
- Overly broad helpers can hide important assertions in later plans.
|
||||
@ -1,87 +0,0 @@
|
||||
---
|
||||
id: PLN-0115
|
||||
ticket: real-render-worker-establishment
|
||||
title: Implement Render Worker Controller Lifecycle
|
||||
status: done
|
||||
created: 2026-06-15
|
||||
completed:
|
||||
ref_decisions: [DEC-0033]
|
||||
tags: [runtime, renderer, worker, lifecycle, concurrency]
|
||||
---
|
||||
|
||||
## Briefing
|
||||
|
||||
`DEC-0033` requires a runtime-owned worker controller in `prometeu-system`, with stop signaling, bounded shutdown, pending/in-flight separation, typed errors, and telemetry.
|
||||
|
||||
## Decisions de Origem
|
||||
|
||||
- `DEC-0033`: Real Render Worker Contract.
|
||||
|
||||
## Alvo
|
||||
|
||||
Implement the production worker controller lifecycle around the handoff primitive and backend trait shape.
|
||||
|
||||
## Escopo
|
||||
|
||||
- Add worker controller type in `prometeu-system`.
|
||||
- Spawn and join a worker thread.
|
||||
- Add configurable shutdown timeout with initial default `250ms`.
|
||||
- Add stop token/wakeup behavior.
|
||||
- Preserve pending/in-flight separation.
|
||||
- Capture panic as typed internal failure.
|
||||
|
||||
## Fora de Escopo
|
||||
|
||||
- No desktop host presentation.
|
||||
- No full runtime tick integration.
|
||||
- No stale epoch/repeat behavior beyond lifecycle foundations.
|
||||
|
||||
## Plano de Execucao
|
||||
|
||||
### Step 1 - Define controller and config
|
||||
|
||||
**What:** Add worker controller and configuration.
|
||||
**How:** Include shutdown timeout, default `250ms`, and hooks for backend and handoff dependencies.
|
||||
**File(s):** new `render_worker.rs` or similar in `prometeu-system`.
|
||||
|
||||
### Step 2 - Implement start and run loop
|
||||
|
||||
**What:** Start worker execution.
|
||||
**How:** Spawn a thread that waits on the handoff, takes submissions, marks in-flight locally, calls backend, and publishes results through a frame sink abstraction.
|
||||
**File(s):** worker controller module.
|
||||
|
||||
### Step 3 - Implement stop and bounded join
|
||||
|
||||
**What:** Make shutdown observable and bounded.
|
||||
**How:** Signal stop, wake the condvar, join with timeout strategy available in Rust implementation, and return/report `ShutdownTimeout` when exceeded.
|
||||
**File(s):** worker controller module.
|
||||
|
||||
### Step 4 - Add panic containment
|
||||
|
||||
**What:** Convert worker panic into typed failure.
|
||||
**How:** Use panic containment around backend calls or worker thread join result and update telemetry/errors.
|
||||
**File(s):** worker controller module.
|
||||
|
||||
### Step 5 - Add lifecycle tests
|
||||
|
||||
**What:** Prove start/stop/join behavior.
|
||||
**How:** Use deterministic harness from `PLN-0114`.
|
||||
**File(s):** worker tests.
|
||||
|
||||
## Criterios de Aceite
|
||||
|
||||
- [ ] Worker controller lives in `prometeu-system`.
|
||||
- [ ] Controller can start and stop a real worker thread.
|
||||
- [ ] Shutdown wakes waiting worker and completes within configured bound in tests.
|
||||
- [ ] Shutdown timeout is typed and observable.
|
||||
- [ ] Worker panic is captured as typed internal failure.
|
||||
|
||||
## Tests / Validacao
|
||||
|
||||
- `cargo test -p prometeu-system render_worker`
|
||||
- `cargo test -p prometeu-system`
|
||||
|
||||
## Riscos
|
||||
|
||||
- Rust thread join timeout requires careful design because standard `JoinHandle::join` has no timeout.
|
||||
- Backend ownership must be `'static + Send` without leaking host-specific types into system contracts.
|
||||
@ -1,78 +0,0 @@
|
||||
---
|
||||
id: PLN-0116
|
||||
ticket: real-render-worker-establishment
|
||||
title: Implement Fake Local Render Backend
|
||||
status: done
|
||||
created: 2026-06-15
|
||||
completed:
|
||||
ref_decisions: [DEC-0033]
|
||||
tags: [runtime, renderer, worker, backend, drivers]
|
||||
---
|
||||
|
||||
## Briefing
|
||||
|
||||
`DEC-0033` requires fake/local backend behavior before desktop integration so worker semantics can be proven without a window.
|
||||
|
||||
## Decisions de Origem
|
||||
|
||||
- `DEC-0033`: Real Render Worker Contract.
|
||||
|
||||
## Alvo
|
||||
|
||||
Implement backend contracts that consume `RenderSubmission` plus read-only resources and produce `OwnedRgba8888Frame`.
|
||||
|
||||
## Escopo
|
||||
|
||||
- Define or finalize a backend trait for worker rendering.
|
||||
- Add a fake backend for deterministic tests.
|
||||
- Add a local framebuffer backend that can rasterize through existing driver/GFX logic without exposing mutable `Hardware` as the worker contract.
|
||||
- Verify output frame ownership and pixels.
|
||||
|
||||
## Fora de Escopo
|
||||
|
||||
- No desktop upload/present.
|
||||
- No runtime tick integration.
|
||||
- No resource mutation policy changes.
|
||||
|
||||
## Plano de Execucao
|
||||
|
||||
### Step 1 - Define backend trait
|
||||
|
||||
**What:** Encode backend consumption contract.
|
||||
**How:** Trait consumes `RenderSubmission` and read-only resource access and returns `OwnedRgba8888Frame` or `RenderWorkerError`.
|
||||
**File(s):** HAL if shared, or system if internal; prefer HAL if host/drivers implement it.
|
||||
|
||||
### Step 2 - Implement fake backend
|
||||
|
||||
**What:** Provide test backend.
|
||||
**How:** Fake backend returns deterministic frames, can block through harness, and can return typed errors.
|
||||
**File(s):** system test module or driver test utility.
|
||||
|
||||
### Step 3 - Implement local framebuffer backend
|
||||
|
||||
**What:** Provide non-window raster backend.
|
||||
**How:** Use existing GFX rendering internally to turn submissions into `OwnedRgba8888Frame`, but expose only the backend trait and read-only resources.
|
||||
**File(s):** `prometeu-drivers` rendering modules, possibly a new backend module.
|
||||
|
||||
### Step 4 - Add output tests
|
||||
|
||||
**What:** Prove frame content and metadata.
|
||||
**How:** Render simple Game2D and ShellUi submissions and compare `OwnedRgba8888Frame` pixels/metadata.
|
||||
**File(s):** driver/system tests.
|
||||
|
||||
## Criterios de Aceite
|
||||
|
||||
- [ ] Backend trait does not expose mutable `Hardware`, `Gfx`, `FrameComposer`, or VM state.
|
||||
- [ ] Fake backend supports deterministic blocking/error behavior.
|
||||
- [ ] Local backend produces `OwnedRgba8888Frame`.
|
||||
- [ ] Game2D and ShellUi submissions can produce frame output without a native window.
|
||||
|
||||
## Tests / Validacao
|
||||
|
||||
- `cargo test -p prometeu-drivers -p prometeu-system`
|
||||
- `rg "&mut Hardware|&mut Gfx|FrameComposer" crates/console/prometeu-hal crates/console/prometeu-system/src/services/vm_runtime -n`
|
||||
|
||||
## Riscos
|
||||
|
||||
- Local backend may be tempted to reuse `Hardware` directly as the public worker contract.
|
||||
- Resource access gaps may surface when moving from fake to local rasterization.
|
||||
@ -1,80 +0,0 @@
|
||||
---
|
||||
id: PLN-0117
|
||||
ticket: real-render-worker-establishment
|
||||
title: Integrate Runtime Submission With Worker
|
||||
status: done
|
||||
created: 2026-06-15
|
||||
completed:
|
||||
ref_decisions: [DEC-0033]
|
||||
tags: [runtime, renderer, worker, integration]
|
||||
---
|
||||
|
||||
## Briefing
|
||||
|
||||
`DEC-0033` requires VM/render production to submit work without blocking on worker consumption. This plan connects runtime frame closure to the worker handoff while preserving local synchronous fallback.
|
||||
|
||||
## Decisions de Origem
|
||||
|
||||
- `DEC-0033`: Real Render Worker Contract.
|
||||
|
||||
## Alvo
|
||||
|
||||
Route eligible Game render submissions from `RenderManager` into the real worker handoff and keep Shell/local paths correct.
|
||||
|
||||
## Escopo
|
||||
|
||||
- Add worker-capable runtime path.
|
||||
- Submit owned `RenderSubmission` to the handoff.
|
||||
- Preserve AppMode policy: Game may use worker; Shell remains local unless a future decision changes it.
|
||||
- Keep local synchronous fallback.
|
||||
- Prove VM tick returns without waiting for worker backend completion.
|
||||
|
||||
## Fora de Escopo
|
||||
|
||||
- No desktop host presentation.
|
||||
- No final stale/repeat behavior beyond integration needed for basic worker use.
|
||||
- No spec edits.
|
||||
|
||||
## Plano de Execucao
|
||||
|
||||
### Step 1 - Add worker ownership to runtime state
|
||||
|
||||
**What:** Store worker controller/handoff in runtime-owned state.
|
||||
**How:** Extend `VirtualMachineRuntime` or `RenderManager` integration points without moving policy to host.
|
||||
**File(s):** `crates/console/prometeu-system/src/services/vm_runtime/tick.rs`, `render_manager.rs`, worker modules.
|
||||
|
||||
### Step 2 - Submit Game frames to worker handoff
|
||||
|
||||
**What:** Replace local worker prototype path with real worker submission.
|
||||
**How:** On eligible Game frame closure, publish owned `RenderSubmission` into the handoff and return to VM tick without waiting for consumption.
|
||||
**File(s):** runtime tick/render manager integration.
|
||||
|
||||
### Step 3 - Preserve local fallback
|
||||
|
||||
**What:** Keep current local synchronous behavior available.
|
||||
**How:** Use existing `RenderConsumerPath`/policy or update it to select local fallback when worker is disabled/unavailable.
|
||||
**File(s):** `render_manager.rs`, `tick.rs`.
|
||||
|
||||
### Step 4 - Add non-blocking tests
|
||||
|
||||
**What:** Prove VM producer does not block.
|
||||
**How:** Use harness to hold backend render, run tick/publish additional frames, assert return and replacement telemetry.
|
||||
**File(s):** runtime worker tests.
|
||||
|
||||
## Criterios de Aceite
|
||||
|
||||
- [ ] Game render submissions can be sent to the real worker handoff.
|
||||
- [ ] VM tick does not wait for worker rasterization.
|
||||
- [ ] Shell/local policy remains unchanged.
|
||||
- [ ] Local synchronous fallback still passes existing tests.
|
||||
- [ ] Replacement telemetry increments when producer outruns worker.
|
||||
|
||||
## Tests / Validacao
|
||||
|
||||
- `cargo test -p prometeu-system`
|
||||
- `cargo test -p prometeu-firmware`
|
||||
|
||||
## Riscos
|
||||
|
||||
- Accidentally making frame closure wait for worker consumption would violate DEC-0033.
|
||||
- Mixing Shell lifecycle rendering into the worker path too early would broaden scope.
|
||||
@ -1,85 +0,0 @@
|
||||
---
|
||||
id: PLN-0118
|
||||
ticket: real-render-worker-establishment
|
||||
title: Add Stale Epoch and Repeat Frame Behavior
|
||||
status: done
|
||||
created: 2026-06-15
|
||||
completed:
|
||||
ref_decisions: [DEC-0033]
|
||||
tags: [runtime, renderer, worker, ownership, telemetry]
|
||||
---
|
||||
|
||||
## Briefing
|
||||
|
||||
`DEC-0033` requires the worker to discard stale in-flight frames before publication and repeat the last published `OwnedRgba8888Frame` rather than recomposing.
|
||||
|
||||
## Decisions de Origem
|
||||
|
||||
- `DEC-0033`: Real Render Worker Contract.
|
||||
|
||||
## Alvo
|
||||
|
||||
Complete ownership/epoch checks and repeat-last-published-frame behavior for the worker path.
|
||||
|
||||
## Escopo
|
||||
|
||||
- Add shared active ownership/epoch observation for worker publication checks.
|
||||
- Discard stale in-flight frames before publication.
|
||||
- Store and expose the latest published `OwnedRgba8888Frame`.
|
||||
- Implement repeat-last-frame behavior and telemetry.
|
||||
|
||||
## Fora de Escopo
|
||||
|
||||
- No desktop upload/present.
|
||||
- No new AppMode policy.
|
||||
- No native window invalidation changes.
|
||||
|
||||
## Plano de Execucao
|
||||
|
||||
### Step 1 - Expose ownership/epoch to worker
|
||||
|
||||
**What:** Let worker validate in-flight submissions before publishing.
|
||||
**How:** Add a thread-safe snapshot or handle that the worker can read without mutating runtime state.
|
||||
**File(s):** `render_manager.rs`, worker modules.
|
||||
|
||||
### Step 2 - Implement stale discard
|
||||
|
||||
**What:** Prevent stale pixels from publication.
|
||||
**How:** Before publishing frame output, compare submission ownership/epoch with active ownership/epoch; discard on mismatch and increment telemetry.
|
||||
**File(s):** worker controller module.
|
||||
|
||||
### Step 3 - Store latest published frame
|
||||
|
||||
**What:** Make the latest valid frame available to host/fallback tests.
|
||||
**How:** Add a published-frame slot distinct from pending submission and in-flight work.
|
||||
**File(s):** worker controller or published frame module.
|
||||
|
||||
### Step 4 - Implement repeat behavior
|
||||
|
||||
**What:** Repeat last valid frame without recomposition.
|
||||
**How:** Add API to fetch/reuse the latest published `OwnedRgba8888Frame`; increment repeat telemetry when cadence requests reuse.
|
||||
**File(s):** worker modules, tests.
|
||||
|
||||
### Step 5 - Add tests
|
||||
|
||||
**What:** Prove stale discard and repeat.
|
||||
**How:** Use harness to hold in-flight work, change owner/epoch, release backend, and assert no publication; separately assert repeat returns last frame.
|
||||
**File(s):** worker tests.
|
||||
|
||||
## Criterios de Aceite
|
||||
|
||||
- [ ] Worker checks active owner/epoch before publishing.
|
||||
- [ ] Stale in-flight frames are discarded and counted.
|
||||
- [ ] Latest published frame is an `OwnedRgba8888Frame`.
|
||||
- [ ] Repeat uses the last published frame and does not recompose.
|
||||
- [ ] Tests cover stale discard and repeat deterministically.
|
||||
|
||||
## Tests / Validacao
|
||||
|
||||
- `cargo test -p prometeu-system render_worker`
|
||||
- `cargo test -p prometeu-system`
|
||||
|
||||
## Riscos
|
||||
|
||||
- Reading active ownership from the wrong layer may reintroduce mutable runtime coupling.
|
||||
- Repeat behavior must not hide render failures unless telemetry records them.
|
||||
@ -1,81 +0,0 @@
|
||||
---
|
||||
id: PLN-0119
|
||||
ticket: real-render-worker-establishment
|
||||
title: Integrate Desktop Host Worker Presentation
|
||||
status: done
|
||||
created: 2026-06-15
|
||||
completed:
|
||||
ref_decisions: [DEC-0033]
|
||||
tags: [runtime, renderer, worker, host, desktop]
|
||||
---
|
||||
|
||||
## Briefing
|
||||
|
||||
`DEC-0033` is not complete until the real host path uses the worker. This plan integrates desktop presentation with the latest published `OwnedRgba8888Frame` while keeping native upload/present in the host event loop.
|
||||
|
||||
## Decisions de Origem
|
||||
|
||||
- `DEC-0033`: Real Render Worker Contract.
|
||||
|
||||
## Alvo
|
||||
|
||||
Make `prometeu-host-desktop-winit` present frames produced by the worker path.
|
||||
|
||||
## Escopo
|
||||
|
||||
- Wire the host to enable/use the worker path.
|
||||
- Read the latest published `OwnedRgba8888Frame`.
|
||||
- Upload/copy RGBA8888 pixels to the existing pixels/winit presentation surface in the event loop.
|
||||
- Preserve host invalidation and redraw behavior.
|
||||
- Keep worker free of native window dependencies.
|
||||
|
||||
## Fora de Escopo
|
||||
|
||||
- No SDL migration.
|
||||
- No change to logical pixel format.
|
||||
- No change to input/audio/filesystem host behavior.
|
||||
|
||||
## Plano de Execucao
|
||||
|
||||
### Step 1 - Add host access to published frame
|
||||
|
||||
**What:** Expose latest worker-published frame to desktop host.
|
||||
**How:** Add an API through runtime/platform integration that returns or copies the latest `OwnedRgba8888Frame` for host upload.
|
||||
**File(s):** system worker modules, host runner integration.
|
||||
|
||||
### Step 2 - Upload RGBA8888 in event loop
|
||||
|
||||
**What:** Present worker output through existing host surface.
|
||||
**How:** Convert/copy `Vec<u32>` RGBA8888 into the existing pixels frame buffer using existing RGBA8888 utility conventions.
|
||||
**File(s):** `crates/host/prometeu-host-desktop-winit/src/runner.rs`, `utilities.rs` if needed.
|
||||
|
||||
### Step 3 - Preserve invalidation model
|
||||
|
||||
**What:** Keep host redraw policy coherent.
|
||||
**How:** Trigger redraw when a new frame is published or host invalidation occurs; repeat uses the last published frame.
|
||||
**File(s):** host runner/presentation state.
|
||||
|
||||
### Step 4 - Add host tests
|
||||
|
||||
**What:** Prove host consumes worker-published frame.
|
||||
**How:** Add unit tests for RGBA8888 copy/upload helper and presentation state transitions without opening a native window.
|
||||
**File(s):** host tests.
|
||||
|
||||
## Criterios de Aceite
|
||||
|
||||
- [ ] Desktop host presents the latest worker-published `OwnedRgba8888Frame`.
|
||||
- [ ] Worker code does not import winit, pixels, SDL, swapchain, native texture, or window APIs.
|
||||
- [ ] Host event loop remains responsible for native present.
|
||||
- [ ] Existing host invalidation tests still pass.
|
||||
- [ ] Real host path can run with worker enabled.
|
||||
|
||||
## Tests / Validacao
|
||||
|
||||
- `cargo test -p prometeu-host-desktop-winit`
|
||||
- `cargo test -p prometeu-system -p prometeu-firmware`
|
||||
- Manual run of a simple cartridge through desktop host with worker path enabled.
|
||||
|
||||
## Riscos
|
||||
|
||||
- Upload/copy can accidentally reinterpret RGBA8888 as native endian bytes.
|
||||
- Host redraw policy can regress into perpetual polling if publication/invalidation are not kept separate.
|
||||
@ -1,79 +0,0 @@
|
||||
---
|
||||
id: PLN-0120
|
||||
ticket: real-render-worker-establishment
|
||||
title: Update Real Render Worker Specs
|
||||
status: done
|
||||
created: 2026-06-15
|
||||
completed:
|
||||
ref_decisions: [DEC-0033]
|
||||
tags: [runtime, renderer, worker, specs]
|
||||
---
|
||||
|
||||
## Briefing
|
||||
|
||||
`DEC-0033` requires canonical runtime specs to document the real worker contract after implementation publishes the behavior.
|
||||
|
||||
## Decisions de Origem
|
||||
|
||||
- `DEC-0033`: Real Render Worker Contract.
|
||||
|
||||
## Alvo
|
||||
|
||||
Update English canonical specs to describe `OwnedRgba8888Frame`, worker handoff, host presentation split, read-only bank access, shutdown, errors, and telemetry.
|
||||
|
||||
## Escopo
|
||||
|
||||
- Update GFX/render specs.
|
||||
- Update portability/host presentation specs.
|
||||
- Update host ABI/syscall specs if worker behavior affects published runtime surfaces.
|
||||
- Ensure no spec says the worker owns native window present.
|
||||
|
||||
## Fora de Escopo
|
||||
|
||||
- No new architecture beyond `DEC-0033`.
|
||||
- No lesson writing.
|
||||
- No implementation changes except documentation.
|
||||
|
||||
## Plano de Execucao
|
||||
|
||||
### Step 1 - Update GFX spec
|
||||
|
||||
**What:** Document worker frame publication.
|
||||
**How:** Add `OwnedRgba8888Frame`, RGBA8888 `Vec<u32>` semantics, latest published frame, and repeat behavior.
|
||||
**File(s):** `docs/specs/runtime/04-gfx-peripheral.md`.
|
||||
|
||||
### Step 2 - Update portability spec
|
||||
|
||||
**What:** Document worker/host split.
|
||||
**How:** State that worker produces owned RGBA8888 frames and host event loop performs native upload/present.
|
||||
**File(s):** `docs/specs/runtime/11-portability-and-cross-platform-execution.md`.
|
||||
|
||||
### Step 3 - Update concurrency/events spec if needed
|
||||
|
||||
**What:** Document handoff and shutdown behavior.
|
||||
**How:** Add single-slot latest-wins, bounded shutdown, and non-blocking producer semantics where concurrency is specified.
|
||||
**File(s):** `docs/specs/runtime/09-events-and-concurrency.md` or adjacent runtime spec.
|
||||
|
||||
### Step 4 - Update README/index references
|
||||
|
||||
**What:** Keep spec map discoverable.
|
||||
**How:** Add any needed cross-reference to worker/render sections.
|
||||
**File(s):** `docs/specs/runtime/README.md`.
|
||||
|
||||
## Criterios de Aceite
|
||||
|
||||
- [ ] Specs define `OwnedRgba8888Frame`.
|
||||
- [ ] Specs state worker does not own native window present.
|
||||
- [ ] Specs state producer does not block on worker rasterization/present.
|
||||
- [ ] Specs document read-only bank access by id.
|
||||
- [ ] Specs document bounded shutdown and typed error expectations.
|
||||
|
||||
## Tests / Validacao
|
||||
|
||||
- `rg "OwnedRgba8888Frame|latest-wins|shutdown|read-only" docs/specs/runtime -n`
|
||||
- `discussion validate`
|
||||
|
||||
## Riscos
|
||||
|
||||
- Updating specs before implementation may accidentally publish intent as completed behavior; execute after code plans that establish the behavior.
|
||||
- Spec wording must remain English and normative.
|
||||
@ -1,102 +0,0 @@
|
||||
---
|
||||
id: PLN-0121
|
||||
ticket: real-render-worker-establishment
|
||||
title: Final Worker Path Validation and Hardening
|
||||
status: done
|
||||
created: 2026-06-15
|
||||
completed:
|
||||
ref_decisions: [DEC-0033]
|
||||
tags: [runtime, renderer, worker, validation, hardening]
|
||||
---
|
||||
|
||||
## Briefing
|
||||
|
||||
`DEC-0033` is complete only when the worker functions on the real host path and the contract is proven end to end. This plan performs final validation, residue scans, and hardening before housekeeping.
|
||||
|
||||
## Decisions de Origem
|
||||
|
||||
- `DEC-0033`: Real Render Worker Contract.
|
||||
|
||||
## Alvo
|
||||
|
||||
Prove the real render worker path satisfies DEC-0033 across HAL, system, drivers, firmware, host, specs, and tests.
|
||||
|
||||
## Escopo
|
||||
|
||||
- Run broad test suites.
|
||||
- Add missing edge tests found during validation.
|
||||
- Scan for forbidden coupling.
|
||||
- Verify host real path uses worker.
|
||||
- Verify local fallback still works.
|
||||
- Prepare evidence for discussion housekeeping.
|
||||
|
||||
## Fora de Escopo
|
||||
|
||||
- No new worker architecture.
|
||||
- No SDL migration.
|
||||
- No unrelated performance tuning.
|
||||
|
||||
## Plano de Execucao
|
||||
|
||||
### Step 1 - Run full affected test suite
|
||||
|
||||
**What:** Validate all touched crates.
|
||||
**How:** Run workspace tests and focused worker/host tests.
|
||||
**File(s):** repository root.
|
||||
|
||||
### Step 2 - Run coupling residue scans
|
||||
|
||||
**What:** Enforce DEC-0033 boundaries.
|
||||
**How:** Search worker/system/HAL code for native window dependencies, mutable hardware/GFX/composer leaks, bank copying/snapshot terminology, and sleeps in concurrency tests.
|
||||
**File(s):** repository root.
|
||||
|
||||
### Step 3 - Add missing hardening tests
|
||||
|
||||
**What:** Close any discovered gaps.
|
||||
**How:** Add focused tests for missing telemetry/error/shutdown/host-path cases.
|
||||
**File(s):** affected test modules.
|
||||
|
||||
### Step 4 - Verify host path
|
||||
|
||||
**What:** Prove desktop host uses worker output.
|
||||
**How:** Run host tests and a manual/simple cartridge path with worker enabled; record evidence in final summary.
|
||||
**File(s):** host runner and runtime integration.
|
||||
|
||||
### Step 5 - Prepare housekeeping evidence
|
||||
|
||||
**What:** Make `DSC-0042` ready for final lesson/housekeeping.
|
||||
**How:** Record completed plan state and validation evidence for later `discussion-housekeep`.
|
||||
**File(s):** discussion artifacts if needed.
|
||||
|
||||
## Criterios de Aceite
|
||||
|
||||
- [ ] `cargo test --workspace` passes.
|
||||
- [ ] Focused worker, system, drivers, firmware, and host tests pass.
|
||||
- [ ] Residue scans show worker code does not depend on native window APIs.
|
||||
- [ ] Residue scans show worker boundary does not expose `&mut Hardware`, `&mut Gfx`, live `FrameComposer`, or mutable VM state.
|
||||
- [ ] Desktop host path uses the real worker output.
|
||||
- [ ] Local fallback still works.
|
||||
- [ ] `discussion validate` passes.
|
||||
|
||||
## Tests / Validacao
|
||||
|
||||
- `cargo test --workspace`
|
||||
- `cargo test -p prometeu-system -p prometeu-drivers -p prometeu-hal -p prometeu-firmware -p prometeu-host-desktop-winit`
|
||||
- `discussion validate`
|
||||
- `rg "winit|pixels|SDL|swapchain|native texture" crates/console/prometeu-system crates/console/prometeu-hal -n`
|
||||
- `rg "&mut Hardware|&mut Gfx|FrameComposer" crates/console/prometeu-system/src/services/vm_runtime crates/console/prometeu-hal -n`
|
||||
- `rg "thread::sleep|sleep\\(" crates/console/prometeu-system/src/services/vm_runtime -n`
|
||||
|
||||
## Riscos
|
||||
|
||||
- Final validation may expose earlier plan gaps; fix them in the narrowest affected plan/module.
|
||||
- Manual host evidence can be flaky if tied to native window availability; keep automated host-unit evidence as the primary gate.
|
||||
|
||||
## Validation Evidence
|
||||
|
||||
- `cargo test --workspace`: passed.
|
||||
- `cargo test -p prometeu-system -p prometeu-drivers -p prometeu-hal -p prometeu-firmware -p prometeu-host-desktop-winit`: passed.
|
||||
- Native API coupling scan for worker/runtime console code: no `winit`, `pixels`, SDL, swapchain, or native texture API references found.
|
||||
- Mutable boundary scan for worker/runtime console code: no `&mut Hardware`, `&mut Gfx`, or concrete live `FrameComposer` references found.
|
||||
- Timing scan for render worker runtime tests: no `thread::sleep` / `sleep(` usage found.
|
||||
- Added host-unit evidence that worker frame presentation is restricted to `GameRunning`; Shell/Hub paths keep local presentation behavior.
|
||||
Loading…
x
Reference in New Issue
Block a user