Skip to content

Commit

Permalink
feat: CommandBuffer::set_dedup
Browse files Browse the repository at this point in the history
  • Loading branch information
ten3roberts committed Sep 4, 2023
1 parent 68674c2 commit f79829f
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 1 deletion.
96 changes: 95 additions & 1 deletion src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand All @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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<T: ComponentValue + PartialEq>(
&mut self,
id: Entity,
component: Component<T>,
value: T,
) -> &mut Self {
let offset = self.inserts.push(value);
unsafe fn cmp<T: PartialEq>(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::<T>,
});

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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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())]);
}
}
37 changes: 37 additions & 0 deletions src/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,43 @@ impl<T: ComponentValue + PartialEq> ComponentPusher for WriteDedup<T> {
}
}

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,
}
Expand Down

0 comments on commit f79829f

Please sign in to comment.