Skip to content

Commit

Permalink
Use EntityBuilder Instead of RuntimeBundle
Browse files Browse the repository at this point in the history
Gets rid of RuntimeBundle and adds the `add_dynamic` function to
EntityBuilder to serve its purpose.
  • Loading branch information
zicklag committed Oct 20, 2020
1 parent 84f3d3b commit a383a18
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 82 deletions.
30 changes: 0 additions & 30 deletions crates/bevy_ecs/hecs/src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,36 +42,6 @@ pub trait DynamicBundle {
unsafe fn put(self, f: impl FnMut(*mut u8, ComponentId, usize) -> bool);
}

/// A bundle that can be created dynamically at runtime
#[derive(Debug)]
pub struct RuntimeBundle {
/// The information for each component in this bundle
pub components: Vec<TypeInfo>,
/// The raw data for all of the components in the bundle
pub data: Vec<Vec<u8>>,
}

impl DynamicBundle for RuntimeBundle {
fn with_ids<T>(&self, f: impl FnOnce(&[ComponentId]) -> T) -> T {
let ids: Vec<_> = self.components.iter().map(|x| x.id()).collect();
f(ids.as_slice())
}

fn type_info(&self) -> Vec<TypeInfo> {
self.components.clone()
}

unsafe fn put(self, mut f: impl FnMut(*mut u8, ComponentId, usize) -> bool) {
for (mut data, info) in self.data.into_iter().zip(self.components.into_iter()) {
debug_assert_eq!(data.len(), info.layout().size());

if f(data.as_mut_ptr(), info.id(), info.layout().size()) {
std::mem::forget(data);
}
}
}
}

