From cf8b77a281d1e6c62a6aa6c6e00eb2b14ab21b25 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Sun, 24 Nov 2024 09:12:47 -0800 Subject: [PATCH] render: Report if anything changed in `RtRenderer` and `UpdatingSpaceRaytracer`. This will allow their clients to decide whether to actually re-trace the scene. --- all-is-cubes-render/src/raytracer/renderer.rs | 23 +++++++++++----- all-is-cubes/src/raytracer/updating.rs | 27 ++++++++++++------- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/all-is-cubes-render/src/raytracer/renderer.rs b/all-is-cubes-render/src/raytracer/renderer.rs index 039facc34..39eafc88c 100644 --- a/all-is-cubes-render/src/raytracer/renderer.rs +++ b/all-is-cubes-render/src/raytracer/renderer.rs @@ -76,16 +76,20 @@ where /// Update the renderer's internal copy of the scene from the data sources /// (`Handle` etc.) it is tracking. /// + /// On success, returns whether any of the scene actually changed. + /// /// Returns [`RenderError::Read`] if said sources are in use. /// In that case, the renderer is still functional but will have stale data. /// /// This method is equivalent to [`HeadlessRenderer::update()`] except for /// fitting the raytracer's needs and capabilities (works with all types; /// not `async`). - pub fn update(&mut self, cursor: Option<&Cursor>) -> Result<(), RenderError> { + pub fn update(&mut self, cursor: Option<&Cursor>) -> Result { + let mut anything_changed = false; + // TODO: raytracer needs to implement drawing the cursor self.had_cursor = cursor.is_some(); - self.cameras.update(); + anything_changed |= self.cameras.update(); self.custom_options_cache = self.custom_options.get(); fn sync_space( @@ -93,6 +97,7 @@ where optional_space: Option<&Handle>, graphics_options_source: &ListenableSource, custom_options_source: &ListenableSource, + anything_changed: &mut bool, ) -> Result<(), RenderError> where D: RtBlockData, @@ -105,18 +110,19 @@ where (Some(space), Some(rt)) if space == rt.space() => {} // Needs replacement (Some(space), rt) => { + *anything_changed = true; *rt = Some(UpdatingSpaceRaytracer::new( space.clone(), graphics_options_source.clone(), custom_options_source.clone(), - )) + )); } // Space is None, so drop raytracer if any (None, c) => *c = None, } // Now that we have one if we should have one, update it. if let Some(rt) = cached_rt { - rt.update().map_err(RenderError::Read)?; + *anything_changed |= rt.update().map_err(RenderError::Read)?; } Ok(()) } @@ -126,15 +132,17 @@ where Option::as_ref(&self.cameras.world_space().get()), &gs, &self.custom_options, + &mut anything_changed, )?; sync_space( &mut self.rts.ui, self.cameras.ui_space(), &gs, &self.custom_options, + &mut anything_changed, )?; - Ok(()) + Ok(anything_changed) } /// Produce an image of the current state of the scene this renderer was created to @@ -298,7 +306,10 @@ impl HeadlessRenderer for RtRenderer<()> { &'a mut self, cursor: Option<&'a Cursor>, ) -> futures_core::future::BoxFuture<'a, Result<(), RenderError>> { - Box::pin(async move { self.update(cursor) }) + Box::pin(async move { + let _anything_changed = self.update(cursor)?; + Ok(()) + }) } fn draw<'a>( diff --git a/all-is-cubes/src/raytracer/updating.rs b/all-is-cubes/src/raytracer/updating.rs index 1cbef3c77..16c89b69a 100644 --- a/all-is-cubes/src/raytracer/updating.rs +++ b/all-is-cubes/src/raytracer/updating.rs @@ -104,8 +104,10 @@ where /// Reads the previously provided [`Space`] and updates the local copy of its contents. /// - /// Returns an error if reading fails. - pub fn update(&mut self) -> Result<(), HandleError> { + /// On success, returns whether any of the scene actually changed. + /// + /// Returns an error if reading the [`Space`] fails. + pub fn update(&mut self) -> Result { // Deadlock safety note: // If the space is being updated, that will acquire the space's lock and then our // todo's lock for notifications. Therefore, to avoid deadlock we would need to @@ -117,7 +119,7 @@ where let todo: &mut SrtTodo = &mut self.todo.lock().unwrap(); if todo.is_empty() { // Nothing to do - return Ok(()); + return Ok(false); } let space = self.space.read()?; @@ -133,7 +135,12 @@ where ); todo.blocks.clear(); todo.cubes.clear(); + + Ok(true) } else { + let mut anything_changed = false; + + // TODO: need to listen to the options sources for accurate change detection let graphics_options = &*self.graphics_options.get(); let custom_options = &*self.custom_options.get(); let options = RtOptionsRef { @@ -143,6 +150,7 @@ where let block_data_slice = space.block_data(); if block_data_slice.len() > self.state.blocks.len() { + anything_changed = true; for block_data in block_data_slice[self.state.blocks.len()..].iter() { self.state .blocks @@ -150,13 +158,14 @@ where } } for block_index in todo.blocks.drain() { - // TODO: handle extending the vector + anything_changed = true; let block_index = usize::from(block_index); self.state.blocks[block_index] = TracingBlock::from_block(options, &block_data_slice[block_index]); } for cube in todo.cubes.drain() { + anything_changed = true; // TODO: this does 2 cube index calculations instead of the 1 it needs let block_index = space.get_block_index(cube).unwrap_or(0); self.state.cubes[cube] = TracingCubeData { @@ -165,9 +174,9 @@ where always_invisible: block_data_slice[block_index as usize].block() == &AIR, }; } - } - Ok(()) + Ok(anything_changed) + } } } @@ -285,9 +294,9 @@ mod tests { } } - fn update_and_assert(&mut self) -> Result<(), HandleError> { + fn update_and_assert(&mut self) -> Result { self.camera.set_options(self.graphics_options.snapshot()); - self.updating.update()?; + let changed = self.updating.update()?; let image_updating = self .updating .get() @@ -304,7 +313,7 @@ mod tests { assert_eq!(image_updating.refmt(&Unquote), image_fresh.refmt(&Unquote)); print!("{image_updating}"); - Ok(()) + Ok(changed) } }