Skip to content

Bevy 0.13 Update PR #55

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

Merged
merged 17 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bevy-trait-query"
version = "0.4.0"
version = "0.5.0"
edition = "2021"

description = "Implementation of trait queries for the bevy game engine"
Expand All @@ -19,21 +19,21 @@ bevy-trait-query-impl = { path = "proc-macro", version = "0.4.0" }
tracing = "0.1"

[dependencies.bevy_ecs]
version = "0.12"
version = "0.13"

[dependencies.bevy_app]
version = "0.12"
version = "0.13"
optional = true

[dependencies.bevy_core]
version = "0.12"
version = "0.13"
optional = true

[dev-dependencies]
criterion = "0.5"

[dev-dependencies.bevy]
version = "0.12"
version = "0.13"
default-features = false

[[bench]]
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Before using this crate, you should be familiar with bevy: https://bevyengine.or

| Bevy Version | [Crate Version](CHANGELOG.md) |
|--------------|---------------|
| 0.13 | 0.5 |
| 0.12 | 0.4 |
| 0.11 | 0.3 |
| 0.10 | 0.2 |
Expand Down
55 changes: 20 additions & 35 deletions proc-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,12 @@ fn impl_trait_query(arg: TokenStream, item: TokenStream) -> Result<TokenStream2>
let impl_generics_with_lifetime = quote! { <#( #impl_generics_with_lifetime ,)*> };

let trait_object_query_code = quote! {
unsafe impl #impl_generics #imports::ReadOnlyWorldQuery for &#trait_object
unsafe impl #impl_generics #imports::QueryData for &#trait_object
#where_clause
{
type ReadOnly = Self;
}
unsafe impl #impl_generics #imports::ReadOnlyQueryData for &#trait_object
#where_clause
{}

Expand All @@ -157,7 +162,6 @@ fn impl_trait_query(arg: TokenStream, item: TokenStream) -> Result<TokenStream2>
{
type Item<'__w> = #my_crate::ReadTraits<'__w, #trait_object>;
type Fetch<'__w> = <#my_crate::All<&'__a #trait_object> as #imports::WorldQuery>::Fetch<'__w>;
type ReadOnly = Self;
type State = #my_crate::TraitQueryState<#trait_object>;

#[inline]
Expand All @@ -183,8 +187,6 @@ fn impl_trait_query(arg: TokenStream, item: TokenStream) -> Result<TokenStream2>
}

const IS_DENSE: bool = <#my_crate::All<&#trait_object> as #imports::WorldQuery>::IS_DENSE;
const IS_ARCHETYPAL: bool =
<#my_crate::All<&#trait_object> as #imports::WorldQuery>::IS_ARCHETYPAL;

#[inline]
unsafe fn set_archetype<'w>(
Expand Down Expand Up @@ -231,17 +233,13 @@ fn impl_trait_query(arg: TokenStream, item: TokenStream) -> Result<TokenStream2>
}

#[inline]
fn update_archetype_component_access(
state: &Self::State,
archetype: &#imports::Archetype,
access: &mut #imports::Access<#imports::ArchetypeComponentId>,
) {
<#my_crate::All<&#trait_object> as #imports::WorldQuery>::update_archetype_component_access(state, archetype, access);
fn init_state(world: &mut #imports::World) -> Self::State {
<#my_crate::All<&#trait_object> as #imports::WorldQuery>::init_state(world)
}

#[inline]
fn init_state(world: &mut #imports::World) -> Self::State {
<#my_crate::All<&#trait_object> as #imports::WorldQuery>::init_state(world)
fn get_state(world: &#imports::World) -> Option<Self::State> {
<#my_crate::All<&#trait_object> as #imports::WorldQuery>::get_state(world)
}

#[inline]
Expand All @@ -253,12 +251,17 @@ fn impl_trait_query(arg: TokenStream, item: TokenStream) -> Result<TokenStream2>
}
}

unsafe impl #impl_generics_with_lifetime #imports::QueryData for &'__a mut #trait_object
#where_clause
{
type ReadOnly = &'__a #trait_object;
}

unsafe impl #impl_generics_with_lifetime #imports::WorldQuery for &'__a mut #trait_object
#where_clause
{
type Item<'__w> = #my_crate::WriteTraits<'__w, #trait_object>;
type Fetch<'__w> = <#my_crate::All<&'__a #trait_object> as #imports::WorldQuery>::Fetch<'__w>;
type ReadOnly = &'__a #trait_object;
type State = #my_crate::TraitQueryState<#trait_object>;

#[inline]
Expand All @@ -284,8 +287,6 @@ fn impl_trait_query(arg: TokenStream, item: TokenStream) -> Result<TokenStream2>
}

const IS_DENSE: bool = <#my_crate::All<&mut #trait_object> as #imports::WorldQuery>::IS_DENSE;
const IS_ARCHETYPAL: bool =
<#my_crate::All<&mut #trait_object> as #imports::WorldQuery>::IS_ARCHETYPAL;

#[inline]
unsafe fn set_archetype<'w>(
Expand Down Expand Up @@ -332,18 +333,13 @@ fn impl_trait_query(arg: TokenStream, item: TokenStream) -> Result<TokenStream2>
}

