diff --git a/Cargo.toml b/Cargo.toml index 00e4a56a..0be6ee4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ serde = { version = "1.0", features = ["derive"] } rayon = "1.3" legion = "0.3" bevy_ecs = "0.3" -hecs = "0.3" +hecs = { version = "0.5", features = ["column-serialize", "row-serialize"] } shipyard = "0.4" specs = {version = "0.16.1", features = ["serde"] } specs-derive = "0.4.1" diff --git a/benches/benchmarks.rs b/benches/benchmarks.rs index c54df5d0..b31544dd 100644 --- a/benches/benchmarks.rs +++ b/benches/benchmarks.rs @@ -171,6 +171,10 @@ fn bench_serialize_text(c: &mut Criterion) { let mut bench = legion::serialize_text::Benchmark::new(); b.iter(move || bench.run()); }); + group.bench_function("hecs", |b| { + let mut bench = hecs::serialize_text::Benchmark::new(); + b.iter(move || bench.run()); + }); // group.bench_function("bevy", |b| { // let mut bench = bevy::serialize_text::Benchmark::new(); // b.iter(move || bench.run()); @@ -183,6 +187,10 @@ fn bench_serialize_binary(c: &mut Criterion) { let mut bench = legion::serialize_binary::Benchmark::new(); b.iter(move || bench.run()); }); + group.bench_function("hecs", |b| { + let mut bench = hecs::serialize_binary::Benchmark::new(); + b.iter(move || bench.run()); + }); // group.bench_function("bevy", |b| { // let mut bench = bevy::serialize_text::Benchmark::new(); // b.iter(move || bench.run()); diff --git a/src/hecs/mod.rs b/src/hecs/mod.rs index f5b01289..efa2a923 100644 --- a/src/hecs/mod.rs +++ b/src/hecs/mod.rs @@ -3,3 +3,5 @@ pub mod frag_iter; pub mod heavy_compute; pub mod simple_insert; pub mod simple_iter; +pub mod serialize_binary; +pub mod serialize_text; \ No newline at end of file diff --git a/src/hecs/serialize_binary.rs b/src/hecs/serialize_binary.rs new file mode 100644 index 00000000..f3aec7f4 --- /dev/null +++ b/src/hecs/serialize_binary.rs @@ -0,0 +1,180 @@ +use hecs::{serialize::column::*, *}; +use serde::{de::SeqAccess, ser::SerializeTuple, Deserialize, Serialize}; + +#[derive(Default, Copy, Clone, Serialize, Deserialize)] +struct Transform([f32; 16]); + +#[derive(Default, Copy, Clone, Serialize, Deserialize)] +struct Position { + x: f32, + y: f32, + z: f32, +} + +#[derive(Default, Copy, Clone, Serialize, Deserialize)] +struct Rotation { + x: f32, + y: f32, + z: f32, +} + +#[derive(Default, Copy, Clone, Serialize, Deserialize)] +struct Velocity { + x: f32, + y: f32, + z: f32, +} + +struct SerContext; + +#[derive(Serialize, Deserialize, Debug, Copy, Clone)] +enum ComponentId { + Transform, + Position, + Rotation, + Velocity, +} + +impl SerializeContext for SerContext { + fn component_count(&self, archetype: &Archetype) -> usize { + use std::any::TypeId; + archetype + .component_types() + .filter(|&x| { + x == TypeId::of::() + || x == TypeId::of::() + || x == TypeId::of::() + || x == TypeId::of::() + }) + .count() + } + + fn serialize_component_ids( + &mut self, + archetype: &Archetype, + out: &mut S, + ) -> Result<(), S::Error> { + if archetype.has::() { + out.serialize_element(&ComponentId::Transform)?; + } + if archetype.has::() { + out.serialize_element(&ComponentId::Position)?; + } + if archetype.has::() { + out.serialize_element(&ComponentId::Rotation)?; + } + if archetype.has::() { + out.serialize_element(&ComponentId::Velocity)?; + } + Ok(()) + } + + fn serialize_components( + &mut self, + archetype: &Archetype, + out: &mut S, + ) -> Result<(), S::Error> { + try_serialize::(archetype, out)?; + try_serialize::(archetype, out)?; + try_serialize::(archetype, out)?; + try_serialize::(archetype, out)?; + Ok(()) + } +} + +struct DeContext { + components: Vec, +} + +impl DeserializeContext for DeContext { + fn deserialize_component_ids<'de, A>(&mut self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + self.components.clear(); + let mut batch = ColumnBatchType::new(); + while let Some(id) = seq.next_element()? { + match id { + ComponentId::Transform => { + batch.add::(); + } + ComponentId::Position => { + batch.add::(); + } + ComponentId::Rotation => { + batch.add::(); + } + ComponentId::Velocity => { + batch.add::(); + } + } + self.components.push(id); + } + Ok(batch) + } + + fn deserialize_components<'de, A>( + &mut self, + entity_count: u32, + mut seq: A, + batch: &mut ColumnBatchBuilder, + ) -> Result<(), A::Error> + where + A: SeqAccess<'de>, + { + for &component in &self.components { + match component { + ComponentId::Transform => { + deserialize_column::(entity_count, &mut seq, batch)?; + } + ComponentId::Position => { + deserialize_column::(entity_count, &mut seq, batch)?; + } + ComponentId::Rotation => { + deserialize_column::(entity_count, &mut seq, batch)?; + } + ComponentId::Velocity => { + deserialize_column::(entity_count, &mut seq, batch)?; + } + } + } + Ok(()) + } +} + +pub struct Benchmark(World); + +impl Benchmark { + pub fn new() -> Self { + let mut world = World::new(); + + world.spawn_batch((0..1000).map(|_| { + ( + Transform::default(), + Position::default(), + Rotation::default(), + Velocity::default(), + ) + })); + + Self(world) + } + + pub fn run(&mut self) { + let Self(world) = self; + let mut encoded = Vec::new(); + serialize( + &world, + &mut SerContext, + &mut bincode::Serializer::new(&mut encoded, bincode::options()), + ) + .unwrap(); + deserialize( + &mut DeContext { + components: Vec::new(), + }, + &mut bincode::Deserializer::from_slice(&encoded, bincode::options()), + ) + .unwrap(); + } +} diff --git a/src/hecs/serialize_text.rs b/src/hecs/serialize_text.rs new file mode 100644 index 00000000..61240fc8 --- /dev/null +++ b/src/hecs/serialize_text.rs @@ -0,0 +1,116 @@ +use hecs::{serialize::row::*, *}; +use serde::{de::MapAccess, ser::SerializeMap, Deserialize, Serialize}; + +#[derive(Default, Copy, Clone, Serialize, Deserialize)] +struct Transform([f32; 16]); + +#[derive(Default, Copy, Clone, Serialize, Deserialize)] +struct Position { + x: f32, + y: f32, + z: f32, +} + +#[derive(Default, Copy, Clone, Serialize, Deserialize)] +struct Rotation { + x: f32, + y: f32, + z: f32, +} + +#[derive(Default, Copy, Clone, Serialize, Deserialize)] +struct Velocity { + x: f32, + y: f32, + z: f32, +} + +struct SerContext; + +#[derive(Serialize, Deserialize, Debug, Copy, Clone)] +enum ComponentId { + Transform, + Position, + Rotation, + Velocity, +} + +impl SerializeContext for SerContext { + fn serialize_entity( + &mut self, + entity: EntityRef<'_>, + map: &mut S, + ) -> Result<(), S::Error> { + try_serialize::(&entity, &ComponentId::Transform, map)?; + try_serialize::(&entity, &ComponentId::Position, map)?; + try_serialize::(&entity, &ComponentId::Rotation, map)?; + try_serialize::(&entity, &ComponentId::Velocity, map)?; + Ok(()) + } +} + +struct DeContext; + +impl DeserializeContext for DeContext { + fn deserialize_entity<'de, M>( + &mut self, + mut map: M, + entity: &mut EntityBuilder, + ) -> Result<(), M::Error> + where + M: MapAccess<'de>, + { + while let Some(key) = map.next_key()? { + match key { + ComponentId::Transform => { + entity.add::(map.next_value()?); + } + ComponentId::Position => { + entity.add::(map.next_value()?); + } + ComponentId::Rotation => { + entity.add::(map.next_value()?); + } + ComponentId::Velocity => { + entity.add::(map.next_value()?); + } + } + } + Ok(()) + } +} + +pub struct Benchmark(World); + +impl Benchmark { + pub fn new() -> Self { + let mut world = World::new(); + + world.spawn_batch((0..1000).map(|_| { + ( + Transform::default(), + Position::default(), + Rotation::default(), + Velocity::default(), + ) + })); + + Self(world) + } + + pub fn run(&mut self) { + let Self(world) = self; + let mut encoded = Vec::new(); + serialize( + &world, + &mut SerContext, + &mut ron::Serializer::new(&mut encoded, None, false).unwrap(), + ) + .unwrap(); + deserialize( + &mut DeContext, + &mut ron::Deserializer::from_bytes(&encoded).unwrap(), + ) + .unwrap(); + } +}