implements PLN-0116
This commit is contained in:
parent
4b890850aa
commit
0c6a669e6e
@ -3,6 +3,7 @@ mod audio;
|
||||
mod frame_composer;
|
||||
mod gfx;
|
||||
pub mod hardware;
|
||||
mod local_render_backend;
|
||||
mod memory_banks;
|
||||
mod pad;
|
||||
mod test_platform;
|
||||
@ -12,6 +13,7 @@ pub use crate::asset::AssetManager;
|
||||
pub use crate::audio::{Audio, AudioCommand, Channel, MAX_CHANNELS, OUTPUT_SAMPLE_RATE};
|
||||
pub use crate::frame_composer::{ComposerBuffer, FrameComposer, SceneStatus, SpriteController};
|
||||
pub use crate::gfx::Gfx;
|
||||
pub use crate::local_render_backend::LocalFramebufferRenderBackend;
|
||||
pub use crate::memory_banks::{
|
||||
GlyphBankPoolAccess, GlyphBankPoolInstaller, MemoryBanks, SceneBankPoolAccess,
|
||||
SceneBankPoolInstaller, SoundBankPoolAccess, SoundBankPoolInstaller,
|
||||
|
||||
111
crates/console/prometeu-drivers/src/local_render_backend.rs
Normal file
111
crates/console/prometeu-drivers/src/local_render_backend.rs
Normal file
@ -0,0 +1,111 @@
|
||||
use crate::gfx::Gfx;
|
||||
use crate::memory_banks::GlyphBankPoolAccess;
|
||||
use prometeu_hal::app_mode::AppMode;
|
||||
use prometeu_hal::{
|
||||
OwnedRgba8888Frame, RenderSubmission, RenderSubmissionPacket, RenderWorkerBackend,
|
||||
RenderWorkerError,
|
||||
};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
pub struct LocalFramebufferRenderBackend {
|
||||
gfx: Mutex<Gfx>,
|
||||
}
|
||||
|
||||
impl LocalFramebufferRenderBackend {
|
||||
pub fn new(width: usize, height: usize, glyph_banks: Arc<dyn GlyphBankPoolAccess>) -> Self {
|
||||
Self { gfx: Mutex::new(Gfx::new(width, height, glyph_banks)) }
|
||||
}
|
||||
|
||||
pub fn render_submission(
|
||||
&self,
|
||||
submission: &RenderSubmission,
|
||||
) -> Result<OwnedRgba8888Frame, RenderWorkerError> {
|
||||
let mut gfx = self.gfx.lock().map_err(|_| RenderWorkerError::InternalFailure)?;
|
||||
match &submission.packet {
|
||||
RenderSubmissionPacket::Game2D(packet) if submission.app_mode == AppMode::Game => {
|
||||
gfx.render_game2d_frame_packet(packet);
|
||||
}
|
||||
RenderSubmissionPacket::ShellUi(packet) if submission.app_mode == AppMode::Shell => {
|
||||
gfx.render_shell_ui_frame_packet(packet);
|
||||
}
|
||||
_ => return Err(RenderWorkerError::RenderFailed),
|
||||
}
|
||||
|
||||
gfx.present();
|
||||
let (width, height) = gfx.size();
|
||||
OwnedRgba8888Frame::packed(
|
||||
submission.frame_id,
|
||||
submission.ownership,
|
||||
width,
|
||||
height,
|
||||
gfx.front_buffer().to_vec(),
|
||||
)
|
||||
.map_err(|_| RenderWorkerError::InternalFailure)
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderWorkerBackend for LocalFramebufferRenderBackend {
|
||||
fn render(
|
||||
&self,
|
||||
submission: &RenderSubmission,
|
||||
) -> Result<OwnedRgba8888Frame, RenderWorkerError> {
|
||||
self.render_submission(submission)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::memory_banks::MemoryBanks;
|
||||
use prometeu_hal::app_mode::AppMode;
|
||||
use prometeu_hal::color::Color;
|
||||
use prometeu_hal::primitives::Rect;
|
||||
use prometeu_hal::{
|
||||
FrameId, Game2DFramePacket, Gfx2dCommand, GfxUiCommand, RenderOwnership, ShellUiFramePacket,
|
||||
};
|
||||
|
||||
fn backend() -> LocalFramebufferRenderBackend {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
LocalFramebufferRenderBackend::new(4, 4, banks)
|
||||
}
|
||||
|
||||
#[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 ownership = RenderOwnership::new(3, AppMode::Game, 9);
|
||||
let submission =
|
||||
RenderSubmission::game2d(FrameId::new(5), packet).with_ownership(ownership);
|
||||
|
||||
let frame = backend.render(&submission).expect("game frame should render");
|
||||
|
||||
assert_eq!(frame.frame_id, FrameId::new(5));
|
||||
assert_eq!(frame.ownership, ownership);
|
||||
assert_eq!(frame.width, 4);
|
||||
assert_eq!(frame.height, 4);
|
||||
assert_eq!(frame.pixels[1 + 4], Color::RED.raw());
|
||||
assert_eq!(frame.pixels[2 + 2 * 4], Color::RED.raw());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_backend_renders_shell_ui_submission_to_owned_frame() {
|
||||
let backend = backend();
|
||||
let packet = ShellUiFramePacket::new(vec![GfxUiCommand::FillRect {
|
||||
rect: Rect { x: 0, y: 0, w: 1, h: 1 },
|
||||
color: Color::GREEN,
|
||||
}]);
|
||||
let ownership = RenderOwnership::new(4, AppMode::Shell, 10);
|
||||
let submission =
|
||||
RenderSubmission::shell_ui(FrameId::new(6), packet).with_ownership(ownership);
|
||||
|
||||
let frame = backend.render(&submission).expect("shell frame should render");
|
||||
|
||||
assert_eq!(frame.frame_id, FrameId::new(6));
|
||||
assert_eq!(frame.ownership, ownership);
|
||||
assert_eq!(frame.pixels[0], Color::GREEN.raw());
|
||||
}
|
||||
}
|
||||
@ -60,5 +60,7 @@ pub use render_submission::{
|
||||
Gfx2dCommand, GfxUiCommand, HudCommand, HudPacket, RenderOwnership, RenderSubmission,
|
||||
RenderSubmissionPacket, ShellUiFramePacket,
|
||||
};
|
||||
pub use render_worker::{RenderWorkerError, RenderWorkerTelemetry};
|
||||
pub use render_worker::{
|
||||
RenderWorkerBackend, RenderWorkerError, RenderWorkerFrameSink, RenderWorkerTelemetry,
|
||||
};
|
||||
pub use touch_bridge::TouchBridge;
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
use std::fmt;
|
||||
|
||||
use crate::{OwnedRgba8888Frame, RenderSubmission};
|
||||
|
||||
/// Typed failure surface for the real render worker and its backend.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum RenderWorkerError {
|
||||
@ -30,6 +32,17 @@ impl fmt::Display for RenderWorkerError {
|
||||
|
||||
impl std::error::Error for RenderWorkerError {}
|
||||
|
||||
pub trait RenderWorkerBackend: Send + 'static {
|
||||
fn render(
|
||||
&self,
|
||||
submission: &RenderSubmission,
|
||||
) -> Result<OwnedRgba8888Frame, RenderWorkerError>;
|
||||
}
|
||||
|
||||
pub trait RenderWorkerFrameSink: Send + 'static {
|
||||
fn publish(&self, frame: OwnedRgba8888Frame) -> Result<(), RenderWorkerError>;
|
||||
}
|
||||
|
||||
/// Snapshot of render worker handoff, render, publish, and shutdown counters.
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub struct RenderWorkerTelemetry {
|
||||
|
||||
@ -6,11 +6,12 @@ mod services;
|
||||
pub use crash_report::CrashReport;
|
||||
pub use os::{LifecycleError, LifecycleOperation, SystemOS};
|
||||
pub use programs::{NativeShellApp, PrometeuHub, SystemProfileAction, SystemProfileUpdate};
|
||||
pub use prometeu_hal::{RenderWorkerBackend, RenderWorkerFrameSink};
|
||||
pub use services::fs;
|
||||
pub use services::process;
|
||||
pub use services::task;
|
||||
pub use services::vm_runtime::{
|
||||
RenderWorkerBackend, RenderWorkerConfig, RenderWorkerController, RenderWorkerFrameSink,
|
||||
RenderWorkerHandoff, RenderWorkerHandoffWait, VirtualMachineRuntime,
|
||||
RenderWorkerConfig, RenderWorkerController, RenderWorkerHandoff, RenderWorkerHandoffWait,
|
||||
VirtualMachineRuntime,
|
||||
};
|
||||
pub use services::windows;
|
||||
|
||||
@ -21,9 +21,7 @@ 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::{
|
||||
RenderWorkerBackend, RenderWorkerConfig, RenderWorkerController, RenderWorkerFrameSink,
|
||||
};
|
||||
pub use render_worker::{RenderWorkerConfig, RenderWorkerController};
|
||||
pub use render_worker_handoff::{RenderWorkerHandoff, RenderWorkerHandoffWait};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::services::vm_runtime::{RenderWorkerHandoff, RenderWorkerHandoffWait};
|
||||
use prometeu_hal::{
|
||||
FrameId, OwnedRgba8888Frame, RenderSubmission, RenderWorkerError, RenderWorkerTelemetry,
|
||||
FrameId, RenderWorkerBackend, RenderWorkerError, RenderWorkerFrameSink, RenderWorkerTelemetry,
|
||||
};
|
||||
use std::panic::{AssertUnwindSafe, catch_unwind};
|
||||
use std::sync::mpsc::{self, Receiver, RecvTimeoutError};
|
||||
@ -8,17 +8,6 @@ use std::sync::{Arc, Mutex};
|
||||
use std::thread::{self, JoinHandle};
|
||||
use std::time::Duration;
|
||||
|
||||
pub trait RenderWorkerBackend: Send + 'static {
|
||||
fn render(
|
||||
&self,
|
||||
submission: &RenderSubmission,
|
||||
) -> Result<OwnedRgba8888Frame, RenderWorkerError>;
|
||||
}
|
||||
|
||||
pub trait RenderWorkerFrameSink: Send + 'static {
|
||||
fn publish(&self, frame: OwnedRgba8888Frame) -> Result<(), RenderWorkerError>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct RenderWorkerConfig {
|
||||
pub shutdown_timeout: Duration,
|
||||
@ -173,20 +162,23 @@ mod tests {
|
||||
use crate::services::vm_runtime::render_worker_test_harness::{
|
||||
FakeRenderBackend, RenderGate, game_submission,
|
||||
};
|
||||
use prometeu_hal::{FrameId, OwnedRgba8888Frame};
|
||||
use prometeu_hal::{FrameId, OwnedRgba8888Frame, RenderSubmission};
|
||||
|
||||
impl RenderWorkerBackend for Arc<FakeRenderBackend> {
|
||||
#[derive(Clone, Debug)]
|
||||
struct SharedFakeBackend(Arc<FakeRenderBackend>);
|
||||
|
||||
impl RenderWorkerBackend for SharedFakeBackend {
|
||||
fn render(
|
||||
&self,
|
||||
submission: &RenderSubmission,
|
||||
) -> Result<OwnedRgba8888Frame, RenderWorkerError> {
|
||||
FakeRenderBackend::render(self, submission)
|
||||
self.0.render(submission)
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderWorkerFrameSink for Arc<FakeRenderBackend> {
|
||||
impl RenderWorkerFrameSink for SharedFakeBackend {
|
||||
fn publish(&self, frame: OwnedRgba8888Frame) -> Result<(), RenderWorkerError> {
|
||||
FakeRenderBackend::publish(self, frame);
|
||||
self.0.publish(frame);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -219,8 +211,8 @@ mod tests {
|
||||
let mut controller = RenderWorkerController::start(
|
||||
RenderWorkerConfig::default(),
|
||||
Arc::clone(&handoff),
|
||||
Arc::clone(&backend),
|
||||
backend,
|
||||
SharedFakeBackend(Arc::clone(&backend)),
|
||||
SharedFakeBackend(backend),
|
||||
);
|
||||
|
||||
assert_eq!(controller.stop(), Ok(()));
|
||||
@ -234,8 +226,8 @@ mod tests {
|
||||
let mut controller = RenderWorkerController::start(
|
||||
RenderWorkerConfig::default(),
|
||||
Arc::clone(&handoff),
|
||||
Arc::clone(&backend),
|
||||
Arc::clone(&backend),
|
||||
SharedFakeBackend(Arc::clone(&backend)),
|
||||
SharedFakeBackend(Arc::clone(&backend)),
|
||||
);
|
||||
|
||||
handoff.publish(game_submission(11, 1, 1));
|
||||
@ -255,8 +247,8 @@ mod tests {
|
||||
let mut controller = RenderWorkerController::start(
|
||||
RenderWorkerConfig { shutdown_timeout: Duration::from_millis(1) },
|
||||
Arc::clone(&handoff),
|
||||
Arc::clone(&backend),
|
||||
Arc::clone(&backend),
|
||||
SharedFakeBackend(Arc::clone(&backend)),
|
||||
SharedFakeBackend(Arc::clone(&backend)),
|
||||
);
|
||||
|
||||
handoff.publish(game_submission(12, 1, 1));
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -2,7 +2,7 @@
|
||||
id: PLN-0116
|
||||
ticket: real-render-worker-establishment
|
||||
title: Implement Fake Local Render Backend
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-06-15
|
||||
completed:
|
||||
ref_decisions: [DEC-0033]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user