Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize memory size of SparseArray and integer-based ECS IDs #3678

Closed
wants to merge 71 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
6620dd9
Crude initial implementation
james7132 Jan 15, 2022
37bd31f
Use nonmax instead.
james7132 Jan 15, 2022
28fe7a0
Implement it for BundleId
james7132 Jan 15, 2022
8bca091
Fix tests
james7132 Jan 15, 2022
f01f1d2
Add missing safety and panic docs.
james7132 Jan 15, 2022
d2ebb01
Remove dependency on static_assertions
james7132 Jan 15, 2022
1c2525c
Fix formatting
james7132 Jan 15, 2022
6aa4a97
Fix test
james7132 Jan 15, 2022
ab57a2b
Optimize space for ArchetypeId
james7132 Jan 26, 2022
85fc10c
Update docs
james7132 Jan 27, 2022
eea5465
Provide better panic messages
james7132 Jan 27, 2022
41d7c08
Address safety comment review
james7132 Mar 6, 2022
db30528
Remvoe ArchetypeId by niching EntityLocation
james7132 Mar 6, 2022
351ffea
Merge branch 'main' into ecs-id-size-opt
james7132 Mar 6, 2022
f01b97a
Remove comment about Entity niching
james7132 Mar 6, 2022
692b4a0
Fix up existing safety doc comments
james7132 Mar 6, 2022
307775e
Fix broken doc reference
james7132 Mar 7, 2022
1e19f0e
Merge branch 'main' into ecs-id-size-opt
james7132 May 20, 2022
ceeea05
Formatting
james7132 May 20, 2022
a1bd8c4
Per-ID type Repr
james7132 May 22, 2022
fd34430
Formatting
james7132 May 22, 2022
77f9a15
Shrink ComponentId to u32
james7132 May 22, 2022
96ddad1
Shrink ArchetypeId to u32
james7132 May 22, 2022
0a275db
Shrink BundleId to u32
james7132 May 22, 2022
a0edde8
Shrink ArchetypeComponentId
james7132 May 22, 2022
3ae86f0
cleanup ComponentId mappingsk
james7132 May 25, 2022
83a8b57
Optimize ComponentSparseSet
james7132 May 25, 2022
e74dc1f
Formatting
james7132 May 25, 2022
3c07935
Merge branch 'main' into ecs-id-size-opt
james7132 May 31, 2022
c70b98b
Fix CI
james7132 May 31, 2022
66d2c4d
Merge branch 'main' into ecs-id-size-opt
james7132 Jun 1, 2022
7e7c85e
Merge branch 'main' into ecs-id-size-opt
james7132 Jun 3, 2022
ff8cb07
Merge branch 'main' into ecs-id-size-opt
james7132 Jun 20, 2022
ebf8f9c
Merge branch 'main' into ecs-id-size-opt
james7132 Nov 20, 2022
675cdca
Fix CI
james7132 Nov 20, 2022
d2648e1
Merge branch 'main' into ecs-id-size-opt
james7132 Nov 23, 2022
e23485b
Use NonZeroU32 for Components
james7132 Nov 23, 2022
ef3082c
NonMax -> NonZero
james7132 Nov 23, 2022
ea2d275
De-niche ArchetypeComponentId
james7132 Nov 23, 2022
b8219fa
Make repr_to_index unsafe
james7132 Nov 23, 2022
92ca951
Zero-index pad Archetypes and Bundles
james7132 Nov 23, 2022
2aa3fe0
Delete outdated test
james7132 Nov 23, 2022
e28d7ac
Fix CI
james7132 Nov 23, 2022
e477f50
Merge branch 'main' into ecs-id-size-opt
james7132 Jan 6, 2023
e945015
Fix build
james7132 Jan 6, 2023
c8cddc4
Merge branch 'main' into ecs-id-size-opt
james7132 Jan 6, 2023
3388b4a
Fix safety comment.
james7132 Jan 7, 2023
79c65da
Discard incorrect test
james7132 Jan 11, 2023
83b03f8
Added missing details to SystemParam Local documentation. (#7106)
iiYese Jan 6, 2023
b0b7e58
Fix doc comment "Turbo" -> "Extreme" (#7091)
A-Walrus Jan 6, 2023
bebfb01
add `Axis::devices` to get all the input devices (#5400)
1e1001 Jan 6, 2023
5fa57ab
Update an outdated example for `Mut::map_unchanged` (#7115)
JoJoJet Jan 6, 2023
563cb2e
Remove the `SystemParamState` trait and remove types like `ResState` …
JoJoJet Jan 7, 2023
8340a3b
bevy_render: Run calculate_bounds in the end-of-update exclusive syst…
superdump Jan 9, 2023
025010a
Support storage buffers in derive `AsBindGroup` (#6129)
IceSentry Jan 9, 2023
a15827b
Expose symphonia features from rodio in bevy_audio and bevy (#6388)
Ian-Yy Jan 9, 2023
49e76e6
Smooth Transition between Animations (#6922)
smessmer Jan 9, 2023
32dac8a
Gamepad events refactor (#6965)
DevinLeamy Jan 9, 2023
45ee432
Separate Extract from Sub App Schedule (#7046)
hymm Jan 9, 2023
d823da0
Reduce branching in TrackedRenderPass (#7053)
james7132 Jan 9, 2023
4343f2e
reflect: add `insert` and `remove` methods to `List` (#7063)
soqb Jan 9, 2023
845b956
Panic on dropping NonSend in non-origin thread. (#6534)
james7132 Jan 9, 2023
a151fbf
Relax `Sync` bound on `Local<T> as ExclusiveSystemParam` (#7040)
JoJoJet Jan 9, 2023
1ad542a
add rust-version for MSRV and CI job to check (#6852)
mockersf Jan 9, 2023
dc857c0
Implement `SparseSetIndex` for `WorldId` (#7125)
2ne1ugly Jan 9, 2023
6c4acf9
Fix doc in `App::add_sub_app` (#7139)
2ne1ugly Jan 9, 2023
1246fdf
Fix overflow scaling for images (#7142)
mockersf Jan 9, 2023
5cda426
Add `TypeRegistrationDeserializer` and remove `BorrowedStr` (#7094)
SkiFire13 Jan 9, 2023
b7f1421
Add `Mut::reborrow` (#7114)
JoJoJet Jan 9, 2023
0ae7f77
Merge branch 'main' into ecs-id-size-opt
james7132 Jan 21, 2023
1029b0b
Fix build
james7132 Jan 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 67 additions & 18 deletions crates/bevy_ecs/src/archetype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use crate::{
use std::{
collections::HashMap,
hash::Hash,
num::{NonZeroU32, NonZeroUsize},
ops::{Index, IndexMut},
};

Expand Down Expand Up @@ -71,24 +72,42 @@ impl ArchetypeRow {
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
// SAFETY: Must be repr(transparent) due to the safety requirements on EntityLocation
#[repr(transparent)]
pub struct ArchetypeId(u32);
pub struct ArchetypeId(NonZeroU32);

impl ArchetypeId {
/// The ID for the [`Archetype`] without any components.
pub const EMPTY: ArchetypeId = ArchetypeId(0);
/// # Safety:
pub const EMPTY: ArchetypeId = {
// SAFETY: 1 is guaranteed to not equal 0
unsafe { Self::new_unchecked(1) }
};

/// Creates a new [`ArchetypeId`].
///
/// This must always have an all-1s bit pattern to ensure soundness in fast entity id space allocation.
pub const INVALID: ArchetypeId = ArchetypeId(u32::MAX);
/// # Panics
/// This will panic if `index` is not in the range `[0, u32::MAX]` or greater.
pub(crate) const fn new(index: usize) -> Self {
assert!(
index > 0 && index <= u32::MAX as usize,
"ArchetypeID cannot be u32::MAX or greater"
);
// SAFETY: The above assertion will fail if the value is not valid.
unsafe { Self::new_unchecked(index) }
}

/// Creates a new [`ArchetypeId`] without checking for validity of the
/// provided index.
///
/// # Safety
/// This is only safe if `index` is in the range of `[0, u32::MAX]`.
#[inline]
pub(crate) const fn new(index: usize) -> Self {
ArchetypeId(index as u32)
pub(crate) const unsafe fn new_unchecked(index: usize) -> Self {
Self(NonZeroU32::new_unchecked(index as u32))
}

/// Gets the corresponding index for the ID.
#[inline]
pub(crate) fn index(self) -> usize {
self.0 as usize
pub(crate) const fn index(self) -> usize {
self.0.get() as usize
}
}

Expand Down Expand Up @@ -530,7 +549,7 @@ pub struct ArchetypeGeneration(usize);
impl ArchetypeGeneration {
#[inline]
pub(crate) const fn initial() -> Self {
ArchetypeGeneration(0)
ArchetypeGeneration(1)
}

#[inline]
Expand Down Expand Up @@ -572,23 +591,45 @@ struct ArchetypeIdentity {
/// [`Resource`]: crate::system::Resource
/// [many-to-many relationship]: https://en.wikipedia.org/wiki/Many-to-many_(data_model)
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct ArchetypeComponentId(usize);
pub struct ArchetypeComponentId(NonZeroUsize);

impl ArchetypeComponentId {
/// Creates a new [`ArchetypeComponentId`] from an index.
#[inline]
pub(crate) const fn new(index: usize) -> Self {
Self(index)
assert!(index != 0);
// SAFETY: The value is guarenteed not to be 0 by the above check.
Self(unsafe { NonZeroUsize::new_unchecked(index) })
}
}

impl SparseSetIndex for ArchetypeComponentId {
type Repr = NonZeroUsize;
const MAX_SIZE: usize = NonZeroUsize::MAX_SIZE;

#[inline]
fn sparse_set_index(&self) -> usize {
self.0
self.0.get()
}

#[inline]
fn get_sparse_set_index(value: usize) -> Self {
Self(value)
Self::new(value)
}

#[inline]
unsafe fn repr_from_index(index: usize) -> Self::Repr {
debug_assert!(index < Self::MAX_SIZE);
// SAFETY: Caller guarentees that `index` does not exceed `u32::MAX - 1`.
// This addition cannot overflow under these guarentees.
//
// Created index cannot be zero as it's being incremented by one.
unsafe { NonZeroUsize::new_unchecked(index + 1) }
}

#[inline]
fn repr_to_index(repr: &Self::Repr) -> usize {
repr.get() as usize - 1
}
}

Expand All @@ -607,9 +648,16 @@ pub struct Archetypes {
impl Archetypes {
pub(crate) fn new() -> Self {
let mut archetypes = Archetypes {
archetypes: Vec::new(),
// Dummy value to fill the zero-index slot.
archetypes: vec![Archetype {
id: ArchetypeId::new(1),
table_id: TableId::empty(),
edges: Default::default(),
entities: Vec::new(),
components: SparseSet::new().into_immutable(),
}],
archetype_ids: Default::default(),
archetype_component_count: 0,
archetype_component_count: 1,
};
archetypes.get_id_or_insert(TableId::empty(), Vec::new(), Vec::new());
archetypes
Expand Down Expand Up @@ -700,11 +748,12 @@ impl Archetypes {
let table_start = *archetype_component_count;
*archetype_component_count += table_components.len();
let table_archetype_components =
(table_start..*archetype_component_count).map(ArchetypeComponentId);
(table_start..*archetype_component_count).map(ArchetypeComponentId::new);
let sparse_start = *archetype_component_count;
*archetype_component_count += sparse_set_components.len();
let sparse_set_archetype_components =
(sparse_start..*archetype_component_count).map(ArchetypeComponentId);
(sparse_start..*archetype_component_count).map(ArchetypeComponentId::new);

archetypes.push(Archetype::new(
id,
table_id,
Expand Down
43 changes: 39 additions & 4 deletions crates/bevy_ecs/src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,20 +241,43 @@ all_tuples!(tuple_impl, 0, 15, B);
pub struct BundleId(usize);

impl BundleId {
/// Creates a new [`BundleId`] from an index.
///
/// # Panic
/// This function will panic if `value` is greater than or equal to [`u32::MAX`].
#[inline]
pub(crate) const fn new(value: usize) -> Self {
Self(value)
}

#[inline]
pub fn index(self) -> usize {
pub(crate) fn index(self) -> usize {
self.0
}
}

impl SparseSetIndex for BundleId {
type Repr = usize;
const MAX_SIZE: usize = (u32::MAX - 1) as usize;

#[inline]
fn sparse_set_index(&self) -> usize {
self.index()
}

#[inline]
fn get_sparse_set_index(value: usize) -> Self {
Self(value)
Self::new(value)
}

#[inline]
unsafe fn repr_from_index(index: usize) -> Self::Repr {
index
}

#[inline]
fn repr_to_index(repr: &Self::Repr) -> usize {
*repr
}
}

Expand Down Expand Up @@ -680,7 +703,6 @@ impl<'a, 'b> BundleSpawner<'a, 'b> {
}
}

#[derive(Default)]
pub struct Bundles {
bundle_infos: Vec<BundleInfo>,
bundle_ids: HashMap<TypeId, BundleId>,
Expand Down Expand Up @@ -714,7 +736,20 @@ impl Bundles {
id
});
// SAFETY: index either exists, or was initialized
unsafe { self.bundle_infos.get_unchecked(id.0) }
unsafe { self.bundle_infos.get_unchecked(id.index()) }
}
}

impl Default for Bundles {
fn default() -> Self {
Bundles {
// Dummy value to fill the zero-index slot.
bundle_infos: vec![BundleInfo {
id: BundleId::new(1),
component_ids: Vec::new(),
}],
bundle_ids: Default::default(),
}
}
}

Expand Down
Loading