From f79829f9194091f9115adfcd0204a0009e354582 Mon Sep 17 00:00:00 2001 From: Tei Roberts Date: Mon, 4 Sep 2023 23:41:43 +0200 Subject: [PATCH] feat: CommandBuffer::set_dedup --- src/commands.rs | 96 ++++++++++++++++++++++++++++++++++++++++++++++++- src/writer.rs | 37 +++++++++++++++++++ 2 files changed, 132 insertions(+), 1 deletion(-) diff --git a/src/commands.rs b/src/commands.rs index bc272d7..90d1d55 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -5,7 +5,7 @@ use anyhow::Context; use crate::{ buffer::MultiComponentBuffer, - writer::{MissingDyn, SingleComponentWriter}, + writer::{MissingDyn, SingleComponentWriter, WriteDedupDyn}, BatchSpawn, Component, ComponentDesc, ComponentValue, Entity, EntityBuilder, World, }; @@ -26,6 +26,12 @@ enum Command { desc: ComponentDesc, offset: usize, }, + SetDedup { + id: Entity, + desc: ComponentDesc, + offset: usize, + cmp: unsafe fn(*const u8, *const u8) -> bool, + }, SetMissing { id: Entity, desc: ComponentDesc, @@ -61,6 +67,17 @@ impl fmt::Debug for Command { .field("desc", desc) .field("offset", offset) .finish(), + Self::SetDedup { + id, + desc, + offset, + cmp: _, + } => f + .debug_struct("SetDedup") + .field("id", id) + .field("desc", desc) + .field("offset", offset) + .finish(), Self::SetMissing { id, desc, offset } => f .debug_struct("SetMissing") .field("id", id) @@ -125,6 +142,32 @@ impl CommandBuffer { self } + /// Set a component for `id`. + /// + /// Does not trigger a modification event if the value is the same + pub fn set_dedup( + &mut self, + id: Entity, + component: Component, + value: T, + ) -> &mut Self { + let offset = self.inserts.push(value); + unsafe fn cmp(a: *const u8, b: *const u8) -> bool { + let a = &*(a as *const T); + let b = &*(b as *const T); + + a == b + } + self.commands.push(Command::SetDedup { + id, + desc: component.desc(), + offset, + cmp: cmp::, + }); + + self + } + /// Set a component for `id` if it does not exist when the commandbuffer is applied. /// /// This avoid accidentally overwriting a component that was added by another system. @@ -243,6 +286,21 @@ impl CommandBuffer { .map_err(|v| v.into_anyhow()) .with_context(|| format!("Failed to set component {}", desc.name()))?; }, + Command::SetDedup { + id, + desc, + offset, + cmp, + } => unsafe { + let value = self.inserts.take_dyn(offset); + world + .set_with_writer( + id, + SingleComponentWriter::new(desc, WriteDedupDyn { value, cmp }), + ) + .map_err(|v| v.into_anyhow()) + .with_context(|| format!("Failed to set component {}", desc.name()))?; + }, Command::SetMissing { id, desc, offset } => unsafe { let value = self.inserts.take_dyn(offset); world @@ -312,4 +370,40 @@ mod tests { assert_eq!(query.collect_vec(&world), [(false, "Foo".to_string())]); } + + #[test] + fn set_dedup() { + use alloc::string::String; + use alloc::string::ToString; + + component! { + a: String, + } + + let mut world = World::new(); + let mut cmd = CommandBuffer::new(); + + let mut query = Query::new((a().modified().satisfied(), a().cloned())); + + let id = EntityBuilder::new().spawn(&mut world); + + assert!(query.collect_vec(&world).is_empty()); + + cmd.set_dedup(id, a(), "Foo".into()) + .set_dedup(id, a(), "Bar".into()); + + cmd.apply(&mut world).unwrap(); + + assert_eq!(query.collect_vec(&world), [(true, "Bar".to_string())]); + assert_eq!(query.collect_vec(&world), [(false, "Bar".to_string())]); + + cmd.set_dedup(id, a(), "Baz".into()); + cmd.apply(&mut world).unwrap(); + + assert_eq!(query.collect_vec(&world), [(true, "Baz".to_string())]); + + cmd.set_dedup(id, a(), "Baz".into()); + cmd.apply(&mut world).unwrap(); + assert_eq!(query.collect_vec(&world), [(false, "Baz".to_string())]); + } } diff --git a/src/writer.rs b/src/writer.rs index e1c7fe2..190eb8c 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -282,6 +282,43 @@ impl ComponentPusher for WriteDedup { } } +pub(crate) struct WriteDedupDyn { + pub(crate) value: *mut u8, + pub(crate) cmp: unsafe fn(*const u8, *const u8) -> bool, +} + +impl ComponentUpdater for WriteDedupDyn { + type Updated = (); + + unsafe fn update(self, data: &mut CellData, slot: Slot, id: Entity, tick: u32) { + let desc = data.storage.desc(); + unsafe { + let dst = data.storage.at_mut(slot).unwrap(); + + if (self.cmp)(self.value, dst) { + return; + } + + desc.drop(dst); + + ptr::copy_nonoverlapping(self.value, dst, desc.size()); + } + + data.set_modified(&[id], Slice::single(slot), tick); + } +} + +impl ComponentPusher for WriteDedupDyn { + type Pushed = (); + + unsafe fn push(self, data: &mut CellData, id: Entity, tick: u32) { + let slot = data.storage.len(); + data.storage.extend(self.value, 1); + + data.set_added(&[id], Slice::single(slot), tick); + } +} + pub(crate) struct ReplaceDyn { pub(crate) value: *mut u8, }