#[inline]
fn update_archetype_component_access(
state: &Self::State,
archetype: &#imports::Archetype,
access: &mut #imports::Access<#imports::ArchetypeComponentId>,
) {
<#my_crate::All<&mut #trait_object> as #imports::WorldQuery>::update_archetype_component_access(state, archetype, access);
fn init_state(world: &mut #imports::World) -> Self::State {
<#my_crate::All<&mut #trait_object> as #imports::WorldQuery>::init_state(world)
}


#[inline]
fn init_state(world: &mut #imports::World) -> Self::State {
<#my_crate::All<&mut #trait_object> as #imports::WorldQuery>::init_state(world)
fn get_state(world: &#imports::World) -> Option<Self::State> {
<#my_crate::All<&mut #trait_object> as #imports::WorldQuery>::get_state(world)
}

#[inline]
Expand All @@ -364,14 +360,3 @@ fn impl_trait_query(arg: TokenStream, item: TokenStream) -> Result<TokenStream2>
#trait_object_query_code
})
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
91 changes: 52 additions & 39 deletions src/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use bevy_ecs::{
component::{ComponentId, Tick},
entity::Entity,
ptr::UnsafeCellDeref,
query::{QueryItem, ReadOnlyWorldQuery, WorldQuery},
query::{QueryData, QueryItem, ReadOnlyQueryData, WorldQuery},
storage::{SparseSets, Table, TableRow},
world::{unsafe_world_cell::UnsafeWorldCell, World},
};
Expand Down Expand Up @@ -58,7 +58,7 @@ impl<'a, Trait: ?Sized + TraitQuery> Iterator for ReadTableTraitsIter<'a, Trait>
let ptr = unsafe {
column
.get_data_ptr()
.byte_add(self.table_row.index() * meta.size_bytes)
.byte_add(self.table_row.as_usize() * meta.size_bytes)
};
let trait_object = unsafe { meta.dyn_ctor.cast(ptr) };

