implements PLN-0117
This commit is contained in:
parent
0c6a669e6e
commit
cfb5b1ac89
@ -112,7 +112,7 @@ fn matrix_packet_policy_and_pacing_are_mode_specific() {
|
||||
assert_eq!(game_policy.execution, RenderExecutionPolicy::WorkerCapableWithLocalFallback);
|
||||
assert_eq!(
|
||||
game_policy.resolve_consumer_path(RenderRuntimeCapabilities { game_render_worker: true }),
|
||||
RenderConsumerPath::LocalWorkerPrototype
|
||||
RenderConsumerPath::RealWorker
|
||||
);
|
||||
|
||||
let shell_policy = RenderPolicy::for_app_mode(AppMode::Shell);
|
||||
@ -163,7 +163,7 @@ fn matrix_sync_and_worker_paths_share_latest_wins_handoff_contract() {
|
||||
|
||||
let outcome = match path {
|
||||
RenderConsumerPath::LocalSynchronous => manager.consume_latest(&mut surface),
|
||||
RenderConsumerPath::LocalWorkerPrototype => {
|
||||
RenderConsumerPath::RealWorker => {
|
||||
let mut worker = LocalRenderWorker;
|
||||
worker.consume_latest(&mut manager, &mut surface)
|
||||
}
|
||||
@ -172,7 +172,7 @@ fn matrix_sync_and_worker_paths_share_latest_wins_handoff_contract() {
|
||||
(outcome, surface.seen, manager.render_telemetry().replaced_before_consume)
|
||||
}
|
||||
|
||||
for path in [RenderConsumerPath::LocalSynchronous, RenderConsumerPath::LocalWorkerPrototype] {
|
||||
for path in [RenderConsumerPath::LocalSynchronous, RenderConsumerPath::RealWorker] {
|
||||
let (outcome, seen, replaced) = consume_with(path);
|
||||
assert_eq!(outcome, RenderConsumeOutcome::Presented { frame_id: FrameId::new(1) });
|
||||
assert_eq!(seen, vec![FrameId::new(1)]);
|
||||
|
||||
@ -3,6 +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::{RenderWorkerBackend, RenderWorkerError, RenderWorkerFrameSink};
|
||||
use std::collections::HashMap;
|
||||
|
||||
impl VirtualMachineRuntime {
|
||||
@ -25,6 +26,8 @@ impl VirtualMachineRuntime {
|
||||
frame_scheduler: FrameScheduler::default(),
|
||||
render_capabilities: RenderRuntimeCapabilities::default(),
|
||||
render_manager: RenderManager::new(AppMode::Game),
|
||||
render_worker_handoff: Arc::new(RenderWorkerHandoff::default()),
|
||||
render_worker_controller: None,
|
||||
gfx2d_commands: Vec::new(),
|
||||
gfxui_commands: Vec::new(),
|
||||
logs_written_this_frame: HashMap::new(),
|
||||
@ -124,6 +127,8 @@ impl VirtualMachineRuntime {
|
||||
self.frame_scheduler = FrameScheduler::default();
|
||||
self.render_capabilities = RenderRuntimeCapabilities::default();
|
||||
self.render_manager = RenderManager::new(AppMode::Game);
|
||||
let _ = self.stop_render_worker();
|
||||
self.render_worker_handoff = Arc::new(RenderWorkerHandoff::default());
|
||||
self.gfx2d_commands.clear();
|
||||
self.gfxui_commands.clear();
|
||||
self.logs_written_this_frame.clear();
|
||||
@ -147,6 +152,38 @@ impl VirtualMachineRuntime {
|
||||
self.render_capabilities = capabilities;
|
||||
}
|
||||
|
||||
pub fn start_render_worker<B, S>(&mut self, config: RenderWorkerConfig, backend: B, sink: S)
|
||||
where
|
||||
B: RenderWorkerBackend,
|
||||
S: RenderWorkerFrameSink,
|
||||
{
|
||||
let _ = self.stop_render_worker();
|
||||
self.render_worker_handoff = Arc::new(RenderWorkerHandoff::default());
|
||||
self.render_worker_controller = Some(RenderWorkerController::start(
|
||||
config,
|
||||
Arc::clone(&self.render_worker_handoff),
|
||||
backend,
|
||||
sink,
|
||||
));
|
||||
self.render_capabilities.game_render_worker = true;
|
||||
}
|
||||
|
||||
pub fn stop_render_worker(&mut self) -> Result<(), RenderWorkerError> {
|
||||
if let Some(mut controller) = self.render_worker_controller.take() {
|
||||
controller.stop()
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
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 fn initialize_vm(
|
||||
&mut self,
|
||||
log_service: &mut LogService,
|
||||
|
||||
@ -41,6 +41,8 @@ pub struct VirtualMachineRuntime {
|
||||
pub frame_scheduler: FrameScheduler,
|
||||
pub render_capabilities: RenderRuntimeCapabilities,
|
||||
pub render_manager: RenderManager,
|
||||
pub render_worker_handoff: Arc<RenderWorkerHandoff>,
|
||||
pub render_worker_controller: Option<RenderWorkerController>,
|
||||
pub gfx2d_commands: Vec<Gfx2dCommand>,
|
||||
pub gfxui_commands: Vec<GfxUiCommand>,
|
||||
pub logs_written_this_frame: HashMap<u32, u32>,
|
||||
|
||||
@ -59,7 +59,7 @@ pub struct RenderRuntimeCapabilities {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum RenderConsumerPath {
|
||||
LocalSynchronous,
|
||||
LocalWorkerPrototype,
|
||||
RealWorker,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@ -95,7 +95,7 @@ impl RenderPolicy {
|
||||
) -> RenderConsumerPath {
|
||||
match (self.execution, capabilities.game_render_worker) {
|
||||
(RenderExecutionPolicy::WorkerCapableWithLocalFallback, true) => {
|
||||
RenderConsumerPath::LocalWorkerPrototype
|
||||
RenderConsumerPath::RealWorker
|
||||
}
|
||||
_ => RenderConsumerPath::LocalSynchronous,
|
||||
}
|
||||
@ -107,8 +107,10 @@ pub trait RenderSurface {
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[cfg(test)]
|
||||
pub struct LocalRenderWorker;
|
||||
|
||||
#[cfg(test)]
|
||||
impl LocalRenderWorker {
|
||||
pub fn consume_latest<S: RenderSurface>(
|
||||
&mut self,
|
||||
@ -329,6 +331,12 @@ impl RenderManager {
|
||||
discarded
|
||||
}
|
||||
|
||||
pub fn take_pending_for_worker(&mut self) -> Option<RenderSubmission> {
|
||||
let submission = self.handoff.take_latest()?;
|
||||
self.telemetry.record_consumed(submission.frame_id);
|
||||
Some(submission)
|
||||
}
|
||||
|
||||
pub fn request_shutdown(&mut self) -> Option<RenderSubmission> {
|
||||
self.consumer_state = RenderConsumerState::Stopped;
|
||||
self.discard_pending_submission()
|
||||
@ -624,7 +632,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
policy.resolve_consumer_path(RenderRuntimeCapabilities { game_render_worker: true }),
|
||||
RenderConsumerPath::LocalWorkerPrototype
|
||||
RenderConsumerPath::RealWorker
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -107,6 +107,14 @@ impl RenderWorkerController {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RenderWorkerController {
|
||||
fn drop(&mut self) {
|
||||
if self.handle.is_some() {
|
||||
let _ = self.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_worker_loop<B, S>(
|
||||
handoff: Arc<RenderWorkerHandoff>,
|
||||
state: Arc<Mutex<RenderWorkerState>>,
|
||||
@ -159,10 +167,11 @@ fn record_error(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::services::vm_runtime::VirtualMachineRuntime;
|
||||
use crate::services::vm_runtime::render_worker_test_harness::{
|
||||
FakeRenderBackend, RenderGate, game_submission,
|
||||
};
|
||||
use prometeu_hal::{FrameId, OwnedRgba8888Frame, RenderSubmission};
|
||||
use prometeu_hal::{FrameId, Game2DFramePacket, OwnedRgba8888Frame, RenderSubmission};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct SharedFakeBackend(Arc<FakeRenderBackend>);
|
||||
@ -280,4 +289,52 @@ mod tests {
|
||||
assert_eq!(controller.telemetry().worker_panics, 1);
|
||||
assert_eq!(controller.telemetry().last_error_frame_id, 13);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_publishes_game_submissions_to_worker_without_waiting_for_render() {
|
||||
let gate = RenderGate::default();
|
||||
let backend = Arc::new(FakeRenderBackend::with_gate(gate.clone()));
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
runtime.start_render_worker(
|
||||
RenderWorkerConfig::default(),
|
||||
SharedFakeBackend(Arc::clone(&backend)),
|
||||
SharedFakeBackend(Arc::clone(&backend)),
|
||||
);
|
||||
|
||||
runtime
|
||||
.render_manager
|
||||
.close_frame_with_packet(prometeu_hal::RenderSubmissionPacket::Game2D(
|
||||
Game2DFramePacket::default(),
|
||||
))
|
||||
.expect("game packet should close");
|
||||
assert!(runtime.publish_pending_render_to_worker());
|
||||
gate.wait_for_entries(1);
|
||||
|
||||
runtime
|
||||
.render_manager
|
||||
.close_frame_with_packet(prometeu_hal::RenderSubmissionPacket::Game2D(
|
||||
Game2DFramePacket::default(),
|
||||
))
|
||||
.expect("second game packet should close");
|
||||
assert!(runtime.publish_pending_render_to_worker());
|
||||
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_eq!(runtime.render_worker_handoff.telemetry().replaced_before_consume, 1);
|
||||
|
||||
gate.release_one();
|
||||
gate.wait_for_entries(2);
|
||||
gate.release_one();
|
||||
|
||||
assert_eq!(runtime.stop_render_worker(), Ok(()));
|
||||
let frames = backend.published_frames();
|
||||
assert_eq!(frames.len(), 2);
|
||||
assert_eq!(frames[0].frame_id, FrameId::ZERO);
|
||||
assert_eq!(frames[1].frame_id, FrameId::new(2));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
use super::dispatch::VmRuntimeHost;
|
||||
use super::render_manager::{
|
||||
LocalRenderWorker, RenderConsumeOutcome, RenderConsumerPath, RenderSurface,
|
||||
};
|
||||
use super::render_manager::{RenderConsumeOutcome, RenderConsumerPath, RenderSurface};
|
||||
use super::*;
|
||||
use crate::CrashReport;
|
||||
use crate::fs::{FsState, VirtualFS};
|
||||
@ -273,9 +271,18 @@ impl VirtualMachineRuntime {
|
||||
RenderConsumerPath::LocalSynchronous => {
|
||||
self.render_manager.consume_latest(&mut surface)
|
||||
}
|
||||
RenderConsumerPath::LocalWorkerPrototype => {
|
||||
let mut worker = LocalRenderWorker;
|
||||
worker.consume_latest(&mut self.render_manager, &mut surface)
|
||||
RenderConsumerPath::RealWorker
|
||||
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
|
||||
}
|
||||
}
|
||||
RenderConsumerPath::RealWorker => {
|
||||
self.render_manager.consume_latest(&mut surface)
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -2,7 +2,7 @@
|
||||
id: PLN-0117
|
||||
ticket: real-render-worker-establishment
|
||||
title: Integrate Runtime Submission With Worker
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-06-15
|
||||
completed:
|
||||
ref_decisions: [DEC-0033]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user