implements PLN-0119

This commit is contained in:
bQUARKz 2026-06-16 05:44:08 +01:00
parent 6b5b27d068
commit 67a629dbe4
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
8 changed files with 171 additions and 17 deletions

View File

@ -33,6 +33,8 @@ use std::sync::Arc;
/// - **Audio**: Stereo, Command-based mixing.
/// - **Input**: 12-button Digital Gamepad + Absolute Touch/Mouse.
pub struct Hardware {
/// Shared physical memory bank ownership used by peripherals and worker backends.
pub memory_banks: Arc<MemoryBanks>,
/// The Graphics Processing Unit (GPU). Handles drawing primitives, sprites, and tilemaps.
pub gfx: Gfx,
/// Canonical frame orchestration owner for scene/camera/cache/resolver/sprites.
@ -216,6 +218,7 @@ impl Hardware {
);
let glyph_slot_index = assets.glyph_asset_slot_index();
Self {
memory_banks: Arc::clone(&memory_banks),
gfx: Gfx::new(
Self::W,
Self::H,

View File

@ -1,5 +1,7 @@
use crate::asset::GlyphAssetSlotIndex;
use crate::frame_composer::FrameComposer;
use crate::gfx::Gfx;
use crate::memory_banks::GlyphBankPoolAccess;
use crate::memory_banks::{GlyphBankPoolAccess, MemoryBanks, SceneBankPoolAccess};
use prometeu_hal::app_mode::AppMode;
use prometeu_hal::{
OwnedRgba8888Frame, RenderSubmission, RenderSubmissionPacket, RenderWorkerBackend,
@ -9,11 +11,51 @@ use std::sync::{Arc, Mutex};
pub struct LocalFramebufferRenderBackend {
gfx: Mutex<Gfx>,
frame_composer: Mutex<FrameComposer>,
}
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)) }
Self::new_with_parts(
width,
height,
glyph_banks,
Arc::new(MemoryBanks::new()),
GlyphAssetSlotIndex::new(),
)
}
pub fn new_with_memory_banks(
width: usize,
height: usize,
memory_banks: Arc<MemoryBanks>,
glyph_slot_index: GlyphAssetSlotIndex,
) -> Self {
Self::new_with_parts(
width,
height,
Arc::clone(&memory_banks) as Arc<dyn GlyphBankPoolAccess>,
memory_banks,
glyph_slot_index,
)
}
fn new_with_parts(
width: usize,
height: usize,
glyph_banks: Arc<dyn GlyphBankPoolAccess>,
scene_banks: Arc<dyn SceneBankPoolAccess>,
glyph_slot_index: GlyphAssetSlotIndex,
) -> Self {
Self {
gfx: Mutex::new(Gfx::new(width, height, glyph_banks)),
frame_composer: Mutex::new(FrameComposer::new(
width,
height,
scene_banks,
glyph_slot_index,
)),
}
}
pub fn render_submission(
@ -23,7 +65,16 @@ impl LocalFramebufferRenderBackend {
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);
if packet.composer.bound_scene.is_none() {
gfx.render_game2d_frame_packet(packet);
} else {
let mut frame_composer = self
.frame_composer
.lock()
.map_err(|_| RenderWorkerError::InternalFailure)?;
frame_composer.render_game2d_frame_packet(packet, &mut *gfx);
gfx.apply_gfx2d_commands(&packet.gfx2d);
}
}
RenderSubmissionPacket::ShellUi(packet) if submission.app_mode == AppMode::Shell => {
gfx.render_shell_ui_frame_packet(packet);

View File

@ -1,9 +1,13 @@
use crate::CrashReport;
use crate::os::SystemOS;
use crate::{RenderWorkerConfig, RenderWorkerController};
use prometeu_hal::app_mode::AppMode;
use prometeu_hal::cartridge::Cartridge;
use prometeu_hal::telemetry::{CertificationConfig, TelemetryFrame};
use prometeu_hal::{InputSignals, RuntimePlatform};
use prometeu_hal::{
InputSignals, OwnedRgba8888Frame, RenderWorkerBackend, RenderWorkerError,
RenderWorkerFrameSink, RuntimePlatform,
};
use prometeu_vm::VirtualMachine;
use std::sync::atomic::Ordering;
@ -96,6 +100,30 @@ impl<'a> VmFacade<'a> {
self.os.vm_runtime.atomic_telemetry.snapshot()
}
pub fn start_render_worker<B, S>(&mut self, config: RenderWorkerConfig, backend: B, sink: S)
where
B: RenderWorkerBackend,
S: RenderWorkerFrameSink,
{
self.os.vm_runtime.start_render_worker(config, backend, sink);
}
pub fn stop_render_worker(&mut self) -> Result<(), RenderWorkerError> {
self.os.vm_runtime.stop_render_worker()
}
pub fn render_worker_controller(&self) -> Option<&RenderWorkerController> {
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 cert_config(&self) -> &CertificationConfig {
&self.os.vm_runtime.certifier.config
}

View File

@ -3,7 +3,9 @@ use crate::CrashReport;
use crate::fs::{FsBackend, FsState, VirtualFS};
use prometeu_hal::cartridge::Cartridge;
use prometeu_hal::log::{LogLevel, LogSource};
use prometeu_hal::{RenderWorkerBackend, RenderWorkerError, RenderWorkerFrameSink};
use prometeu_hal::{
OwnedRgba8888Frame, RenderWorkerBackend, RenderWorkerError, RenderWorkerFrameSink,
};
use std::collections::HashMap;
impl VirtualMachineRuntime {
@ -193,6 +195,14 @@ impl VirtualMachineRuntime {
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 initialize_vm(
&mut self,
log_service: &mut LogService,

View File

@ -4,14 +4,16 @@ use crate::fs_backend::HostDirBackend;
use crate::input::HostInputHandler;
use crate::log_sink::HostConsoleSink;
use crate::stats::HostStats;
use crate::utilities::draw_rgba8888_to_rgba8;
use crate::utilities::{draw_owned_rgba8888_frame_to_rgba8, draw_rgba8888_to_rgba8};
use pixels::wgpu::PresentMode;
use pixels::{Pixels, PixelsBuilder, SurfaceTexture};
use prometeu_drivers::AudioCommand;
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 std::sync::Arc;
use std::time::{Duration, Instant};
use winit::application::ApplicationHandler;
use winit::dpi::LogicalSize;
@ -22,6 +24,15 @@ 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,
@ -153,8 +164,20 @@ impl HostRunner {
firmware.os.fs().mount(Box::new(backend));
}
let hardware = Hardware::new();
let memory_banks = Arc::new(MemoryBanks::new());
let hardware = Hardware::new_with_memory_banks(Arc::clone(&memory_banks));
let display_size = hardware.display_size();
let worker_backend = LocalFramebufferRenderBackend::new_with_memory_banks(
display_size.0,
display_size.1,
memory_banks,
hardware.assets.glyph_asset_slot_index(),
);
firmware.os.vm().start_render_worker(
RenderWorkerConfig::default(),
worker_backend,
HostWorkerFrameSink,
);
Self {
window: None,
@ -278,10 +301,17 @@ impl ApplicationHandler for HostRunner {
// Mutable borrow of the frame (lasts only within this block)
let frame = pixels.frame_mut();
// Immutable borrow of prometeu-core (different field, ok)
let src = self.hardware.gfx.front_buffer();
draw_rgba8888_to_rgba8(src, frame);
let use_worker_frame =
matches!(self.firmware.state, FirmwareState::GameRunning(_));
if use_worker_frame
&& let Some(worker_frame) =
self.firmware.os.vm().repeat_latest_render_worker_frame()
{
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() {
@ -372,7 +402,14 @@ impl ApplicationHandler for HostRunner {
self.stats.record_frame();
}
self.presentation.note_published_frame(self.firmware.os.frame_index());
let use_worker_frame = matches!(self.firmware.state, FirmwareState::GameRunning(_));
if use_worker_frame
&& let Some(worker_frame) = self.firmware.os.vm().latest_render_worker_frame()
{
self.presentation.note_published_frame(worker_frame.frame_id.get());
} else {
self.presentation.note_published_frame(self.firmware.os.frame_index());
}
if was_debugger_connected != self.debugger.stream.is_some()
|| was_waiting_for_start != self.debugger.waiting_for_start

View File

@ -1,3 +1,5 @@
use prometeu_hal::OwnedRgba8888Frame;
/// Copies RGBA8888 pixels in canonical RGBA raw order into the `pixels` RGBA8 frame.
pub fn draw_rgba8888_to_rgba8(src: &[u32], dst_rgba: &mut [u8]) {
for (i, &px) in src.iter().enumerate() {
@ -9,9 +11,15 @@ pub fn draw_rgba8888_to_rgba8(src: &[u32], dst_rgba: &mut [u8]) {
}
}
pub fn draw_owned_rgba8888_frame_to_rgba8(frame: &OwnedRgba8888Frame, dst_rgba: &mut [u8]) {
draw_rgba8888_to_rgba8(&frame.pixels[..frame.required_pixel_len()], dst_rgba);
}
#[cfg(test)]
mod tests {
use super::draw_rgba8888_to_rgba8;
use super::{draw_owned_rgba8888_frame_to_rgba8, draw_rgba8888_to_rgba8};
use prometeu_hal::app_mode::AppMode;
use prometeu_hal::{FrameId, OwnedRgba8888Frame, RenderOwnership};
#[test]
fn draw_rgba8888_to_rgba8_preserves_channel_order_and_alpha() {
@ -22,4 +30,21 @@ mod tests {
assert_eq!(dst, [0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD, 0xEF, 0x01]);
}
#[test]
fn draw_owned_frame_to_rgba8_uses_owned_rgba8888_pixels() {
let frame = OwnedRgba8888Frame::packed(
FrameId::new(3),
RenderOwnership::new(1, AppMode::Game, 7),
1,
1,
vec![0x10203040],
)
.expect("test frame should be valid");
let mut dst = [0; 4];
draw_owned_rgba8888_frame_to_rgba8(&frame, &mut dst);
assert_eq!(dst, [0x10, 0x20, 0x30, 0x40]);
}
}

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,7 @@
id: PLN-0119
ticket: real-render-worker-establishment
title: Integrate Desktop Host Worker Presentation
status: open
status: done
created: 2026-06-15
completed:
ref_decisions: [DEC-0033]