Skip to content

Commit

Permalink
Query (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
Suficio authored Nov 6, 2022
1 parent 7cfe661 commit 1a5a997
Show file tree
Hide file tree
Showing 21 changed files with 790 additions and 84 deletions.
5 changes: 2 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,15 @@ inspector = ["tide", "tide-websockets"]
members = ["crates/*"]

[workspace.dependencies]
# TODO(https://github.com/bevyengine/bevy/pull/6121) Detached non-Send tasks don't run
bevy = { git = "https://github.com/Suficio/bevy.git", branch = "query-state-with-state" }
# TODO(https://github.com/bevyengine/bevy/pull/6240) QueryState::new_with_state
bevy = { git = "https://github.com/Suficio/bevy.git", rev = "75742e179a74b55fca0cb3ee0824e0b7d760ea99" }
deno_core = "0.155.0"

[dependencies]
bevy = { workspace = true }
deno_core = { workspace = true }
deno_console = "0.73.0"

serde_json = "1.0"
serde_path_to_error = "0.1"

# Inspector dependencies
Expand Down
2 changes: 1 addition & 1 deletion examples/hello_world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use bevy_js::{self as bjs, runtimes::BevyRuntime};
fn main() {
App::new()
.add_plugins(MinimalPlugins)
.add_plugin(LogPlugin)
.add_plugin(LogPlugin::default())
.add_plugin(bjs::JsPlugin::<BevyRuntime>::default())
.add_startup_system(setup_runtime)
.run();
Expand Down
2 changes: 1 addition & 1 deletion examples/inspector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use bevy_js::{self as bjs, runtimes::BevyRuntime};
fn main() {
App::new()
.add_plugins(MinimalPlugins)
.add_plugin(LogPlugin)
.add_plugin(LogPlugin::default())
.add_plugin(bjs::JsPlugin::<BevyRuntime>::default())
.add_startup_system(setup_runtime)
.run();
Expand Down
2 changes: 1 addition & 1 deletion examples/startup_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use bevy_js::{self as bjs, runtimes::BevyRuntime};
fn main() {
App::new()
.add_plugins(MinimalPlugins)
.add_plugin(LogPlugin)
.add_plugin(LogPlugin::default())
.add_plugin(bjs::JsPlugin::<BevyRuntime>::default())
.add_startup_system(setup_runtime)
.run();
Expand Down
6 changes: 3 additions & 3 deletions examples/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

const { worldResourceId } = Bevy.ecs;

const { Entity, World } = Bevy.ecs;
const { Entity, World, Query } = Bevy.ecs;
const { Visibility, ComputedVisibility } = Bevy.render.view.visibility;
const { FocusPolicy } = Bevy.ui.focus;
const { TextBundle } = Bevy.ui.entity;
Expand All @@ -31,8 +31,8 @@ const { Time } = Bevy.time.time;
const world = new World(worldResourceId);

/// Track texts by tracking entity ID
const colorText = world.spawn();
const fpsText = world.spawn();
const colorText = world.spawnEmpty();
const fpsText = world.spawnEmpty();

setup(colorText, fpsText);

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod builder;
pub mod inspector;
pub mod lend;
mod loader;
mod query;
mod resource;
mod runtime;
pub mod runtimes;
Expand Down
157 changes: 157 additions & 0 deletions src/query/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
mod ptr;
mod vec;

pub use ptr::ComponentPtr;
pub use vec::VecPtr;

// TODO: Align with Bevy
#[allow(unreachable_code)]
pub(crate) unsafe fn debug_checked_unreachable() -> ! {
#[cfg(debug_assertions)]
unreachable!();
std::hint::unreachable_unchecked();
}

#[cfg(test)]
mod tests {
use super::*;
use bevy::{
prelude::*,
reflect::{ReflectFromPtr, TypeRegistry},
};

#[derive(Component)]
struct A;

#[derive(Component)]
#[component(storage = "SparseSet")]
struct B;

#[derive(Component, Reflect, PartialEq, Debug)]
struct Data(String);

#[test]
fn query_empty() {
let mut world = World::new();
let component_id = world.init_component::<A>();

let mut query =
unsafe { QueryState::<ComponentPtr, ()>::new_with_state(&mut world, component_id, ()) };
let count = query.iter(&mut world).count();
assert_eq!(count, 0);

// Check dynamic VecPtr variations

let mut query = unsafe {
QueryState::<VecPtr<ComponentPtr>, ()>::new_with_state(
&mut world,
vec![component_id],
(),
)
};
let count = query.iter(&mut world).count();
assert_eq!(count, 0);
}

#[test]
fn query() {
let mut world = World::new();

let a = world.init_component::<A>();
let b = world.init_component::<B>();

world.spawn((A, B));
world.spawn(A);

let mut query =
unsafe { QueryState::<ComponentPtr, ()>::new_with_state(&mut world, a, ()) };
let count = query.iter(&mut world).count();
assert_eq!(count, 2);

let mut query =
unsafe { QueryState::<ComponentPtr, ()>::new_with_state(&mut world, b, ()) };
let count = query.iter(&mut world).count();
assert_eq!(count, 1);

let mut query = unsafe {
QueryState::<(ComponentPtr, ComponentPtr), ()>::new_with_state(&mut world, (a, b), ())
};
let count = query.iter(&mut world).count();
assert_eq!(count, 1);

// Check dynamic VecPtr variations

let mut query = unsafe {
QueryState::<VecPtr<ComponentPtr>, ()>::new_with_state(&mut world, vec![a], ())
};
let count = query.iter(&mut world).count();
assert_eq!(count, 2);

let mut query = unsafe {
QueryState::<VecPtr<ComponentPtr>, ()>::new_with_state(&mut world, vec![b], ())
};
let count = query.iter(&mut world).count();
assert_eq!(count, 1);

let mut query = unsafe {
QueryState::<VecPtr<ComponentPtr>, ()>::new_with_state(&mut world, vec![a, b], ())
};
let count = query.iter(&mut world).count();
assert_eq!(count, 1);
}

#[test]
fn query_data() {
let mut world = World::new();
let type_registry = TypeRegistry::default();
let mut type_registry = type_registry.write();

type_registry.register::<Data>();
let component_id = world.init_component::<Data>();

world.spawn(Data("Hello, World!".to_string()));

let mut query =
unsafe { QueryState::<ComponentPtr, ()>::new_with_state(&mut world, component_id, ()) };

for data in query.iter(&mut world) {
let reflect_data = type_registry.get(std::any::TypeId::of::<Data>()).unwrap();
let reflect_from_ptr = reflect_data.data::<ReflectFromPtr>().unwrap();

let data = unsafe { reflect_from_ptr.as_reflect_ptr(data) };
let data = data.as_any().downcast_ref::<Data>().unwrap();

assert_eq!(data, &Data("Hello, World!".to_string()))
}
}

#[test]
fn query_data_multiple() {
let mut world = World::new();
let type_registry = TypeRegistry::default();
let mut type_registry = type_registry.write();

type_registry.register::<Data>();
let component_id = world.init_component::<Data>();

world.spawn(Data("Hello, World!".to_string()));
world.spawn(Data("Hello, World!".to_string()));
world.spawn(Data("Hello, World!".to_string()));

let mut query =
unsafe { QueryState::<ComponentPtr, ()>::new_with_state(&mut world, component_id, ()) };

let res = query.iter_mut(&mut world).collect::<Vec<_>>();
assert_eq!(res.len(), 3);

for data in res.into_iter() {
let reflect_data = type_registry.get(std::any::TypeId::of::<Data>()).unwrap();
let reflect_from_ptr = reflect_data.data::<ReflectFromPtr>().unwrap();

let data = unsafe { reflect_from_ptr.as_reflect_ptr(data) };
let data = data.as_any().downcast_ref::<Data>().unwrap();

assert_eq!(data.0, "Hello, World!".to_string());
}
}
}
141 changes: 141 additions & 0 deletions src/query/ptr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
use super::debug_checked_unreachable;
use bevy::{
ecs::{
archetype::{Archetype, ArchetypeComponentId},
component::{ComponentId, StorageType},
query::{Access, FilteredAccess, ReadOnlyWorldQuery, WorldQuery},
storage::{ComponentSparseSet, Table},
},
prelude::*,
ptr::Ptr,
};

/// Type used to query non-dense components
pub struct ComponentPtr;

#[doc(hidden)]
pub struct ReadFetchSparse<'w> {
storage_type: StorageType,
component_size: usize,

table_components: Option<Ptr<'w>>,
sparse_set: Option<&'w ComponentSparseSet>,
}

unsafe impl ReadOnlyWorldQuery for ComponentPtr {}

unsafe impl WorldQuery for ComponentPtr {
type Fetch<'w> = ReadFetchSparse<'w>;
type Item<'w> = Ptr<'w>;
type ReadOnly = Self;
type State = ComponentId;

fn shrink<'wlong: 'wshort, 'wshort>(item: Ptr<'wlong>) -> Ptr<'wshort> {
item
}

unsafe fn init_fetch<'w>(
world: &'w World,
&component_id: &ComponentId,
_last_change_tick: u32,
_change_tick: u32,
) -> ReadFetchSparse<'w> {
let component_info = world.components().get_info(component_id).unwrap();
let storage_type = component_info.storage_type();

ReadFetchSparse {
storage_type,
component_size: component_info.layout().size(),

table_components: None,
sparse_set: (storage_type == StorageType::SparseSet)
.then(|| world.storages().sparse_sets.get(component_id).unwrap()),
}
}

unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> {
ReadFetchSparse {
storage_type: fetch.storage_type,
component_size: fetch.component_size,

table_components: fetch.table_components,
sparse_set: fetch.sparse_set,
}
}

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

#[inline]
unsafe fn set_archetype<'w>(
fetch: &mut ReadFetchSparse<'w>,
component_id: &ComponentId,
_archetype: &'w Archetype,
table: &'w Table,
) {
if fetch.storage_type == StorageType::Table {
Self::set_table(fetch, component_id, table);
}
}

#[inline]
unsafe fn set_table<'w>(
fetch: &mut ReadFetchSparse<'w>,
&component_id: &ComponentId,
table: &'w Table,
) {
fetch.table_components = Some(table.get_column(component_id).unwrap().get_data_ptr());
}

#[inline(always)]
unsafe fn fetch<'w>(
fetch: &mut Self::Fetch<'w>,
entity: Entity,
table_row: usize,
) -> Self::Item<'w> {
match fetch.storage_type {
StorageType::Table => fetch
.table_components
.unwrap_or_else(|| debug_checked_unreachable())
.byte_add(table_row * fetch.component_size),
StorageType::SparseSet => fetch
.sparse_set
.unwrap_or_else(|| debug_checked_unreachable())
.get(entity)
.unwrap_or_else(|| debug_checked_unreachable()),
}
}

fn update_component_access(
&component_id: &ComponentId,
access: &mut FilteredAccess<ComponentId>,
) {
assert!(
!access.access().has_write(component_id),
"Read access to component with id: {:?} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",
component_id
);
access.add_read(component_id);
}

fn update_archetype_component_access(
&component_id: &ComponentId,
archetype: &Archetype,
access: &mut Access<ArchetypeComponentId>,
) {
if let Some(archetype_component_id) = archetype.get_archetype_component_id(component_id) {
access.add_read(archetype_component_id);
}
}

fn init_state(_world: &mut World) -> Self::State {
panic!("Dynamic queries can only be initialized through QueryState::new_with_state");
}

fn matches_component_set(
&component_id: &ComponentId,
set_contains_id: &impl Fn(ComponentId) -> bool,
) -> bool {
set_contains_id(component_id)
}
}
Loading

0 comments on commit 1a5a997

Please sign in to comment.