/// A statically typed collection of components
pub trait Bundle: DynamicBundle {
#[doc(hidden)]
Expand Down
43 changes: 28 additions & 15 deletions crates/bevy_ecs/hecs/src/entity_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,8 @@ use crate::{
};

use bevy_utils::HashSet;
use core::{
any::TypeId,
mem::{self, MaybeUninit},
ptr,
};
use core::{intrinsics::copy_nonoverlapping, mem::MaybeUninit, ptr};
use ptr::slice_from_raw_parts;

use crate::{archetype::TypeInfo, Component, DynamicBundle};

Expand Down Expand Up @@ -68,24 +65,40 @@ impl EntityBuilder {

/// Add `component` to the entity
pub fn add<T: Component>(&mut self, component: T) -> &mut Self {
if !self.id_set.insert(TypeId::of::<T>().into()) {
self.add_dynamic(TypeInfo::of::<T>(), unsafe {
&*slice_from_raw_parts(
&component as *const T as *const u8,
std::mem::size_of::<T>(),
)
});
std::mem::forget(component);
self
}

/// Add a dynamic component to the entity
///
/// You must provide the type info and a slice containing the component data
pub fn add_dynamic(&mut self, type_info: TypeInfo, data: &[u8]) -> &mut Self {
debug_assert_eq!(type_info.layout().size(), data.len());

if !self.id_set.insert(type_info.id) {
return self;
}
let end = self.cursor + mem::size_of::<T>();
let end = self.cursor + type_info.layout.size();
if end > self.storage.len() {
self.grow(end);
}
if mem::size_of::<T>() != 0 {
if type_info.layout.size() != 0 {
unsafe {
self.storage
.as_mut_ptr()
.add(self.cursor)
.cast::<T>()
.write_unaligned(component);
copy_nonoverlapping(
data.as_ptr(),
self.storage.as_mut_ptr().add(self.cursor) as *mut u8,
data.len(),
);
}
}
self.info.push((TypeInfo::of::<T>(), self.cursor));
self.cursor += mem::size_of::<T>();
self.info.push((type_info, self.cursor));
self.cursor += type_info.layout().size();
self
}

Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/hecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ mod world;

pub use archetype::{Archetype, TypeState};
pub use borrow::{AtomicBorrow, Ref, RefMut};
pub use bundle::{Bundle, DynamicBundle, MissingComponent, RuntimeBundle};
pub use bundle::{Bundle, DynamicBundle, MissingComponent};
pub use entities::{Entity, EntityReserver, Location, NoSuchEntity};
pub use entity_builder::{BuiltEntity, EntityBuilder};
pub use query::{
Expand Down
82 changes: 46 additions & 36 deletions examples/ecs/dynamic_components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::{alloc::Layout, time::Duration};
use bevy::prelude::*;
use bevy_app::ScheduleRunnerPlugin;
use bevy_ecs::{
ComponentId, DynamicComponentInfo, DynamicComponentQuery, DynamicSystemSettings, RuntimeBundle,
ComponentId, DynamicComponentInfo, DynamicComponentQuery, DynamicSystemSettings, EntityBuilder,
TypeInfo,
};

Expand All @@ -24,11 +24,12 @@ fn spawn_scene(world: &mut World, _resources: &mut Resources) {
// representing a Position and one representing a Velocity. Each of these will be made up of two
// bytes for simplicity, one representing the x and y position/velocity.

// We create our first component bundle
let components1 = RuntimeBundle {
// we must define our components' type information
components: vec![
// First we define our "Position" component
// We create our first entity
let mut builder = EntityBuilder::new();
// Then we add our "Position component"
let entity1 = builder
.add_dynamic(
// We need to describe our component's information
TypeInfo {
// We must provide a unique id for the compoonent
id: ComponentId::ExternalId(0),
Expand All @@ -37,53 +38,62 @@ fn spawn_scene(world: &mut World, _resources: &mut Resources) {
// And we must specify a drop function for our component
drop: |_| (),
},
// Next we define our "Velocity" component
// And provide the raw byte data data for the component
vec![
0, // X position byte
0, // Y position byte
]
// And cast the data to a pointer
.as_slice(),
)
// Next we add our "Velocity component"
.add_dynamic(
TypeInfo {
// We must specify a different ID for the velocity component
// This component needs its own unique ID
id: ComponentId::ExternalId(1),
// We specify the layout which happens to be the same as "Position"
layout: Layout::from_size_align(2, 1).unwrap(),
// And the drop function
layout: Layout::from_size_align(2 /* size */, 1 /* alignment */).unwrap(),
drop: |_| (),
},
],

// Data must be a Vector of Vectors of bytes and must contain the raw byte data for
// each of the components we want to add
data: vec![
// This will be the raw byte data for our position component
vec![
0, // X position byte
0, // Y position byte
],
// This will be the raw byte data for our velocity component
vec![
1, // X velocity byte
0, // Y velocity byte
],
],
};

// Now we create another bundle for our next entity
let components2 = RuntimeBundle {
components: vec![
1, // Y position byte
]
.as_slice(),
)
.build();

// And let's create another entity
let mut builder = EntityBuilder::new();
let entity2 = builder
.add_dynamic(
TypeInfo {
id: ComponentId::ExternalId(0),
layout: Layout::from_size_align(2 /* size */, 1 /* alignment */).unwrap(),
layout: Layout::from_size_align(2, 1).unwrap(),
drop: |_| (),
},
vec![
0, // X position byte
0, // Y position byte
]
.as_slice(),
)
.add_dynamic(
TypeInfo {
id: ComponentId::ExternalId(1),
layout: Layout::from_size_align(2, 1).unwrap(),
drop: |_| (),
},
],
data: vec![vec![0, 0], vec![0, 2]],
};
vec![
2, // X position byte
0, // Y position byte
]
.as_slice(),
)
.build();

// Now we can spawn our entities
world.spawn(components1);
world.spawn(components2);
world.spawn(entity1);
world.spawn(entity2);
}

fn main() {
Expand Down

0 comments on commit a383a18

Please sign in to comment.