Skip to content

Commit 6cbe636

Browse files
committed
Togglable bit flag for Identifier
1 parent 1323de7 commit 6cbe636

File tree

7 files changed

+382
-121
lines changed

7 files changed

+382
-121
lines changed

crates/bevy_ecs/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ bevy_utils = { path = "../bevy_utils", version = "0.14.0-dev" }
2323
bevy_ecs_macros = { path = "macros", version = "0.14.0-dev" }
2424
petgraph = "0.6"
2525

26-
bitflags = "2.3"
26+
bitflags = "2.4"
2727
concurrent-queue = "2.4.0"
2828
fixedbitset = "0.4.2"
2929
rustc-hash = "1.1"

crates/bevy_ecs/src/entity/map_entities.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ impl EntityMapper for SceneEntityMapper<'_> {
8686
// this new entity reference is specifically designed to never represent any living entity
8787
let new = Entity::from_raw_and_generation(
8888
self.dead_start.index(),
89-
IdentifierMask::inc_masked_high_by(self.dead_start.generation, self.generations),
89+
IdentifierMask::inc_entity_generation_by(self.dead_start.generation, self.generations),
9090
);
9191

9292
// Prevent generations counter from being a greater value than HIGH_MASK.

crates/bevy_ecs/src/entity/mod.rs

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,7 @@ use bevy_utils::tracing::warn;
4747