Expand Down Expand Up @@ -130,7 +130,7 @@ impl<'w, Trait: ?Sized + TraitQuery> IntoIterator for ReadTraits<'w, Trait> {
let sparse = ReadSparseTraitsIter {
components: self.registry.sparse_components.iter(),
meta: self.registry.sparse_meta.iter(),
entity: self.table.entities()[self.table_row.index()],
entity: self.table.entities()[self.table_row.as_usize()],
sparse_sets: self.sparse_sets,
last_run: self.last_run,
this_run: self.this_run,
Expand All @@ -155,7 +155,7 @@ impl<'w, Trait: ?Sized + TraitQuery> IntoIterator for &ReadTraits<'w, Trait> {
let sparse = ReadSparseTraitsIter {
components: self.registry.sparse_components.iter(),
meta: self.registry.sparse_meta.iter(),
entity: self.table.entities()[self.table_row.index()],
entity: self.table.entities()[self.table_row.as_usize()],
sparse_sets: self.sparse_sets,
last_run: self.last_run,
this_run: self.this_run,
Expand Down Expand Up @@ -246,7 +246,7 @@ impl<'a, Trait: ?Sized + TraitQuery> Iterator for WriteTableTraitsIter<'a, Trait
let ptr = unsafe {
column
.get_data_ptr()
.byte_add(self.table_row.index() * meta.size_bytes)
.byte_add(self.table_row.as_usize() * meta.size_bytes)
};
// SAFETY: The instance of `WriteTraits` that created this iterator
// has exclusive access to all table components registered with the trait.
Expand Down Expand Up @@ -375,7 +375,7 @@ impl<'w, Trait: ?Sized + TraitQuery> IntoIterator for WriteTraits<'w, Trait> {
let sparse = WriteSparseTraitsIter {
components: self.registry.sparse_components.iter(),
meta: self.registry.sparse_meta.iter(),
entity: self.table.entities()[self.table_row.index()],
entity: self.table.entities()[self.table_row.as_usize()],
sparse_sets: self.sparse_sets,
last_run: self.last_run,
this_run: self.this_run,
Expand All @@ -402,7 +402,7 @@ impl<'world, 'local, Trait: ?Sized + TraitQuery> IntoIterator
let sparse = ReadSparseTraitsIter {
components: self.registry.sparse_components.iter(),
meta: self.registry.sparse_meta.iter(),
entity: self.table.entities()[self.table_row.index()],
entity: self.table.entities()[self.table_row.as_usize()],
sparse_sets: self.sparse_sets,
last_run: self.last_run,
this_run: self.this_run,
Expand All @@ -429,7 +429,7 @@ impl<'world, 'local, Trait: ?Sized + TraitQuery> IntoIterator
let sparse = WriteSparseTraitsIter {
components: self.registry.sparse_components.iter(),
meta: self.registry.sparse_meta.iter(),
entity: self.table.entities()[self.table_row.index()],
entity: self.table.entities()[self.table_row.as_usize()],
sparse_sets: self.sparse_sets,
last_run: self.last_run,
this_run: self.this_run,
Expand All @@ -443,15 +443,17 @@ impl<'world, 'local, Trait: ?Sized + TraitQuery> IntoIterator
/// You can usually just use `&dyn Trait` or `&mut dyn Trait` as a `WorldQuery` directly.
pub struct All<T: ?Sized>(T);

unsafe impl<'a, Trait: ?Sized + TraitQuery> ReadOnlyWorldQuery for All<&'a Trait> {}
unsafe impl<'a, Trait: ?Sized + TraitQuery> QueryData for All<&'a Trait> {
type ReadOnly = Self;
}
unsafe impl<'a, Trait: ?Sized + TraitQuery> ReadOnlyQueryData for All<&'a Trait> {}

// SAFETY: We only access the components registered in the trait registry.
// This is known to match the set of components in the TraitQueryState,
// which is used to match archetypes and register world access.
unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a Trait> {
type Item<'w> = ReadTraits<'w, Trait>;
type Fetch<'w> = AllTraitsFetch<'w, Trait>;
type ReadOnly = Self;
type State = TraitQueryState<Trait>;

#[inline]
Expand All @@ -478,7 +480,6 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a Trait> {
}

const IS_DENSE: bool = false;
const IS_ARCHETYPAL: bool = false;

#[inline]
unsafe fn set_archetype<'w>(
Expand Down Expand Up @@ -521,33 +522,38 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a Trait> {
state: &Self::State,
access: &mut bevy_ecs::query::FilteredAccess<ComponentId>,
) {
let mut not_first = false;
let mut new_access = access.clone();
for &component in &*state.components {
assert!(
!access.access().has_write(component),
"&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",
std::any::type_name::<Trait>(),
);
access.add_read(component);
}
}

#[inline]
fn update_archetype_component_access(
state: &Self::State,
archetype: &bevy_ecs::archetype::Archetype,
access: &mut bevy_ecs::query::Access<bevy_ecs::archetype::ArchetypeComponentId>,
) {
for &component in &*state.components {
if let Some(archetype_component_id) = archetype.get_archetype_component_id(component) {
access.add_read(archetype_component_id);
if not_first {
let mut intermediate = access.clone();
intermediate.add_read(component);
new_access.append_or(&intermediate);
new_access.extend_access(&intermediate);
} else {
new_access.and_with(component);
new_access.access_mut().add_read(component);
not_first = true;
Comment on lines +533 to +541
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of the new_access and intermediate variables? Can we not insert directly into the filtered access?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This similar to how AnyOf is implemented in Bevy. The expected result of the whole method is that access contains the result of (access AND write component1) OR (access AND write component2) OR ...

The way this is achieved is by computing each access AND write componentN in an intermediate FilteredAccess, and then OR them together in an accumulator, which is new_access FilteredAccess. new_access needs to be different from access because to compute each intermediate you need the original access, so directly modifying access will produce incorrect results.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, thank you for explaining.

}
}
*access = new_access;
}

#[inline]
fn init_state(world: &mut World) -> Self::State {
TraitQueryState::init(world)
}

#[inline]
fn get_state(world: &World) -> Option<Self::State> {
TraitQueryState::get(world)
}

#[inline]
fn matches_component_set(
state: &Self::State,
Expand All @@ -557,13 +563,16 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a Trait> {
}
}

unsafe impl<'a, Trait: ?Sized + TraitQuery> QueryData for All<&'a mut Trait> {
type ReadOnly = All<&'a Trait>;
}

// SAFETY: We only access the components registered in the trait registry.
// This is known to match the set of components in the TraitQueryState,
// which is used to match archetypes and register world access.
unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a mut Trait> {
type Item<'w> = WriteTraits<'w, Trait>;
type Fetch<'w> = AllTraitsFetch<'w, Trait>;
type ReadOnly = All<&'a Trait>;
type State = TraitQueryState<Trait>;

#[inline]
Expand All @@ -590,7 +599,6 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a mut Trait> {
}

const IS_DENSE: bool = false;
const IS_ARCHETYPAL: bool = false;

#[inline]
unsafe fn set_archetype<'w>(
Expand Down Expand Up @@ -634,33 +642,38 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a mut Trait> {
state: &Self::State,
access: &mut bevy_ecs::query::FilteredAccess<ComponentId>,
) {
let mut not_first = false;
let mut new_access = access.clone();
for &component in &*state.components {
assert!(
!access.access().has_write(component),
"&mut {} conflicts with a previous access in this query. Mutable component access must be unique.",
std::any::type_name::<Trait>(),
);
access.add_write(component);
}
}

#[inline]
fn update_archetype_component_access(
state: &Self::State,
archetype: &bevy_ecs::archetype::Archetype,
access: &mut bevy_ecs::query::Access<bevy_ecs::archetype::ArchetypeComponentId>,
) {
for &component in &*state.components {
if let Some(archetype_component_id) = archetype.get_archetype_component_id(component) {
access.add_write(archetype_component_id);
if not_first {
let mut intermediate = access.clone();
intermediate.add_write(component);
new_access.append_or(&intermediate);
new_access.extend_access(&intermediate);
} else {
new_access.and_with(component);
new_access.access_mut().add_write(component);
not_first = true;
}
}
*access = new_access;
}

#[inline]
fn init_state(world: &mut World) -> Self::State {
TraitQueryState::init(world)
}

#[inline]
fn get_state(world: &World) -> Option<Self::State> {
TraitQueryState::get(world)
}

#[inline]
fn matches_component_set(
state: &Self::State,
Expand Down
Loading