1//! [`Renderer`] implementation that delegates to another thread to perform the actual rendering 2//! 3//! When [`ThreadedRenderer::render_frame`] is called, blocks until the other thread acknowledges 4//! that it has completed rendering 5 6use jgenesis_common::frontend::{Color, FrameSize, RenderFrameOptions, Renderer}; 7use std::slice; 8use std::sync::mpsc; 9use std::sync::mpsc::{Receiver, RecvTimeoutError, SyncSender}; 10use std::time::Duration; 11use thiserror::Error; 12 13struct FrameMessage { 14 frame_buffer: *const Color, 15 frame_buffer_len: usize, 16 frame_size: FrameSize, 17 target_fps: f64, 18 options: RenderFrameOptions, 19} 20 21// SAFETY: This pointer-containing struct is only sent across threads in one place in this module, 22// and the struct is private so it cannot be used outside of this module 23unsafe impl Send for FrameMessage {} 24 25pub type DoneMessage = Result<(), ()>; 26 27#[derive(Debug, Error)] 28pub enum ThreadedRendererError { 29 #[error("Invalid frame buffer length {len} for size {width}x{height}")] 30 InvalidFrameBufferLen { len: usize, width: u32, height: u32 }, 31 #[error("Lost connection to main thread")] 32 LostConnection, 33 #[error("Error from underlying renderer")] 34 Render, 35} 36 37pub struct ThreadedRenderer { 38 frame_sender: SyncSender<FrameMessage>, 39 done_receiver: Receiver<DoneMessage>, 40} 41 42pub struct ThreadedRendererHandle { 43 frame_receiver: Receiver<FrameMessage>, 44 done_sender: SyncSender<DoneMessage>, 45} 46 47impl ThreadedRenderer { 48 pub fn new() -> (Self, ThreadedRendererHandle) { 49 let (frame_sender, frame_receiver) = mpsc::sync_channel(1); 50 let (done_sender, done_receiver) = mpsc::sync_channel(1); 51 52 let renderer = Self { frame_sender, done_receiver }; 53 54 let handle = ThreadedRendererHandle { frame_receiver, done_sender }; 55 56 (renderer, handle) 57 } 58} 59 60impl Renderer for ThreadedRenderer { 61 type Err = ThreadedRendererError; 62 63 fn render_frame( 64 &mut self, 65 frame_buffer: &[Color], 66 frame_size: FrameSize, 67 target_fps: f64, 68 options: RenderFrameOptions, 69 ) -> Result<(), Self::Err> { 70 let frame_len = frame_size.len() as usize; 71 if frame_len > frame_buffer.len() { 72 return Err(ThreadedRendererError::InvalidFrameBufferLen { 73 len: frame_len, 74 width: frame_size.width, 75 height: frame_size.height, 76 }); 77 } 78 79 // SAFETY: This sends a frame buffer raw pointer to the main thread. This function must not 80 // return before the main thread has signaled that it is no longer using the frame buffer 81 let frame_message = FrameMessage { 82 frame_buffer: frame_buffer.as_ptr(), 83 frame_buffer_len: frame_buffer.len(), 84 frame_size, 85 target_fps, 86 options, 87 }; 88 if self.frame_sender.send(frame_message).is_err() { 89 return Err(ThreadedRendererError::LostConnection); 90 } 91 92 match self.done_receiver.recv() { 93 Ok(Ok(())) => {} 94 Ok(Err(())) => return Err(ThreadedRendererError::Render), 95 Err(_) => return Err(ThreadedRendererError::LostConnection), 96 } 97 98 Ok(()) 99 } 100} 101 102#[derive(Debug, Error)] 103pub enum RecvFrameError<RErr> { 104 #[error("recv error: {0}")] 105 Recv(#[from] RecvTimeoutError), 106 #[error("lost connection to other thread")] 107 LostConnection, 108 #[error("renderer error: {0}")] 109 Render(RErr), 110} 111 112impl ThreadedRendererHandle { 113 pub fn try_recv_frame<R: Renderer>( 114 &self, 115 renderer: &mut R, 116 timeout: Duration, 117 ) -> Result<(), RecvFrameError<R::Err>> { 118 let frame_message = self.frame_receiver.recv_timeout(timeout)?; 119 120 // SAFETY: The slice is reconstructed from raw parts sent by the runner thread. The main 121 // thread must not use the slice after it sends the done signal to the runner thread 122 unsafe { 123 let frame_buffer = 124 slice::from_raw_parts(frame_message.frame_buffer, frame_message.frame_buffer_len); 125 126 match renderer.render_frame( 127 frame_buffer, 128 frame_message.frame_size, 129 frame_message.target_fps, 130 frame_message.options, 131 ) { 132 Ok(()) => { 133 self.done_sender.send(Ok(())).map_err(|_| RecvFrameError::LostConnection)?; 134 } 135 Err(err) => { 136 let _ = self.done_sender.send(Err(())); 137 return Err(RecvFrameError::Render(err)); 138 } 139 } 140 } 141 142 Ok(()) 143 } 144}