4848
use crate::{
4949
archetype::{ArchetypeId, ArchetypeRow},
50-
identifier::{
51-
error::IdentifierError,
52-
kinds::IdKind,
53-
masks::{IdentifierMask, HIGH_MASK},
54-
Identifier,
55-
},
50+
identifier::{error::IdentifierError, masks::IdentifierMask, Identifier},
5651
storage::{SparseSetIndex, TableId, TableRow},
5752
};
5853
use serde::{Deserialize, Serialize};
@@ -217,12 +212,20 @@ pub(crate) enum AllocAtWithoutReplacement {
217212

218213
impl Entity {
219214
/// Construct an [`Entity`] from a raw `index` value and a non-zero `generation` value.
220-
/// Ensure that the generation value is never greater than `0x7FFF_FFFF`.
215+
/// Ensure that the masked generation value is never greater than `0x3FFF_FFFF`.
221216
#[inline(always)]
222217
pub(crate) const fn from_raw_and_generation(index: u32, generation: NonZeroU32) -> Entity {
223-
debug_assert!(generation.get() <= HIGH_MASK);
218+
// Create an intermediate Identifier to do debug verification
219+
// This will get optimised away as Identifier and Entity have identical layouts
220+
let id = Identifier::from_parts(index, generation);
224221

225-
Self { index, generation }
222+
// If the PLACEHOLDER bit flag is set, then it is not a valid generation value for Entity.
223+
debug_assert!(!id.is_placeholder());
224+
225+
Self {
226+
index: id.low(),
227+
generation: id.high(),
228+
}
226229
}
227230

228231
/// An entity ID with a placeholder value. This may or may not correspond to an actual entity,
@@ -286,7 +289,13 @@ impl Entity {
286289
/// No particular structure is guaranteed for the returned bits.
287290
#[inline(always)]
288291
pub const fn to_bits(self) -> u64 {
289-
IdentifierMask::pack_into_u64(self.index, self.generation.get())
292+
self.to_identifier().to_bits()
293+
}
294+
295+
/// Convert to an [`Identifier`].
296+
#[inline(always)]
297+
pub const fn to_identifier(self) -> Identifier {
298+
Identifier::from_parts(self.index, self.generation)
290299
}
291300

292301
/// Reconstruct an `Entity` previously destructured with [`Entity::to_bits`].
@@ -298,12 +307,19 @@ impl Entity {
298307
/// This method will likely panic if given `u64` values that did not come from [`Entity::to_bits`].
299308
#[inline]
300309
pub const fn from_bits(bits: u64) -> Self {
310+
#[inline(never)]
311+
#[cold]
312+
#[track_caller]
313+
const fn invalid_entity() -> ! {
314+
panic!("Attempted to initialise invalid bits as an entity");
315+
}
316+
301317
// Construct an Identifier initially to extract the kind from.
302318
let id = Self::try_from_bits(bits);
303319

304320
match id {
305321
Ok(entity) => entity,
306-
Err(_) => panic!("Attempted to initialise invalid bits as an entity"),
322+
Err(_) => invalid_entity(),
307323
}
308324
}
309325

@@ -315,9 +331,7 @@ impl Entity {
315331
#[inline(always)]
316332
pub const fn try_from_bits(bits: u64) -> Result<Self, IdentifierError> {
317333
if let Ok(id) = Identifier::try_from_bits(bits) {
318-
let kind = id.kind() as u8;
319-
320-
if kind == (IdKind::Entity as u8) {
334+
if !id.is_placeholder() {
321335
return Ok(Self {
322336
index: id.low(),
323337
generation: id.high(),
@@ -343,8 +357,7 @@ impl Entity {
343357
/// given index has been reused (index, generation) pairs uniquely identify a given Entity.
344358
#[inline]
345359
pub const fn generation(self) -> u32 {
346-
// Mask so not to expose any flags
347-
IdentifierMask::extract_value_from_high(self.generation.get())
360+
self.to_identifier().masked_high()
348361
}
349362
}
350363

@@ -353,14 +366,19 @@ impl TryFrom<Identifier> for Entity {
353366

354367
#[inline]
355368
fn try_from(value: Identifier) -> Result<Self, Self::Error> {
369+
// Every Entity is an Identifier, but not every Identifier
370+
// is an Entity.
356371
Self::try_from_bits(value.to_bits())
357372
}
358373
}
359374

360375
impl From<Entity> for Identifier {
361376
#[inline]
362377
fn from(value: Entity) -> Self {
363-
Identifier::from_bits(value.to_bits())
378+
// Entity's layout is exactly the same as Identifier, therefore
379+
// this will remove any panic! path as we know Entity -> Identifier
380+
// conversions will never panic.
381+
value.to_identifier()
364382
}
365383
}
366384

@@ -684,12 +702,12 @@ impl Entities {
684702
return None;
685703
}
686704

687-
meta.generation = IdentifierMask::inc_masked_high_by(meta.generation, 1);
705+
meta.generation = IdentifierMask::inc_entity_generation_by(meta.generation, 1);
688706

689707
if meta.generation == NonZeroU32::MIN {
690708
warn!(
691709
"Entity({}) generation wrapped on Entities::free, aliasing may occur",
692-
entity.index
710+
entity.index()
693711
);
694712
}
695713

@@ -775,7 +793,8 @@ impl Entities {
775793

776794
let meta = &mut self.meta[index as usize];
777795
if meta.location.archetype_id == ArchetypeId::INVALID {
778-
meta.generation = IdentifierMask::inc_masked_high_by(meta.generation, generations);
796+
meta.generation =
797+
IdentifierMask::inc_entity_generation_by(meta.generation, generations);
779798
true
780799
} else {
781800
false
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//! Module for defining the flag bits present in the [`super::Identifier`] format.
2+
3+
use std::fmt::Debug;
4+
5+
use bitflags::bitflags;
6+
7+
bitflags! {
8+
/// Flag bits defined for [`super::Identifier`].
9+
pub struct IdentifierFlagBits: u32 {
10+
/// Flag for determining whether an [`super::Identifier`] is a
11+
/// [`super::IdKind::Placeholder`] or [`super::IdKind::Entity`].
12+
const IS_PLACEHOLDER = 0b1000_0000_0000_0000_0000_0000_0000_0000;
13+
/// Flag for determining whether the [`super::Identifier`] is in a
14+
/// `toggeable` state or not.
15+
const IS_TOGGLABLE = 0b0100_0000_0000_0000_0000_0000_0000_0000;
16+
17+
const _ = !0;
18+
}
19+
}
20+
21+
impl PartialEq for IdentifierFlagBits {
22+
#[inline]
23+
fn eq(&self, other: &Self) -> bool {
24+
self.0 == other.0
25+
}
26+
}
27+
28+
impl Debug for IdentifierFlagBits {
29+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30+
f.debug_tuple("IdentifierFlagBits").field(&self.0).finish()
31+
}
32+
}
33+
34+
#[cfg(test)]
35+
mod tests {
36+
use super::*;
37+
38+
#[test]
39+
fn smoke_test() {
40+
// Both flag bits are set
41+
let bits: u32 = 0xC0FF_EEEE;
42+
43+
let flags = IdentifierFlagBits::from_bits_retain(bits);
44+
45+
assert!(flags.contains(IdentifierFlagBits::IS_PLACEHOLDER));
46+
assert!(flags.contains(IdentifierFlagBits::IS_TOGGLABLE));
47+
48+
// Only IS_PLACEHOLDER flag bit set
49+
let bits: u32 = 0x80FF_F00F;
50+
51+
let flags = IdentifierFlagBits::from_bits_retain(bits);
52+
53+
assert!(flags.contains(IdentifierFlagBits::IS_PLACEHOLDER));
54+
assert!(!flags.contains(IdentifierFlagBits::IS_TOGGLABLE));
55+
56+
// Only IS_TOGGLABLE flag bit set
57+
let bits: u32 = 0x40FF_F00F;
58+
59+
let flags = IdentifierFlagBits::from_bits_retain(bits);
60+
61+
assert!(!flags.contains(IdentifierFlagBits::IS_PLACEHOLDER));
62+
assert!(flags.contains(IdentifierFlagBits::IS_TOGGLABLE));
63+
}
64+
}

crates/bevy_ecs/src/identifier/kinds.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
//! Module for defining the different [`super::Identifier`] kinds, which
2+
//! have different semantics with regards to their shared layout.
3+
14
/// The kinds of ID that [`super::Identifier`] can represent. Each
25
/// variant imposes different usages of the low/high segments
36
/// of the ID.
@@ -7,5 +10,5 @@ pub enum IdKind {
710
/// An ID variant that is compatible with [`crate::entity::Entity`].
811
Entity = 0,
912
/// A future ID variant.
10-
Placeholder = 0b1000_0000,
13+
Placeholder = 1,
1114
}

0 commit comments

Comments
 (0)