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

Opt out change detection #6659

Closed
wants to merge 17 commits into from
Closed
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
12 changes: 7 additions & 5 deletions benches/benches/bevy_ecs/change_detection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,13 @@ fn few_changed_detection_generic<T: Component + Default + BenchModify>(
let mut world = setup::<T>(entity_count);
world.clear_trackers();
let mut query = world.query::<&mut T>();
let mut to_modify: Vec<bevy_ecs::prelude::Mut<T>> =
query.iter_mut(&mut world).collect();
to_modify.shuffle(&mut deterministic_rand());
for component in to_modify[0..amount_to_modify].iter_mut() {
black_box(component.bench_modify());
{
let mut to_modify: Vec<_> =
query.iter_mut(&mut world).collect();
to_modify.shuffle(&mut deterministic_rand());
for component in to_modify[0..amount_to_modify].iter_mut() {
black_box(component.bench_modify());
}
}
world
},
Expand Down
91 changes: 86 additions & 5 deletions crates/bevy_ecs/macros/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,30 @@ use bevy_macro_utils::{get_lit_str, Symbol};
use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{quote, ToTokens};
use syn::{parse_macro_input, parse_quote, DeriveInput, Error, Ident, Path, Result};
use syn::{parse_macro_input, parse_quote, DeriveInput, Error, Ident, LitBool, Path, Result};

pub fn derive_resource(input: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(input as DeriveInput);
let bevy_ecs_path: Path = crate::bevy_ecs_path();

let attrs = match parse_resource_attrs(&ast) {
Ok(attrs) => attrs,
Err(e) => return e.into_compile_error().into(),
};

ast.generics
.make_where_clause()
.predicates
.push(parse_quote! { Self: Send + Sync + 'static });

let change_detection_enabled = LitBool::new(attrs.change_detection_enabled, Span::call_site());

let struct_name = &ast.ident;
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();

TokenStream::from(quote! {
impl #impl_generics #bevy_ecs_path::system::Resource for #struct_name #type_generics #where_clause {
const CHANGE_DETECTION_ENABLED: bool = #change_detection_enabled;
}
})
}
Expand All @@ -26,11 +34,16 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(input as DeriveInput);
let bevy_ecs_path: Path = crate::bevy_ecs_path();

let attrs = match parse_component_attr(&ast) {
let attrs = match parse_component_attrs(&ast) {
Ok(attrs) => attrs,
Err(e) => return e.into_compile_error().into(),
};

let component_mut = if attrs.change_detection_enabled {
quote! { #bevy_ecs_path::change_detection::Mut<'a, Self> }
} else {
quote! { &'a mut Self }
};
let storage = storage_path(&bevy_ecs_path, attrs.storage);

ast.generics
Expand All @@ -43,18 +56,29 @@ pub fn derive_component(input: TokenStream) -> TokenStream {

TokenStream::from(quote! {
impl #impl_generics #bevy_ecs_path::component::Component for #struct_name #type_generics #where_clause {
type WriteFetch<'a> = #component_mut;
type Storage = #storage;
fn shrink<'wlong: 'wshort, 'wshort>(item: Self::WriteFetch<'wlong>) -> Self::WriteFetch<'wshort> {
item
}
}
})
}

pub const COMPONENT: Symbol = Symbol("component");
pub const RESOURCE: Symbol = Symbol("resource");
pub const CHANGED_DETECTION: Symbol = Symbol("change_detection");
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
pub const CHANGED_DETECTION: Symbol = Symbol("change_detection");
pub const CHANGE_DETECTION: Symbol = Symbol("change_detection");

pub const STORAGE: Symbol = Symbol("storage");

struct Attrs {
struct ComponentAttrs {
change_detection_enabled: bool,
storage: StorageTy,
}

struct ResourceAttrs {
change_detection_enabled: bool,
}

#[derive(Clone, Copy)]
enum StorageTy {
Table,
Expand All @@ -65,10 +89,11 @@ enum StorageTy {
const TABLE: &str = "Table";
const SPARSE_SET: &str = "SparseSet";

fn parse_component_attr(ast: &DeriveInput) -> Result<Attrs> {
fn parse_component_attrs(ast: &DeriveInput) -> Result<ComponentAttrs> {
let meta_items = bevy_macro_utils::parse_attrs(ast, COMPONENT)?;

let mut attrs = Attrs {
let mut attrs = ComponentAttrs {
change_detection_enabled: true,
storage: StorageTy::Table,
};

Expand All @@ -93,6 +118,17 @@ fn parse_component_attr(ast: &DeriveInput) -> Result<Attrs> {
}
};
}
Meta(NameValue(m)) if m.path == CHANGED_DETECTION => {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Meta(NameValue(m)) if m.path == CHANGED_DETECTION => {
Meta(NameValue(m)) if m.path == CHANGE_DETECTION => {

attrs.change_detection_enabled = match m.lit {
syn::Lit::Bool(value) => value.value,
s => {
return Err(Error::new_spanned(
s,
"Change detection must be a bool, expected 'true' or 'false'.",
))
}
};
}
Meta(meta_item) => {
return Err(Error::new_spanned(
meta_item.path(),
Expand All @@ -114,6 +150,51 @@ fn parse_component_attr(ast: &DeriveInput) -> Result<Attrs> {
Ok(attrs)
}

fn parse_resource_attrs(ast: &DeriveInput) -> Result<ResourceAttrs> {
let meta_items = bevy_macro_utils::parse_attrs(ast, RESOURCE)?;

let mut attrs = ResourceAttrs {
change_detection_enabled: true,
};

for meta in meta_items {
use syn::{
Meta::NameValue,
NestedMeta::{Lit, Meta},
};
match meta {
Meta(NameValue(m)) if m.path == CHANGED_DETECTION => {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Meta(NameValue(m)) if m.path == CHANGED_DETECTION => {
Meta(NameValue(m)) if m.path == CHANGE_DETECTION => {

attrs.change_detection_enabled = match m.lit {
syn::Lit::Bool(value) => value.value,
s => {
return Err(Error::new_spanned(
s,
"Change detection must be a bool, expected 'true' or 'false'.",
))
}
};
}
Meta(meta_item) => {
return Err(Error::new_spanned(
meta_item.path(),
format!(
"unknown resource attribute `{}`",
meta_item.path().into_token_stream()
),
));
}
Lit(lit) => {
return Err(Error::new_spanned(
lit,
"unexpected literal in resource attribute",
))
}
}
}

Ok(attrs)
}

fn storage_path(bevy_ecs_path: &Path, ty: StorageTy) -> TokenStream2 {
let typename = match ty {
StorageTy::Table => Ident::new("TableStorage", Span::call_site()),
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ pub(crate) fn bevy_ecs_path() -> syn::Path {
BevyManifest::default().get_path("bevy_ecs")
}

#[proc_macro_derive(Resource)]
#[proc_macro_derive(Resource, attributes(resource))]
pub fn derive_resource(input: TokenStream) -> TokenStream {
component::derive_resource(input)
}
Expand Down
Loading