diff --git a/src/vram.rs b/src/vram.rs index ef36634..d260999 100644 --- a/src/vram.rs +++ b/src/vram.rs @@ -40,12 +40,10 @@ impl VramProcessor { &self, renderer: R, ) -> ([VramImageHandle; N], VramParams) { - let handles = renderer.sizes().map(|[width, height]| { - let data = Arc::new(Mutex::new(None)); - VramImageHandle { - size: [width as f32, height as f32], - data, - } + let states = renderer.sizes().map(VramRenderImageState::new); + let handles = states.clone().map(|state| VramImageHandle { + size: state.size.map(|i| i as f32), + data: state.sink, }); let images = renderer .sizes() @@ -55,7 +53,7 @@ impl VramProcessor { renderer, params: Arc::downgrade(&sink), images, - sinks: handles.clone().map(|i| i.data), + states, })); let params = VramParams { value: R::Params::default(), @@ -101,42 +99,35 @@ impl VramProcessorWorker { } } -pub enum VramImage { - Unchanged(Option>), - Changed(ColorImage), +pub struct VramImage { + size: [usize; 2], + shades: Vec, } impl VramImage { pub fn new(width: usize, height: usize) -> Self { - Self::Changed(ColorImage::new([width, height], Color32::BLACK)) - } - - pub fn write(&mut self, coords: (usize, usize), shade: u8) { - match self { - Self::Unchanged(image) => { - if image.as_ref().is_none_or(|i| i[coords].r() == shade) { - return; - } - let Some(mut new_image) = image.take().map(Arc::unwrap_or_clone) else { - return; - }; - new_image[coords] = Color32::from_gray(shade); - *self = Self::Changed(new_image); - } - Self::Changed(image) => { - image[coords] = Color32::from_gray(shade); - } + Self { + size: [width, height], + shades: vec![0; width * height], } } - pub fn take(&mut self) -> Option> { - match self { - Self::Unchanged(_) => None, - Self::Changed(image) => { - let arced = Arc::new(std::mem::take(image)); - *self = Self::Unchanged(Some(arced.clone())); - Some(arced) - } + pub fn write(&mut self, coords: (usize, usize), shade: u8) { + self.shades[coords.1 * self.size[0] + coords.0] = shade; + } + + pub fn changed(&self, image: &ColorImage) -> bool { + image + .pixels + .iter() + .map(|p| p.r()) + .zip(&self.shades) + .any(|(a, b)| a != *b) + } + + pub fn read(&self, image: &mut ColorImage) { + for (pixel, shade) in image.pixels.iter_mut().zip(&self.shades) { + *pixel = Color32::from_gray(*shade); } } } @@ -181,11 +172,47 @@ pub trait VramRenderer: Send { fn render(&mut self, params: &Self::Params, images: &mut [VramImage; N]); } +#[derive(Clone)] +struct VramRenderImageState { + size: [usize; 2], + buffers: [Arc; 2], + last_buffer: usize, + sink: Arc>>>, +} + +impl VramRenderImageState { + fn new(size: [usize; 2]) -> Self { + let buffers = [ + Arc::new(ColorImage::new(size, Color32::BLACK)), + Arc::new(ColorImage::new(size, Color32::BLACK)), + ]; + let sink = buffers[0].clone(); + Self { + size, + buffers, + last_buffer: 0, + sink: Arc::new(Mutex::new(Some(sink))), + } + } + fn try_send_update(&mut self, image: &VramImage) { + let last = &self.buffers[self.last_buffer]; + if !image.changed(last) { + return; + } + + let next_buffer = (self.last_buffer + 1) % self.buffers.len(); + let next = &mut self.buffers[next_buffer]; + image.read(Arc::make_mut(next)); + self.last_buffer = next_buffer; + self.sink.lock().unwrap().replace(next.clone()); + } +} + struct VramRendererWrapper> { renderer: R, params: Weak>, images: [VramImage; N], - sinks: [Arc>>>; N], + states: [VramRenderImageState; N], } trait VramRendererImpl: Send { @@ -203,10 +230,8 @@ impl + Send> VramRendererImpl for VramRendere }; self.renderer.render(¶ms, &mut self.images); - for (image, sink) in self.images.iter_mut().zip(&self.sinks) { - if let Some(update) = image.take() { - sink.lock().unwrap().replace(update); - } + for (state, image) in self.states.iter_mut().zip(&self.images) { + state.try_send_update(image); } Ok(()) }