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

Resource change tracking #388

Merged
merged 7 commits into from
Sep 10, 2020
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub use world::*;

pub mod prelude {
pub use crate::{
resource::{FromResources, Local, Res, ResMut, Resource, Resources},
resource::{ChangedRes, FromResources, Local, OrRes, Res, ResMut, Resource, Resources},
system::{
Commands, IntoForEachSystem, IntoQuerySystem, IntoThreadLocalSystem, Query, System,
},
Expand Down
222 changes: 205 additions & 17 deletions crates/bevy_ecs/src/resource/resource_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,41 @@ use core::{
};
use std::marker::PhantomData;

/// A shared borrow of a Resource
/// that will only return in a query if the Resource has been changed
pub struct ChangedRes<'a, T: Resource> {
value: &'a T,
}

impl<'a, T: Resource> ChangedRes<'a, T> {
/// Creates a reference cell to a Resource from a pointer
///
/// # Safety
/// The pointer must have correct lifetime / storage
pub unsafe fn new(value: NonNull<T>) -> Self {
Self {
value: &*value.as_ptr(),
}
}
}

impl<'a, T: Resource> UnsafeClone for ChangedRes<'a, T> {
unsafe fn unsafe_clone(&self) -> Self {
Self { value: self.value }
}
}

unsafe impl<T: Resource> Send for ChangedRes<'_, T> {}
unsafe impl<T: Resource> Sync for ChangedRes<'_, T> {}

impl<'a, T: Resource> Deref for ChangedRes<'a, T> {
type Target = T;

fn deref(&self) -> &T {
self.value
}
}

/// Shared borrow of a Resource
pub struct Res<'a, T: Resource> {
value: &'a T,
Expand Down Expand Up @@ -55,16 +90,18 @@ impl<'a, T: Resource> Deref for Res<'a, T> {
pub struct ResMut<'a, T: Resource> {
_marker: PhantomData<&'a T>,
value: *mut T,
mutated: *mut bool,
}

impl<'a, T: Resource> ResMut<'a, T> {
/// Creates a mutable reference cell to a Resource from a pointer
///
/// # Safety
/// The pointer must have correct lifetime / storage / ownership
pub unsafe fn new(value: NonNull<T>) -> Self {
pub unsafe fn new((value, mutated): (NonNull<T>, NonNull<bool>)) -> Self {
BimDav marked this conversation as resolved.
Show resolved Hide resolved
Self {
value: value.as_ptr(),
mutated: mutated.as_ptr(),
_marker: Default::default(),
}
}
Expand All @@ -83,14 +120,18 @@ impl<'a, T: Resource> Deref for ResMut<'a, T> {

impl<'a, T: Resource> DerefMut for ResMut<'a, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.value }
unsafe {
*self.mutated = true;
&mut *self.value
}
}
}

impl<'a, T: Resource> UnsafeClone for ResMut<'a, T> {
unsafe fn unsafe_clone(&self) -> Self {
Self {
value: self.value,
mutated: self.mutated,
_marker: Default::default(),
}
}
Expand Down Expand Up @@ -143,7 +184,7 @@ pub trait FetchResource<'a>: Sized {
fn release(resources: &Resources);

#[allow(clippy::missing_safety_doc)]
unsafe fn get(resources: &'a Resources, system_id: Option<SystemId>) -> Self::Item;
unsafe fn get(resources: &'a Resources, system_id: Option<SystemId>) -> (Self::Item, bool);
BimDav marked this conversation as resolved.
Show resolved Hide resolved
}

impl<'a, T: Resource> ResourceQuery for Res<'a, T> {
Expand All @@ -156,8 +197,42 @@ pub struct FetchResourceRead<T>(NonNull<T>);
impl<'a, T: Resource> FetchResource<'a> for FetchResourceRead<T> {
type Item = Res<'a, T>;

unsafe fn get(resources: &'a Resources, _system_id: Option<SystemId>) -> Self::Item {
Res::new(resources.get_unsafe_ref::<T>(ResourceIndex::Global))
unsafe fn get(resources: &'a Resources, _system_id: Option<SystemId>) -> (Self::Item, bool) {
(
Res::new(resources.get_unsafe_ref::<T>(ResourceIndex::Global)),
true,
)
}

fn borrow(resources: &Resources) {
resources.borrow::<T>();
}

fn release(resources: &Resources) {
resources.release::<T>();
}

fn access() -> TypeAccess {
let mut access = TypeAccess::default();
access.immutable.insert(TypeId::of::<T>());
access
}
}

impl<'a, T: Resource> ResourceQuery for ChangedRes<'a, T> {
type Fetch = FetchResourceChanged<T>;
}

/// Fetches a shared resource reference
pub struct FetchResourceChanged<T>(NonNull<T>);

impl<'a, T: Resource> FetchResource<'a> for FetchResourceChanged<T> {
type Item = ChangedRes<'a, T>;

unsafe fn get(resources: &'a Resources, _system_id: Option<SystemId>) -> (Self::Item, bool) {
let (value, added, mutated) =
resources.get_unsafe_ref_with_added_and_mutated::<T>(ResourceIndex::Global);
(ChangedRes::new(value), *added.as_ptr() || *mutated.as_ptr())
}

fn borrow(resources: &Resources) {
Expand Down Expand Up @@ -185,8 +260,11 @@ pub struct FetchResourceWrite<T>(NonNull<T>);
impl<'a, T: Resource> FetchResource<'a> for FetchResourceWrite<T> {
type Item = ResMut<'a, T>;

unsafe fn get(resources: &'a Resources, _system_id: Option<SystemId>) -> Self::Item {
ResMut::new(resources.get_unsafe_ref::<T>(ResourceIndex::Global))
unsafe fn get(resources: &'a Resources, _system_id: Option<SystemId>) -> (Self::Item, bool) {
(
ResMut::new(resources.get_unsafe_ref_with_mutated::<T>(ResourceIndex::Global)),
true,
)
}

fn borrow(resources: &Resources) {
Expand Down Expand Up @@ -220,14 +298,17 @@ pub struct FetchResourceLocalMut<T>(NonNull<T>);
impl<'a, T: Resource + FromResources> FetchResource<'a> for FetchResourceLocalMut<T> {
type Item = Local<'a, T>;

unsafe fn get(resources: &'a Resources, system_id: Option<SystemId>) -> Self::Item {
unsafe fn get(resources: &'a Resources, system_id: Option<SystemId>) -> (Self::Item, bool) {
let id = system_id.expect("Local<T> resources can only be used by systems");
Local {
value: resources
.get_unsafe_ref::<T>(ResourceIndex::System(id))
.as_ptr(),
_marker: Default::default(),
}
(
Local {
value: resources
.get_unsafe_ref::<T>(ResourceIndex::System(id))
.as_ptr(),
_marker: Default::default(),
},
true,
)
}

fn borrow(resources: &Resources) {
Expand Down Expand Up @@ -260,9 +341,10 @@ macro_rules! tuple_impl {
$($name::release(resources);)*
}

#[allow(unused_variables)]
unsafe fn get(resources: &'a Resources, system_id: Option<SystemId>) -> Self::Item {
($($name::get(resources, system_id),)*)
#[allow(unused_variables, irrefutable_let_patterns, non_snake_case)]
unsafe fn get(resources: &'a Resources, system_id: Option<SystemId>) -> (Self::Item, bool) {
let ($($name,)*) = ($($name::get(resources, system_id),)*);
(($($name.0,)*), true $(&& $name.1)*)
}

#[allow(unused_mut)]
Expand Down Expand Up @@ -294,3 +376,109 @@ macro_rules! tuple_impl {
}

smaller_tuples_too!(tuple_impl, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A);

pub struct OrRes<T>(T);

pub struct FetchResourceOr<T>(NonNull<T>);

macro_rules! tuple_impl_or {
($($name: ident),*) => {
impl<'a, $($name: FetchResource<'a>),*> FetchResource<'a> for FetchResourceOr<($($name,)*)> {
type Item = OrRes<($($name::Item,)*)>;

#[allow(unused_variables)]
fn borrow(resources: &Resources) {
$($name::borrow(resources);)*
}

#[allow(unused_variables)]
fn release(resources: &Resources) {
$($name::release(resources);)*
}

#[allow(unused_variables, irrefutable_let_patterns, non_snake_case)]
unsafe fn get(resources: &'a Resources, system_id: Option<SystemId>) -> (Self::Item, bool) {
let ($($name,)*) = ($($name::get(resources, system_id),)*);
(OrRes(($($name.0,)*)), false $(|| $name.1)*)
}

#[allow(unused_mut)]
fn access() -> TypeAccess {
let mut access = TypeAccess::default();
$(access.union(&$name::access());)*
access
}
}

impl<$($name: ResourceQuery),*> ResourceQuery for OrRes<($($name,)*)> {
type Fetch = FetchResourceOr<($($name::Fetch,)*)>;

#[allow(unused_variables)]
fn initialize(resources: &mut Resources, system_id: Option<SystemId>) {
$($name::initialize(resources, system_id);)*
}
}

#[allow(unused_variables)]
#[allow(non_snake_case)]
impl<$($name: UnsafeClone),*> UnsafeClone for OrRes<($($name,)*)> {
unsafe fn unsafe_clone(&self) -> Self {
let OrRes(($($name,)*)) = self;
OrRes(($($name.unsafe_clone(),)*))
}
}

impl<$($name,)*> Deref for OrRes<($($name,)*)> {
type Target = ($($name,)*);

fn deref(&self) -> &Self::Target {
&self.0
}
}
};
}

smaller_tuples_too!(tuple_impl_or, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A);

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

#[test]
fn changed_resource() {
let mut resources = Resources::default();
resources.insert(123);
assert_eq!(
resources.query::<ChangedRes<i32>>().as_deref(),
Some(&(123 as i32))
);
resources.clear_trackers();
assert_eq!(resources.query::<ChangedRes<i32>>().as_deref(), None);
*resources.query::<ResMut<i32>>().unwrap() += 1;
assert_eq!(
resources.query::<ChangedRes<i32>>().as_deref(),
Some(&(124 as i32))
);
}

#[test]
fn or_changed_resource() {
let mut resources = Resources::default();
resources.insert(123);
resources.insert(0.2);
assert!(!resources
BimDav marked this conversation as resolved.
Show resolved Hide resolved
.query::<OrRes<(ChangedRes<i32>, ChangedRes<f64>)>>()
.is_none(),);
resources.clear_trackers();
assert!(resources
.query::<OrRes<(ChangedRes<i32>, ChangedRes<f64>)>>()
.is_none(),);
*resources.query::<ResMut<i32>>().unwrap() += 1;
assert!(!resources
.query::<OrRes<(ChangedRes<i32>, ChangedRes<f64>)>>()
.is_none(),);
assert!(resources
.query::<(ChangedRes<i32>, ChangedRes<f64>)>()
.is_none(),);
}
}
Loading