From 4b48e235bc97c5239ee8c223cf2a195ceca92d83 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Mon, 2 Sep 2024 21:56:25 +0000 Subject: [PATCH 01/32] minimal example auto registration for reflect types --- crates/bevy_app/src/app.rs | 4 ++++ crates/bevy_reflect/Cargo.toml | 2 ++ .../bevy_reflect/derive/src/impls/structs.rs | 18 ++++++++++++++++++ crates/bevy_reflect/src/lib.rs | 3 +++ crates/bevy_reflect/src/type_registry.rs | 15 +++++++++++++++ examples/reflection/reflection.rs | 8 +++++++- 6 files changed, 49 insertions(+), 1 deletion(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index da41efbc152c4..b502e32f64c44 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -92,8 +92,12 @@ impl Debug for App { impl Default for App { fn default() -> Self { let mut app = App::empty(); + app.sub_apps.main.update_schedule = Some(Main.intern()); + #[cfg(feature = "bevy_reflect")] + bevy_reflect::wasm_init::wasm_init(); + #[cfg(feature = "bevy_reflect")] app.init_resource::(); diff --git a/crates/bevy_reflect/Cargo.toml b/crates/bevy_reflect/Cargo.toml index 5cfbf833009fe..401ef4071a48a 100644 --- a/crates/bevy_reflect/Cargo.toml +++ b/crates/bevy_reflect/Cargo.toml @@ -39,6 +39,8 @@ glam = { version = "0.28", features = ["serde"], optional = true } petgraph = { version = "0.6", features = ["serde-1"], optional = true } smol_str = { version = "0.2.0", optional = true } uuid = { version = "1.0", optional = true, features = ["v4", "serde"] } +inventory = "0.3" +wasm-init = "0.2" [dev-dependencies] ron = "0.8.0" diff --git a/crates/bevy_reflect/derive/src/impls/structs.rs b/crates/bevy_reflect/derive/src/impls/structs.rs index 15b11d4dd97a1..8095cda8c3ee8 100644 --- a/crates/bevy_reflect/derive/src/impls/structs.rs +++ b/crates/bevy_reflect/derive/src/impls/structs.rs @@ -60,6 +60,22 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS .generics() .split_for_impl(); + let auto_reflect = if ty_generics.clone().into_token_stream().is_empty() { + quote! { + #[cfg(target_arch = "wasm32")] + #bevy_reflect_path::wasm_init::wasm_init!{ + #bevy_reflect_path::AUTOMATIC_REFLECT_TYPES + .write() + .unwrap() + .push(|reg: &mut #bevy_reflect_path::TypeRegistry| reg.register::<#struct_path>()); + } + #[cfg(not(target_arch = "wasm32"))] + #bevy_reflect_path::inventory::submit!(#bevy_reflect_path::AUTOMATIC_REFLECT_TYPES(|reg: &mut #bevy_reflect_path::TypeRegistry| reg.register::<#struct_path>())); + } + } else { + quote! {} + }; + let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); quote! { @@ -73,6 +89,8 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS #function_impls + #auto_reflect + impl #impl_generics #bevy_reflect_path::Struct for #struct_path #ty_generics #where_reflect_clause { fn field(&self, name: &str) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> { match name { diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 29829130feeac..240bc3bd6112a 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -597,6 +597,9 @@ pub use type_registry::*; pub use bevy_reflect_derive::*; pub use erased_serde; +pub extern crate inventory; +pub extern crate wasm_init; + extern crate alloc; /// Exports used by the reflection macros. diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 1263b0bbed523..0f9f087257805 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -9,6 +9,13 @@ use std::{ sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}, }; +#[cfg(target_arch = "wasm32")] +pub static AUTOMATIC_REFLECT_TYPES: RwLock> = RwLock::new(Vec::new()); +#[cfg(not(target_arch = "wasm32"))] +pub struct AUTOMATIC_REFLECT_TYPES(pub fn(&mut TypeRegistry)); +#[cfg(not(target_arch = "wasm32"))] +inventory::collect!(AUTOMATIC_REFLECT_TYPES); + /// A registry of [reflected] types. /// /// This struct is used as the central store for type information. @@ -108,6 +115,14 @@ impl TypeRegistry { registry.register::(); registry.register::(); registry.register::(); + #[cfg(target_arch = "wasm32")] + for f in AUTOMATIC_REFLECT_TYPES.read().unwrap().iter() { + f(&mut registry) + } + #[cfg(not(target_arch = "wasm32"))] + for f in inventory::iter:: { + f.0(&mut registry) + } registry } diff --git a/examples/reflection/reflection.rs b/examples/reflection/reflection.rs index 283c1c6d18257..4cb6003116e1c 100644 --- a/examples/reflection/reflection.rs +++ b/examples/reflection/reflection.rs @@ -4,6 +4,8 @@ //! by their string name. Reflection is a core part of Bevy and enables a number of interesting //! features (like scenes). +use std::any::Any; + use bevy::{ prelude::*, reflect::{ @@ -17,7 +19,7 @@ fn main() { App::new() .add_plugins(DefaultPlugins) // Bar will be automatically registered as it's a dependency of Foo - .register_type::() + // .register_type::() .add_systems(Startup, setup) .run(); } @@ -101,6 +103,8 @@ fn setup(type_registry: Res) { let mut deserializer = ron::de::Deserializer::from_str(&ron_string).unwrap(); let reflect_value = reflect_deserializer.deserialize(&mut deserializer).unwrap(); + assert!(type_registry.contains(value.type_id())); + // Deserializing returns a `Box` value. // Generally, deserializing a value will return the "dynamic" variant of a type. // For example, deserializing a struct will return the DynamicStruct type. @@ -118,4 +122,6 @@ fn setup(type_registry: Res) { // By "patching" `Foo` with the deserialized DynamicStruct, we can "Deserialize" Foo. // This means we can serialize and deserialize with a single `Reflect` derive! value.apply(&*reflect_value); + + info!("{}", type_registry.iter().collect::>().len()); } From 89c52dd0dd5314a3eeadd8195aaa995316272f35 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Tue, 3 Sep 2024 09:49:16 +0000 Subject: [PATCH 02/32] implement auto registration for the rest of supported `#[derive(Reflect)]` types --- crates/bevy_app/src/app.rs | 3 -- .../bevy_reflect/derive/src/impls/common.rs | 28 ++++++++++++++- crates/bevy_reflect/derive/src/impls/enums.rs | 9 ++++- crates/bevy_reflect/derive/src/impls/mod.rs | 4 ++- .../bevy_reflect/derive/src/impls/structs.rs | 23 ++++-------- .../derive/src/impls/tuple_structs.rs | 9 ++++- .../bevy_reflect/derive/src/impls/values.rs | 9 ++++- crates/bevy_reflect/src/type_registry.rs | 36 ++++++++++++------- 8 files changed, 83 insertions(+), 38 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index b502e32f64c44..2b7a088dff70c 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -95,9 +95,6 @@ impl Default for App { app.sub_apps.main.update_schedule = Some(Main.intern()); - #[cfg(feature = "bevy_reflect")] - bevy_reflect::wasm_init::wasm_init(); - #[cfg(feature = "bevy_reflect")] app.init_resource::(); diff --git a/crates/bevy_reflect/derive/src/impls/common.rs b/crates/bevy_reflect/derive/src/impls/common.rs index f01fd96e044cc..25ba89fcd35c7 100644 --- a/crates/bevy_reflect/derive/src/impls/common.rs +++ b/crates/bevy_reflect/derive/src/impls/common.rs @@ -1,6 +1,6 @@ use bevy_macro_utils::fq_std::{FQAny, FQBox, FQOption, FQResult}; -use quote::quote; +use quote::{quote, ToTokens}; use crate::{derive_data::ReflectMeta, utility::WhereClauseOptions}; @@ -156,3 +156,29 @@ pub fn common_partial_reflect_methods( #debug_fn } } + +pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option { + let bevy_reflect_path = meta.bevy_reflect_path(); + let type_path = meta.type_path(); + let (_, ty_generics, _) = meta.type_path().generics().split_for_impl(); + + if !ty_generics.into_token_stream().is_empty() { + return None; + }; + + Some(quote! { + #[cfg(target_family = "wasm")] + #bevy_reflect_path::wasm_init::wasm_init!{ + #bevy_reflect_path::DERIVED_REFLECT_TYPES + .write() + .unwrap() + .push(|reg: &mut #bevy_reflect_path::TypeRegistry| reg.register::<#type_path>()); + } + #[cfg(not(target_family = "wasm"))] + #bevy_reflect_path::inventory::submit!( + #bevy_reflect_path::DERIVED_REFLECT_TYPES( + |reg: &mut #bevy_reflect_path::TypeRegistry| reg.register::<#type_path>() + ) + ); + }) +} diff --git a/crates/bevy_reflect/derive/src/impls/enums.rs b/crates/bevy_reflect/derive/src/impls/enums.rs index 15d5c01ffa796..595372cce487e 100644 --- a/crates/bevy_reflect/derive/src/impls/enums.rs +++ b/crates/bevy_reflect/derive/src/impls/enums.rs @@ -1,6 +1,9 @@ use crate::derive_data::{EnumVariantFields, ReflectEnum, StructField}; use crate::enum_utility::{EnumVariantOutputData, TryApplyVariantBuilder, VariantBuilder}; -use crate::impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed}; +use crate::impls::{ + common_partial_reflect_methods, impl_full_reflect, reflect_auto_registration, + impl_type_path, impl_typed, +}; use bevy_macro_utils::fq_std::{FQBox, FQOption, FQResult}; use proc_macro2::{Ident, Span}; use quote::quote; @@ -80,6 +83,8 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream let (impl_generics, ty_generics, where_clause) = reflect_enum.meta().type_path().generics().split_for_impl(); + let auto_register = reflect_auto_registration(&reflect_enum.meta()); + let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); quote! { @@ -93,6 +98,8 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream #function_impls + #auto_register + impl #impl_generics #bevy_reflect_path::Enum for #enum_path #ty_generics #where_reflect_clause { fn field(&self, #ref_name: &str) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> { match #match_this { diff --git a/crates/bevy_reflect/derive/src/impls/mod.rs b/crates/bevy_reflect/derive/src/impls/mod.rs index 0905d43bd260e..5c0eacdc316b5 100644 --- a/crates/bevy_reflect/derive/src/impls/mod.rs +++ b/crates/bevy_reflect/derive/src/impls/mod.rs @@ -9,7 +9,9 @@ mod typed; mod values; pub(crate) use assertions::impl_assertions; -pub(crate) use common::{common_partial_reflect_methods, impl_full_reflect}; +pub(crate) use common::{ + common_partial_reflect_methods, impl_full_reflect, reflect_auto_registration, +}; pub(crate) use enums::impl_enum; #[cfg(feature = "functions")] pub(crate) use func::impl_function_traits; diff --git a/crates/bevy_reflect/derive/src/impls/structs.rs b/crates/bevy_reflect/derive/src/impls/structs.rs index 8095cda8c3ee8..9c8baebe27e07 100644 --- a/crates/bevy_reflect/derive/src/impls/structs.rs +++ b/crates/bevy_reflect/derive/src/impls/structs.rs @@ -1,4 +1,7 @@ -use crate::impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed}; +use crate::impls::{ + common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed, + reflect_auto_registration, +}; use crate::struct_utility::FieldAccessors; use crate::ReflectStruct; use bevy_macro_utils::fq_std::{FQBox, FQDefault, FQOption, FQResult}; @@ -60,21 +63,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS .generics() .split_for_impl(); - let auto_reflect = if ty_generics.clone().into_token_stream().is_empty() { - quote! { - #[cfg(target_arch = "wasm32")] - #bevy_reflect_path::wasm_init::wasm_init!{ - #bevy_reflect_path::AUTOMATIC_REFLECT_TYPES - .write() - .unwrap() - .push(|reg: &mut #bevy_reflect_path::TypeRegistry| reg.register::<#struct_path>()); - } - #[cfg(not(target_arch = "wasm32"))] - #bevy_reflect_path::inventory::submit!(#bevy_reflect_path::AUTOMATIC_REFLECT_TYPES(|reg: &mut #bevy_reflect_path::TypeRegistry| reg.register::<#struct_path>())); - } - } else { - quote! {} - }; + let auto_register = reflect_auto_registration(&reflect_struct.meta()); let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); @@ -89,7 +78,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS #function_impls - #auto_reflect + #auto_register impl #impl_generics #bevy_reflect_path::Struct for #struct_path #ty_generics #where_reflect_clause { fn field(&self, name: &str) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> { diff --git a/crates/bevy_reflect/derive/src/impls/tuple_structs.rs b/crates/bevy_reflect/derive/src/impls/tuple_structs.rs index fc2228e70e4c8..9e91d056aaaa9 100644 --- a/crates/bevy_reflect/derive/src/impls/tuple_structs.rs +++ b/crates/bevy_reflect/derive/src/impls/tuple_structs.rs @@ -1,4 +1,7 @@ -use crate::impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed}; +use crate::impls::{ + common_partial_reflect_methods, impl_full_reflect, reflect_auto_registration, + impl_type_path, impl_typed, +}; use crate::struct_utility::FieldAccessors; use crate::ReflectStruct; use bevy_macro_utils::fq_std::{FQBox, FQDefault, FQOption, FQResult}; @@ -48,6 +51,8 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2:: .generics() .split_for_impl(); + let auto_register = reflect_auto_registration(&reflect_struct.meta()); + let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); quote! { @@ -61,6 +66,8 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2:: #function_impls + #auto_register + impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_path #ty_generics #where_reflect_clause { fn field(&self, index: usize) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> { match index { diff --git a/crates/bevy_reflect/derive/src/impls/values.rs b/crates/bevy_reflect/derive/src/impls/values.rs index 3a804f3bed5a2..820fee6ad3805 100644 --- a/crates/bevy_reflect/derive/src/impls/values.rs +++ b/crates/bevy_reflect/derive/src/impls/values.rs @@ -1,4 +1,7 @@ -use crate::impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed}; +use crate::impls::{ + common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed, + reflect_auto_registration, +}; use crate::utility::WhereClauseOptions; use crate::ReflectMeta; use bevy_macro_utils::fq_std::{FQBox, FQClone, FQOption, FQResult}; @@ -57,6 +60,8 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream { let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); let get_type_registration_impl = meta.get_type_registration(&where_clause_options); + let auto_register = reflect_auto_registration(meta); + quote! { #get_type_registration_impl @@ -68,6 +73,8 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream { #function_impls + #auto_register + impl #impl_generics #bevy_reflect_path::PartialReflect for #type_path #ty_generics #where_reflect_clause { #[inline] fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> { diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 0f9f087257805..0a2b4b813bd41 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -9,12 +9,13 @@ use std::{ sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}, }; -#[cfg(target_arch = "wasm32")] -pub static AUTOMATIC_REFLECT_TYPES: RwLock> = RwLock::new(Vec::new()); -#[cfg(not(target_arch = "wasm32"))] -pub struct AUTOMATIC_REFLECT_TYPES(pub fn(&mut TypeRegistry)); -#[cfg(not(target_arch = "wasm32"))] -inventory::collect!(AUTOMATIC_REFLECT_TYPES); +#[cfg(target_family = "wasm")] +pub static DERIVED_REFLECT_TYPES: RwLock> = RwLock::new(Vec::new()); +#[cfg(not(target_family = "wasm"))] +#[allow(non_camel_case_types)] +pub struct DERIVED_REFLECT_TYPES(pub fn(&mut TypeRegistry)); +#[cfg(not(target_family = "wasm"))] +inventory::collect!(DERIVED_REFLECT_TYPES); /// A registry of [reflected] types. /// @@ -115,15 +116,24 @@ impl TypeRegistry { registry.register::(); registry.register::(); registry.register::(); - #[cfg(target_arch = "wasm32")] - for f in AUTOMATIC_REFLECT_TYPES.read().unwrap().iter() { - f(&mut registry) + registry.register_derived_types(); + registry + } + + pub fn register_derived_types(&mut self) { + // wasm_init must be called at least once to run all init code. + // Calling it multiple times is ok and doesn't do anything. + #[cfg(target_family = "wasm")] + wasm_init::wasm_init(); + + #[cfg(target_family = "wasm")] + for ty in DERIVED_REFLECT_TYPES.read().unwrap().iter() { + ty(self) } - #[cfg(not(target_arch = "wasm32"))] - for f in inventory::iter:: { - f.0(&mut registry) + #[cfg(not(target_family = "wasm"))] + for ty in inventory::iter:: { + ty.0(self) } - registry } /// Attempts to register the type `T` if it has not yet been registered already. From f3614b8ed3eb46f04a5593cd1d2dfbd8d3efa34f Mon Sep 17 00:00:00 2001 From: eugineerd Date: Tue, 3 Sep 2024 11:58:50 +0000 Subject: [PATCH 03/32] add `no_auto_register` reflect attribute to allow opting out of automatic registration --- .../derive/src/container_attributes.rs | 19 +++++++++++++++++++ .../bevy_reflect/derive/src/impls/common.rs | 4 ++++ 2 files changed, 23 insertions(+) diff --git a/crates/bevy_reflect/derive/src/container_attributes.rs b/crates/bevy_reflect/derive/src/container_attributes.rs index b5f0f906b3bcc..d713fc3d8dffe 100644 --- a/crates/bevy_reflect/derive/src/container_attributes.rs +++ b/crates/bevy_reflect/derive/src/container_attributes.rs @@ -24,6 +24,7 @@ mod kw { syn::custom_keyword!(PartialEq); syn::custom_keyword!(Hash); syn::custom_keyword!(no_field_bounds); + syn::custom_keyword!(no_auto_register); } // The "special" trait idents that are used internally for reflection. @@ -188,6 +189,7 @@ pub(crate) struct ContainerAttributes { type_path_attrs: TypePathAttrs, custom_where: Option, no_field_bounds: bool, + no_auto_register: bool, custom_attributes: CustomAttributes, idents: Vec, } @@ -239,6 +241,8 @@ impl ContainerAttributes { self.parse_type_path(input, trait_) } else if lookahead.peek(kw::no_field_bounds) { self.parse_no_field_bounds(input) + } else if lookahead.peek(kw::no_auto_register) { + self.parse_no_auto_register(input) } else if lookahead.peek(kw::Debug) { self.parse_debug(input) } else if lookahead.peek(kw::PartialEq) { @@ -347,6 +351,16 @@ impl ContainerAttributes { Ok(()) } + /// Parse `no_auto_register` attribute. + /// + /// Examples: + /// - `#[reflect(no_auto_register)]` + fn parse_no_auto_register(&mut self, input: ParseStream) -> syn::Result<()> { + input.parse::()?; + self.no_auto_register = true; + Ok(()) + } + /// Parse `where` attribute. /// /// Examples: @@ -526,6 +540,11 @@ impl ContainerAttributes { pub fn no_field_bounds(&self) -> bool { self.no_field_bounds } + + /// Returns true if the `no_auto_register` attribute was found on this type. + pub fn no_auto_register(&self) -> bool { + self.no_auto_register + } } /// Adds an identifier to a vector of identifiers if it is not already present. diff --git a/crates/bevy_reflect/derive/src/impls/common.rs b/crates/bevy_reflect/derive/src/impls/common.rs index 25ba89fcd35c7..245a284d4fcb7 100644 --- a/crates/bevy_reflect/derive/src/impls/common.rs +++ b/crates/bevy_reflect/derive/src/impls/common.rs @@ -158,6 +158,10 @@ pub fn common_partial_reflect_methods( } pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option { + if meta.attrs().no_auto_register() { + return None; + } + let bevy_reflect_path = meta.bevy_reflect_path(); let type_path = meta.type_path(); let (_, ty_generics, _) = meta.type_path().generics().split_for_impl(); From 73673455e9f03aa513571be022cb003a3278516e Mon Sep 17 00:00:00 2001 From: eugineerd Date: Tue, 3 Sep 2024 13:47:08 +0000 Subject: [PATCH 04/32] added `reflect_auto_register` feature to allow enabling/disabling automatic reflect registration functionality at compile time --- Cargo.toml | 3 +++ crates/bevy_internal/Cargo.toml | 3 +++ crates/bevy_reflect/Cargo.toml | 10 +++++++-- crates/bevy_reflect/derive/Cargo.toml | 2 ++ crates/bevy_reflect/derive/src/impls/enums.rs | 7 +++++-- .../bevy_reflect/derive/src/impls/structs.rs | 3 +++ .../derive/src/impls/tuple_structs.rs | 7 +++++-- .../bevy_reflect/derive/src/impls/values.rs | 3 +++ crates/bevy_reflect/src/lib.rs | 2 ++ crates/bevy_reflect/src/type_registry.rs | 21 +++++++++++++++---- 10 files changed, 51 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3708e1d45bd9d..99cb3b5ca0e75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -350,6 +350,9 @@ track_change_detection = ["bevy_internal/track_change_detection"] # Enable function reflection reflect_functions = ["bevy_internal/reflect_functions"] +# Enables automatic registration of types annotated with #[derive(Reflect)] +reflect_auto_register = ["bevy_internal/reflect_auto_register"] + [dependencies] bevy_internal = { path = "crates/bevy_internal", version = "0.15.0-dev", default-features = false } diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index 2cd6e22162319..7281a759e30f6 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -212,6 +212,9 @@ reflect_functions = [ "bevy_ecs/reflect_functions", ] +# Enables automatic registration of types annotated with #[derive(Reflect)] +reflect_auto_register = ["bevy_reflect/auto_register_derives"] + [dependencies] # bevy bevy_a11y = { path = "../bevy_a11y", version = "0.15.0-dev" } diff --git a/crates/bevy_reflect/Cargo.toml b/crates/bevy_reflect/Cargo.toml index 401ef4071a48a..20ef6c29bab52 100644 --- a/crates/bevy_reflect/Cargo.toml +++ b/crates/bevy_reflect/Cargo.toml @@ -21,6 +21,12 @@ uuid = ["dep:uuid"] documentation = ["bevy_reflect_derive/documentation"] # Enables function reflection functions = ["bevy_reflect_derive/functions"] +# Enables automatic registration of types annotated with #[derive(Reflect)] +auto_register_derives = [ + "bevy_reflect_derive/auto_register_derives", + "dep:inventory", + "dep:wasm-init", +] [dependencies] # bevy @@ -39,8 +45,8 @@ glam = { version = "0.28", features = ["serde"], optional = true } petgraph = { version = "0.6", features = ["serde-1"], optional = true } smol_str = { version = "0.2.0", optional = true } uuid = { version = "1.0", optional = true, features = ["v4", "serde"] } -inventory = "0.3" -wasm-init = "0.2" +inventory = { version = "0.3", optional = true } +wasm-init = { version = "0.2", optional = true } [dev-dependencies] ron = "0.8.0" diff --git a/crates/bevy_reflect/derive/Cargo.toml b/crates/bevy_reflect/derive/Cargo.toml index 0df5d9af2383c..259d9c1836cc8 100644 --- a/crates/bevy_reflect/derive/Cargo.toml +++ b/crates/bevy_reflect/derive/Cargo.toml @@ -17,6 +17,8 @@ default = [] documentation = [] # Enables macro logic related to function reflection functions = [] +# Enables automatic registration of types annotated with #[derive(Reflect)] +auto_register_derives = [] [dependencies] bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.15.0-dev" } diff --git a/crates/bevy_reflect/derive/src/impls/enums.rs b/crates/bevy_reflect/derive/src/impls/enums.rs index 595372cce487e..07adda9a16e1f 100644 --- a/crates/bevy_reflect/derive/src/impls/enums.rs +++ b/crates/bevy_reflect/derive/src/impls/enums.rs @@ -1,8 +1,8 @@ use crate::derive_data::{EnumVariantFields, ReflectEnum, StructField}; use crate::enum_utility::{EnumVariantOutputData, TryApplyVariantBuilder, VariantBuilder}; use crate::impls::{ - common_partial_reflect_methods, impl_full_reflect, reflect_auto_registration, - impl_type_path, impl_typed, + common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed, + reflect_auto_registration, }; use bevy_macro_utils::fq_std::{FQBox, FQOption, FQResult}; use proc_macro2::{Ident, Span}; @@ -83,6 +83,9 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream let (impl_generics, ty_generics, where_clause) = reflect_enum.meta().type_path().generics().split_for_impl(); + #[cfg(not(feature = "auto_register_derives"))] + let auto_register = None::; + #[cfg(feature = "auto_register_derives")] let auto_register = reflect_auto_registration(&reflect_enum.meta()); let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); diff --git a/crates/bevy_reflect/derive/src/impls/structs.rs b/crates/bevy_reflect/derive/src/impls/structs.rs index 9c8baebe27e07..3a72c89711e8a 100644 --- a/crates/bevy_reflect/derive/src/impls/structs.rs +++ b/crates/bevy_reflect/derive/src/impls/structs.rs @@ -63,6 +63,9 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS .generics() .split_for_impl(); + #[cfg(not(feature = "auto_register_derives"))] + let auto_register = None::; + #[cfg(feature = "auto_register_derives")] let auto_register = reflect_auto_registration(&reflect_struct.meta()); let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); diff --git a/crates/bevy_reflect/derive/src/impls/tuple_structs.rs b/crates/bevy_reflect/derive/src/impls/tuple_structs.rs index 9e91d056aaaa9..90751b00ffce6 100644 --- a/crates/bevy_reflect/derive/src/impls/tuple_structs.rs +++ b/crates/bevy_reflect/derive/src/impls/tuple_structs.rs @@ -1,6 +1,6 @@ use crate::impls::{ - common_partial_reflect_methods, impl_full_reflect, reflect_auto_registration, - impl_type_path, impl_typed, + common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed, + reflect_auto_registration, }; use crate::struct_utility::FieldAccessors; use crate::ReflectStruct; @@ -51,6 +51,9 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2:: .generics() .split_for_impl(); + #[cfg(not(feature = "auto_register_derives"))] + let auto_register = None::; + #[cfg(feature = "auto_register_derives")] let auto_register = reflect_auto_registration(&reflect_struct.meta()); let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); diff --git a/crates/bevy_reflect/derive/src/impls/values.rs b/crates/bevy_reflect/derive/src/impls/values.rs index 820fee6ad3805..9e190182e3478 100644 --- a/crates/bevy_reflect/derive/src/impls/values.rs +++ b/crates/bevy_reflect/derive/src/impls/values.rs @@ -60,6 +60,9 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream { let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); let get_type_registration_impl = meta.get_type_registration(&where_clause_options); + #[cfg(not(feature = "auto_register_derives"))] + let auto_register = None::; + #[cfg(feature = "auto_register_derives")] let auto_register = reflect_auto_registration(meta); quote! { diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 240bc3bd6112a..5788047e101cb 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -597,7 +597,9 @@ pub use type_registry::*; pub use bevy_reflect_derive::*; pub use erased_serde; +#[cfg(feature = "auto_register_derives")] pub extern crate inventory; +#[cfg(feature = "auto_register_derives")] pub extern crate wasm_init; extern crate alloc; diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 0a2b4b813bd41..0745727f64c72 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -9,12 +9,13 @@ use std::{ sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}, }; -#[cfg(target_family = "wasm")] +/// Stores registration functions of all types that must be automatically registered. +#[cfg(all(target_family = "wasm", feature = "auto_register_derives"))] pub static DERIVED_REFLECT_TYPES: RwLock> = RwLock::new(Vec::new()); -#[cfg(not(target_family = "wasm"))] +#[cfg(all(not(target_family = "wasm"), feature = "auto_register_derives"))] #[allow(non_camel_case_types)] pub struct DERIVED_REFLECT_TYPES(pub fn(&mut TypeRegistry)); -#[cfg(not(target_family = "wasm"))] +#[cfg(all(not(target_family = "wasm"), feature = "auto_register_derives"))] inventory::collect!(DERIVED_REFLECT_TYPES); /// A registry of [reflected] types. @@ -117,10 +118,19 @@ impl TypeRegistry { registry.register::(); registry.register::(); registry.register_derived_types(); + registry } - pub fn register_derived_types(&mut self) { + /// Register all non-generic types annotated with `#[derive(Reflect)]`. + /// + /// This function does nothing if `auto_register_derives` feature is not enabled. + fn register_derived_types(&mut self) { + self.register_derived_types_internal() + } + + #[cfg(feature = "auto_register_derives")] + fn register_derived_types_internal(&mut self) { // wasm_init must be called at least once to run all init code. // Calling it multiple times is ok and doesn't do anything. #[cfg(target_family = "wasm")] @@ -136,6 +146,9 @@ impl TypeRegistry { } } + #[cfg(not(feature = "auto_register_derives"))] + fn register_derived_types_internal(&mut self) {} + /// Attempts to register the type `T` if it has not yet been registered already. /// /// This will also recursively register any type dependencies as specified by [`GetTypeRegistration::register_type_dependencies`]. From 16ee7cfcc251fe7bdda748e65173966ab976dbd6 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Wed, 4 Sep 2024 08:16:54 +0000 Subject: [PATCH 05/32] reduce wasm overhead --- crates/bevy_reflect/derive/src/impls/common.rs | 3 ++- crates/bevy_reflect/src/type_registry.rs | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/bevy_reflect/derive/src/impls/common.rs b/crates/bevy_reflect/derive/src/impls/common.rs index 245a284d4fcb7..9e0d4bc207028 100644 --- a/crates/bevy_reflect/derive/src/impls/common.rs +++ b/crates/bevy_reflect/derive/src/impls/common.rs @@ -173,10 +173,11 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option()); + .push((TypeRegistration::of::<#type_path>(), #type_path::register_type_dependencies)); } #[cfg(not(target_family = "wasm"))] #bevy_reflect_path::inventory::submit!( diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 0745727f64c72..05c4309fbf7b5 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -11,7 +11,8 @@ use std::{ /// Stores registration functions of all types that must be automatically registered. #[cfg(all(target_family = "wasm", feature = "auto_register_derives"))] -pub static DERIVED_REFLECT_TYPES: RwLock> = RwLock::new(Vec::new()); +pub static DERIVED_REFLECT_TYPES: RwLock> = + RwLock::new(Vec::new()); #[cfg(all(not(target_family = "wasm"), feature = "auto_register_derives"))] #[allow(non_camel_case_types)] pub struct DERIVED_REFLECT_TYPES(pub fn(&mut TypeRegistry)); @@ -137,8 +138,9 @@ impl TypeRegistry { wasm_init::wasm_init(); #[cfg(target_family = "wasm")] - for ty in DERIVED_REFLECT_TYPES.read().unwrap().iter() { - ty(self) + for (ty, dep_reg) in DERIVED_REFLECT_TYPES.read().unwrap().iter() { + self.add_registration(ty.clone()); + dep_reg(self); } #[cfg(not(target_family = "wasm"))] for ty in inventory::iter:: { From 35f0591510d1bd8abc7c976f9843edc9b7c3d4a4 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Wed, 4 Sep 2024 08:20:25 +0000 Subject: [PATCH 06/32] run `cargo run -p build-templated-pages -- update features` --- docs/cargo_features.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/cargo_features.md b/docs/cargo_features.md index c360c2eb8cfba..0a0a788b2f082 100644 --- a/docs/cargo_features.md +++ b/docs/cargo_features.md @@ -74,6 +74,7 @@ The default feature set enables most of the expected features of a game engine, |pbr_multi_layer_material_textures|Enable support for multi-layer material textures in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs| |pbr_transmission_textures|Enable support for transmission-related textures in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs| |pnm|PNM image format support, includes pam, pbm, pgm and ppm| +|reflect_auto_register|Enables automatic registration of types annotated with #[derive(Reflect)]| |reflect_functions|Enable function reflection| |serialize|Enable serialization support through serde| |shader_format_glsl|Enable support for shaders in GLSL| From 119a59808ca8ced530eac36290bc32e2522937b1 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Wed, 4 Sep 2024 19:02:32 +0000 Subject: [PATCH 07/32] fix not registering `TypeData` --- .../bevy_reflect/derive/src/impls/common.rs | 11 +++++----- crates/bevy_reflect/src/type_registry.rs | 20 +++++++++++-------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/crates/bevy_reflect/derive/src/impls/common.rs b/crates/bevy_reflect/derive/src/impls/common.rs index 9e0d4bc207028..666285133a34b 100644 --- a/crates/bevy_reflect/derive/src/impls/common.rs +++ b/crates/bevy_reflect/derive/src/impls/common.rs @@ -173,16 +173,15 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option(), #type_path::register_type_dependencies)); + .expect("Failed to get lock to write type for automatic type registration") + .push(|registry| registry.register::<#type_path>()); } #[cfg(not(target_family = "wasm"))] #bevy_reflect_path::inventory::submit!( - #bevy_reflect_path::DERIVED_REFLECT_TYPES( - |reg: &mut #bevy_reflect_path::TypeRegistry| reg.register::<#type_path>() + #bevy_reflect_path::AUTOMATIC_REFLECT_REGISTRATIONS( + |registry| registry.register::<#type_path>() ) ); }) diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 05c4309fbf7b5..ce8933b65efab 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -11,13 +11,13 @@ use std::{ /// Stores registration functions of all types that must be automatically registered. #[cfg(all(target_family = "wasm", feature = "auto_register_derives"))] -pub static DERIVED_REFLECT_TYPES: RwLock> = +pub static AUTOMATIC_REFLECT_REGISTRATIONS: RwLock> = RwLock::new(Vec::new()); #[cfg(all(not(target_family = "wasm"), feature = "auto_register_derives"))] #[allow(non_camel_case_types)] -pub struct DERIVED_REFLECT_TYPES(pub fn(&mut TypeRegistry)); +pub struct AUTOMATIC_REFLECT_REGISTRATIONS(pub fn(&mut TypeRegistry)); #[cfg(all(not(target_family = "wasm"), feature = "auto_register_derives"))] -inventory::collect!(DERIVED_REFLECT_TYPES); +inventory::collect!(AUTOMATIC_REFLECT_REGISTRATIONS); /// A registry of [reflected] types. /// @@ -138,13 +138,17 @@ impl TypeRegistry { wasm_init::wasm_init(); #[cfg(target_family = "wasm")] - for (ty, dep_reg) in DERIVED_REFLECT_TYPES.read().unwrap().iter() { - self.add_registration(ty.clone()); - dep_reg(self); + for registration_fn in AUTOMATIC_REFLECT_REGISTRATIONS + .read() + .expect("Failed to get lock to read types for automatic type registration") + .iter() + { + registration_fn(self) } + #[cfg(not(target_family = "wasm"))] - for ty in inventory::iter:: { - ty.0(self) + for registration_fn in inventory::iter:: { + registration_fn.0(self) } } From 53696f75e0dd660cd87a0702329ae20ad5d9ac36 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 5 Sep 2024 14:28:06 +0000 Subject: [PATCH 08/32] add doc test and remove feature-gating --- Cargo.toml | 3 - crates/bevy_app/src/app.rs | 1 - crates/bevy_internal/Cargo.toml | 3 - crates/bevy_reflect/Cargo.toml | 9 +-- crates/bevy_reflect/derive/Cargo.toml | 2 - crates/bevy_reflect/derive/src/impls/enums.rs | 3 - .../bevy_reflect/derive/src/impls/structs.rs | 3 - .../derive/src/impls/tuple_structs.rs | 3 - .../bevy_reflect/derive/src/impls/values.rs | 3 - crates/bevy_reflect/src/lib.rs | 2 - crates/bevy_reflect/src/type_registry.rs | 62 +++++++++++++------ docs/cargo_features.md | 1 - 12 files changed, 46 insertions(+), 49 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 99cb3b5ca0e75..3708e1d45bd9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -350,9 +350,6 @@ track_change_detection = ["bevy_internal/track_change_detection"] # Enable function reflection reflect_functions = ["bevy_internal/reflect_functions"] -# Enables automatic registration of types annotated with #[derive(Reflect)] -reflect_auto_register = ["bevy_internal/reflect_auto_register"] - [dependencies] bevy_internal = { path = "crates/bevy_internal", version = "0.15.0-dev", default-features = false } diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 2b7a088dff70c..da41efbc152c4 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -92,7 +92,6 @@ impl Debug for App { impl Default for App { fn default() -> Self { let mut app = App::empty(); - app.sub_apps.main.update_schedule = Some(Main.intern()); #[cfg(feature = "bevy_reflect")] diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index 7281a759e30f6..2cd6e22162319 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -212,9 +212,6 @@ reflect_functions = [ "bevy_ecs/reflect_functions", ] -# Enables automatic registration of types annotated with #[derive(Reflect)] -reflect_auto_register = ["bevy_reflect/auto_register_derives"] - [dependencies] # bevy bevy_a11y = { path = "../bevy_a11y", version = "0.15.0-dev" } diff --git a/crates/bevy_reflect/Cargo.toml b/crates/bevy_reflect/Cargo.toml index 20ef6c29bab52..30655cee3b09a 100644 --- a/crates/bevy_reflect/Cargo.toml +++ b/crates/bevy_reflect/Cargo.toml @@ -22,11 +22,6 @@ documentation = ["bevy_reflect_derive/documentation"] # Enables function reflection functions = ["bevy_reflect_derive/functions"] # Enables automatic registration of types annotated with #[derive(Reflect)] -auto_register_derives = [ - "bevy_reflect_derive/auto_register_derives", - "dep:inventory", - "dep:wasm-init", -] [dependencies] # bevy @@ -45,8 +40,8 @@ glam = { version = "0.28", features = ["serde"], optional = true } petgraph = { version = "0.6", features = ["serde-1"], optional = true } smol_str = { version = "0.2.0", optional = true } uuid = { version = "1.0", optional = true, features = ["v4", "serde"] } -inventory = { version = "0.3", optional = true } -wasm-init = { version = "0.2", optional = true } +inventory = "0.3" +wasm-init = "0.2" [dev-dependencies] ron = "0.8.0" diff --git a/crates/bevy_reflect/derive/Cargo.toml b/crates/bevy_reflect/derive/Cargo.toml index 259d9c1836cc8..0df5d9af2383c 100644 --- a/crates/bevy_reflect/derive/Cargo.toml +++ b/crates/bevy_reflect/derive/Cargo.toml @@ -17,8 +17,6 @@ default = [] documentation = [] # Enables macro logic related to function reflection functions = [] -# Enables automatic registration of types annotated with #[derive(Reflect)] -auto_register_derives = [] [dependencies] bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.15.0-dev" } diff --git a/crates/bevy_reflect/derive/src/impls/enums.rs b/crates/bevy_reflect/derive/src/impls/enums.rs index 07adda9a16e1f..bc09ecd02d933 100644 --- a/crates/bevy_reflect/derive/src/impls/enums.rs +++ b/crates/bevy_reflect/derive/src/impls/enums.rs @@ -83,9 +83,6 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream let (impl_generics, ty_generics, where_clause) = reflect_enum.meta().type_path().generics().split_for_impl(); - #[cfg(not(feature = "auto_register_derives"))] - let auto_register = None::; - #[cfg(feature = "auto_register_derives")] let auto_register = reflect_auto_registration(&reflect_enum.meta()); let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); diff --git a/crates/bevy_reflect/derive/src/impls/structs.rs b/crates/bevy_reflect/derive/src/impls/structs.rs index 3a72c89711e8a..9c8baebe27e07 100644 --- a/crates/bevy_reflect/derive/src/impls/structs.rs +++ b/crates/bevy_reflect/derive/src/impls/structs.rs @@ -63,9 +63,6 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS .generics() .split_for_impl(); - #[cfg(not(feature = "auto_register_derives"))] - let auto_register = None::; - #[cfg(feature = "auto_register_derives")] let auto_register = reflect_auto_registration(&reflect_struct.meta()); let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); diff --git a/crates/bevy_reflect/derive/src/impls/tuple_structs.rs b/crates/bevy_reflect/derive/src/impls/tuple_structs.rs index 90751b00ffce6..04c2cfb3068f6 100644 --- a/crates/bevy_reflect/derive/src/impls/tuple_structs.rs +++ b/crates/bevy_reflect/derive/src/impls/tuple_structs.rs @@ -51,9 +51,6 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2:: .generics() .split_for_impl(); - #[cfg(not(feature = "auto_register_derives"))] - let auto_register = None::; - #[cfg(feature = "auto_register_derives")] let auto_register = reflect_auto_registration(&reflect_struct.meta()); let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); diff --git a/crates/bevy_reflect/derive/src/impls/values.rs b/crates/bevy_reflect/derive/src/impls/values.rs index 9e190182e3478..820fee6ad3805 100644 --- a/crates/bevy_reflect/derive/src/impls/values.rs +++ b/crates/bevy_reflect/derive/src/impls/values.rs @@ -60,9 +60,6 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream { let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); let get_type_registration_impl = meta.get_type_registration(&where_clause_options); - #[cfg(not(feature = "auto_register_derives"))] - let auto_register = None::; - #[cfg(feature = "auto_register_derives")] let auto_register = reflect_auto_registration(meta); quote! { diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 5788047e101cb..240bc3bd6112a 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -597,9 +597,7 @@ pub use type_registry::*; pub use bevy_reflect_derive::*; pub use erased_serde; -#[cfg(feature = "auto_register_derives")] pub extern crate inventory; -#[cfg(feature = "auto_register_derives")] pub extern crate wasm_init; extern crate alloc; diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index ce8933b65efab..21a1e68d3def1 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -9,15 +9,15 @@ use std::{ sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}, }; -/// Stores registration functions of all types that must be automatically registered. -#[cfg(all(target_family = "wasm", feature = "auto_register_derives"))] -pub static AUTOMATIC_REFLECT_REGISTRATIONS: RwLock> = - RwLock::new(Vec::new()); -#[cfg(all(not(target_family = "wasm"), feature = "auto_register_derives"))] +/// Stores registration functions of all reflect types that can be automatically registered. +#[cfg(not(target_family = "wasm"))] #[allow(non_camel_case_types)] pub struct AUTOMATIC_REFLECT_REGISTRATIONS(pub fn(&mut TypeRegistry)); -#[cfg(all(not(target_family = "wasm"), feature = "auto_register_derives"))] +#[cfg(not(target_family = "wasm"))] inventory::collect!(AUTOMATIC_REFLECT_REGISTRATIONS); +#[cfg(target_family = "wasm")] +pub static AUTOMATIC_REFLECT_REGISTRATIONS: RwLock> = + RwLock::new(Vec::new()); /// A registry of [reflected] types. /// @@ -83,7 +83,10 @@ pub trait GetTypeRegistration: 'static { impl Default for TypeRegistry { fn default() -> Self { - Self::new() + let mut registry = Self::new(); + registry.register_derived_types(); + + registry } } @@ -118,20 +121,44 @@ impl TypeRegistry { registry.register::(); registry.register::(); registry.register::(); - registry.register_derived_types(); registry } /// Register all non-generic types annotated with `#[derive(Reflect)]`. /// - /// This function does nothing if `auto_register_derives` feature is not enabled. - fn register_derived_types(&mut self) { - self.register_derived_types_internal() - } - - #[cfg(feature = "auto_register_derives")] - fn register_derived_types_internal(&mut self) { + /// Calling this method is equivalent to calling [`register`](Self::register) on all types without generic parameters + /// that derived [`Refelct`](crate::Reflect) trait. + /// + /// This method is supported on Linux, macOS, iOS, Android and Windows via the `inventory` crate, + /// and on wasm via the `wasm-init` crate. It does nothing on platforms not supported by either of those crates. + /// + /// # Example + /// + /// ``` + /// # use std::any::TypeId; + /// # use bevy_reflect::{Reflect, TypeRegistry, std_traits::ReflectDefault}; + /// #[derive(Reflect, Default)] + /// #[reflect(Default)] + /// struct Foo { + /// name: Option, + /// value: i32 + /// } + /// + /// let mut type_registry = TypeRegistry::empty(); + /// type_registry.register_derived_types(); + /// + /// // The main type + /// assert!(type_registry.contains(TypeId::of::())); + /// + /// // Its type dependencies + /// assert!(type_registry.contains(TypeId::of::>())); + /// assert!(type_registry.contains(TypeId::of::())); + /// + /// // Its type data + /// assert!(type_registry.get_type_data::(TypeId::of::()).is_some()); + /// ``` + pub fn register_derived_types(&mut self) { // wasm_init must be called at least once to run all init code. // Calling it multiple times is ok and doesn't do anything. #[cfg(target_family = "wasm")] @@ -146,15 +173,14 @@ impl TypeRegistry { registration_fn(self) } + dbg!("Running..."); #[cfg(not(target_family = "wasm"))] for registration_fn in inventory::iter:: { + dbg!("{:?}", registration_fn.0); registration_fn.0(self) } } - #[cfg(not(feature = "auto_register_derives"))] - fn register_derived_types_internal(&mut self) {} - /// Attempts to register the type `T` if it has not yet been registered already. /// /// This will also recursively register any type dependencies as specified by [`GetTypeRegistration::register_type_dependencies`]. diff --git a/docs/cargo_features.md b/docs/cargo_features.md index 0a0a788b2f082..c360c2eb8cfba 100644 --- a/docs/cargo_features.md +++ b/docs/cargo_features.md @@ -74,7 +74,6 @@ The default feature set enables most of the expected features of a game engine, |pbr_multi_layer_material_textures|Enable support for multi-layer material textures in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs| |pbr_transmission_textures|Enable support for transmission-related textures in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs| |pnm|PNM image format support, includes pam, pbm, pgm and ppm| -|reflect_auto_register|Enables automatic registration of types annotated with #[derive(Reflect)]| |reflect_functions|Enable function reflection| |serialize|Enable serialization support through serde| |shader_format_glsl|Enable support for shaders in GLSL| From 7d761e170e69cbe5a2a60a8f1ab7e313de6f1931 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 5 Sep 2024 14:44:29 +0000 Subject: [PATCH 09/32] remove `dbg!` --- crates/bevy_reflect/src/type_registry.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 21a1e68d3def1..625e8816fd063 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -173,10 +173,8 @@ impl TypeRegistry { registration_fn(self) } - dbg!("Running..."); #[cfg(not(target_family = "wasm"))] for registration_fn in inventory::iter:: { - dbg!("{:?}", registration_fn.0); registration_fn.0(self) } } From d8eb59735c5500dc1909740fbf598bcd18926b9c Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 5 Sep 2024 14:49:15 +0000 Subject: [PATCH 10/32] update examples for automatic reflect type registration --- examples/reflection/dynamic_types.rs | 3 +-- examples/reflection/reflection.rs | 10 +--------- examples/reflection/type_data.rs | 2 +- examples/scene/scene.rs | 3 --- 4 files changed, 3 insertions(+), 15 deletions(-) diff --git a/examples/reflection/dynamic_types.rs b/examples/reflection/dynamic_types.rs index 18227cb59810e..98fdc21ceec0e 100644 --- a/examples/reflection/dynamic_types.rs +++ b/examples/reflection/dynamic_types.rs @@ -67,8 +67,7 @@ fn main() { // They generally can't know how to construct a type ahead of time, // so they instead build and return these dynamic representations. let input = "(id: 123)"; - let mut registry = TypeRegistry::default(); - registry.register::(); + let registry = TypeRegistry::default(); let registration = registry.get(std::any::TypeId::of::()).unwrap(); let deserialized = TypedReflectDeserializer::new(registration, ®istry) .deserialize(&mut ron::Deserializer::from_str(input).unwrap()) diff --git a/examples/reflection/reflection.rs b/examples/reflection/reflection.rs index 4cb6003116e1c..e2a45020434b2 100644 --- a/examples/reflection/reflection.rs +++ b/examples/reflection/reflection.rs @@ -4,8 +4,6 @@ //! by their string name. Reflection is a core part of Bevy and enables a number of interesting //! features (like scenes). -use std::any::Any; - use bevy::{ prelude::*, reflect::{ @@ -18,15 +16,13 @@ use serde::de::DeserializeSeed; fn main() { App::new() .add_plugins(DefaultPlugins) - // Bar will be automatically registered as it's a dependency of Foo - // .register_type::() .add_systems(Startup, setup) .run(); } /// Deriving `Reflect` implements the relevant reflection traits. In this case, it implements the /// `Reflect` trait and the `Struct` trait `derive(Reflect)` assumes that all fields also implement -/// Reflect. +/// Reflect. All types without generics that `derive(Reflect)` are automatically registered. /// /// All fields in a reflected item will need to be `Reflect` as well. You can opt a field out of /// reflection by using the `#[reflect(ignore)]` attribute. @@ -103,8 +99,6 @@ fn setup(type_registry: Res) { let mut deserializer = ron::de::Deserializer::from_str(&ron_string).unwrap(); let reflect_value = reflect_deserializer.deserialize(&mut deserializer).unwrap(); - assert!(type_registry.contains(value.type_id())); - // Deserializing returns a `Box` value. // Generally, deserializing a value will return the "dynamic" variant of a type. // For example, deserializing a struct will return the DynamicStruct type. @@ -122,6 +116,4 @@ fn setup(type_registry: Res) { // By "patching" `Foo` with the deserialized DynamicStruct, we can "Deserialize" Foo. // This means we can serialize and deserialize with a single `Reflect` derive! value.apply(&*reflect_value); - - info!("{}", type_registry.iter().collect::>().len()); } diff --git a/examples/reflection/type_data.rs b/examples/reflection/type_data.rs index f1f9022b233b7..7f34e31236e5e 100644 --- a/examples/reflection/type_data.rs +++ b/examples/reflection/type_data.rs @@ -75,7 +75,7 @@ fn main() { // With all this done, we're ready to make use of `ReflectDamageable`! // It starts with registering our type along with its type data: - let mut registry = TypeRegistry::default(); + let mut registry = TypeRegistry::empty(); registry.register::(); registry.register_type_data::(); diff --git a/examples/scene/scene.rs b/examples/scene/scene.rs index e5f18d1bdd4ad..b6ad596f5ae75 100644 --- a/examples/scene/scene.rs +++ b/examples/scene/scene.rs @@ -5,9 +5,6 @@ use std::{fs::File, io::Write}; fn main() { App::new() .add_plugins(DefaultPlugins) - .register_type::() - .register_type::() - .register_type::() .add_systems( Startup, (save_scene_system, load_scene_system, infotext_system), From 1da577ac682bd36a93eda1e1e8dde361f5d0c6c0 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 5 Sep 2024 14:51:18 +0000 Subject: [PATCH 11/32] fix typo --- crates/bevy_reflect/src/type_registry.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 625e8816fd063..cc75b505cdc23 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -128,7 +128,7 @@ impl TypeRegistry { /// Register all non-generic types annotated with `#[derive(Reflect)]`. /// /// Calling this method is equivalent to calling [`register`](Self::register) on all types without generic parameters - /// that derived [`Refelct`](crate::Reflect) trait. + /// that derived [`Reflect`](crate::Reflect) trait. /// /// This method is supported on Linux, macOS, iOS, Android and Windows via the `inventory` crate, /// and on wasm via the `wasm-init` crate. It does nothing on platforms not supported by either of those crates. From 44857984395051a98952c3eeded2e829d7429a15 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 5 Sep 2024 15:07:50 +0000 Subject: [PATCH 12/32] remove outdated comment from reflect Cargo.toml --- crates/bevy_reflect/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/bevy_reflect/Cargo.toml b/crates/bevy_reflect/Cargo.toml index 30655cee3b09a..401ef4071a48a 100644 --- a/crates/bevy_reflect/Cargo.toml +++ b/crates/bevy_reflect/Cargo.toml @@ -21,7 +21,6 @@ uuid = ["dep:uuid"] documentation = ["bevy_reflect_derive/documentation"] # Enables function reflection functions = ["bevy_reflect_derive/functions"] -# Enables automatic registration of types annotated with #[derive(Reflect)] [dependencies] # bevy From ef6bb2de5cbd8f91e275d1d5779ddca6c99afaf5 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 5 Sep 2024 17:39:29 +0000 Subject: [PATCH 13/32] remove needless borrow --- crates/bevy_reflect/derive/src/impls/enums.rs | 2 +- crates/bevy_reflect/derive/src/impls/structs.rs | 2 +- crates/bevy_reflect/derive/src/impls/tuple_structs.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_reflect/derive/src/impls/enums.rs b/crates/bevy_reflect/derive/src/impls/enums.rs index bc09ecd02d933..ae695fd5f4af3 100644 --- a/crates/bevy_reflect/derive/src/impls/enums.rs +++ b/crates/bevy_reflect/derive/src/impls/enums.rs @@ -83,7 +83,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream let (impl_generics, ty_generics, where_clause) = reflect_enum.meta().type_path().generics().split_for_impl(); - let auto_register = reflect_auto_registration(&reflect_enum.meta()); + let auto_register = reflect_auto_registration(reflect_enum.meta()); let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); diff --git a/crates/bevy_reflect/derive/src/impls/structs.rs b/crates/bevy_reflect/derive/src/impls/structs.rs index 9c8baebe27e07..444a3f8e6d9ad 100644 --- a/crates/bevy_reflect/derive/src/impls/structs.rs +++ b/crates/bevy_reflect/derive/src/impls/structs.rs @@ -63,7 +63,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS .generics() .split_for_impl(); - let auto_register = reflect_auto_registration(&reflect_struct.meta()); + let auto_register = reflect_auto_registration(reflect_struct.meta()); let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); diff --git a/crates/bevy_reflect/derive/src/impls/tuple_structs.rs b/crates/bevy_reflect/derive/src/impls/tuple_structs.rs index 04c2cfb3068f6..b6695b06cb81d 100644 --- a/crates/bevy_reflect/derive/src/impls/tuple_structs.rs +++ b/crates/bevy_reflect/derive/src/impls/tuple_structs.rs @@ -51,7 +51,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2:: .generics() .split_for_impl(); - let auto_register = reflect_auto_registration(&reflect_struct.meta()); + let auto_register = reflect_auto_registration(reflect_struct.meta()); let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); From 5fa15723e3c3882902df06623050d77248171458 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 5 Sep 2024 17:40:41 +0000 Subject: [PATCH 14/32] fix from check-doc --- crates/bevy_reflect/src/type_registry.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index cc75b505cdc23..e57e720d2e5c8 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -128,7 +128,7 @@ impl TypeRegistry { /// Register all non-generic types annotated with `#[derive(Reflect)]`. /// /// Calling this method is equivalent to calling [`register`](Self::register) on all types without generic parameters - /// that derived [`Reflect`](crate::Reflect) trait. + /// that derived [`Reflect`] trait. /// /// This method is supported on Linux, macOS, iOS, Android and Windows via the `inventory` crate, /// and on wasm via the `wasm-init` crate. It does nothing on platforms not supported by either of those crates. From d2d329038ebdc3ec8dcc8a274861493a84e44413 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 5 Sep 2024 17:55:39 +0000 Subject: [PATCH 15/32] move calling automatic type registration to `AppTypeRegistry`. Automatic reflect registration registeres all reflect types within testing module. This creates multiple type registrations with the same type path, which breaks `ReflectDeserializer` in `bevy_reflect` tests. --- crates/bevy_ecs/src/reflect/mod.rs | 10 +++++++++- crates/bevy_reflect/src/type_registry.rs | 6 +----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ecs/src/reflect/mod.rs b/crates/bevy_ecs/src/reflect/mod.rs index ae02af891314f..77547baf56ee4 100644 --- a/crates/bevy_ecs/src/reflect/mod.rs +++ b/crates/bevy_ecs/src/reflect/mod.rs @@ -26,9 +26,17 @@ pub use resource::{ReflectResource, ReflectResourceFns}; /// A [`Resource`] storing [`TypeRegistry`] for /// type registrations relevant to a whole app. -#[derive(Resource, Clone, Default)] +#[derive(Resource, Clone)] pub struct AppTypeRegistry(pub TypeRegistryArc); +impl Default for AppTypeRegistry { + fn default() -> Self { + let registry_arc = TypeRegistryArc::default(); + registry_arc.write().register_derived_types(); + Self(registry_arc) + } +} + impl Deref for AppTypeRegistry { type Target = TypeRegistryArc; diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index e57e720d2e5c8..c3b2ee1d79fd5 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -83,10 +83,7 @@ pub trait GetTypeRegistration: 'static { impl Default for TypeRegistry { fn default() -> Self { - let mut registry = Self::new(); - registry.register_derived_types(); - - registry + Self::new() } } @@ -121,7 +118,6 @@ impl TypeRegistry { registry.register::(); registry.register::(); registry.register::(); - registry } From 878e8d31b55ae4ae1f17d413114b7b152f37feb5 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 5 Sep 2024 17:59:28 +0000 Subject: [PATCH 16/32] more clippy fixes --- crates/bevy_reflect/src/type_registry.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index c3b2ee1d79fd5..45b16b3fa09f0 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -166,12 +166,12 @@ impl TypeRegistry { .expect("Failed to get lock to read types for automatic type registration") .iter() { - registration_fn(self) + registration_fn(self); } #[cfg(not(target_family = "wasm"))] for registration_fn in inventory::iter:: { - registration_fn.0(self) + registration_fn.0(self); } } From b518d5ee69b90b637356533c09ecfcc6ad2ee373 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 5 Sep 2024 18:19:26 +0000 Subject: [PATCH 17/32] Move automatic types registration to app creation Registering types on `AppTypeRegistry` creation breaks dynamic scene builder. --- crates/bevy_app/src/app.rs | 2 +- crates/bevy_ecs/src/reflect/mod.rs | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index da41efbc152c4..86202aa33bd2c 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -95,7 +95,7 @@ impl Default for App { app.sub_apps.main.update_schedule = Some(Main.intern()); #[cfg(feature = "bevy_reflect")] - app.init_resource::(); + app.insert_resource(AppTypeRegistry::new_with_derived_types()); #[cfg(feature = "reflect_functions")] app.init_resource::(); diff --git a/crates/bevy_ecs/src/reflect/mod.rs b/crates/bevy_ecs/src/reflect/mod.rs index 77547baf56ee4..6e0088aee4126 100644 --- a/crates/bevy_ecs/src/reflect/mod.rs +++ b/crates/bevy_ecs/src/reflect/mod.rs @@ -26,17 +26,9 @@ pub use resource::{ReflectResource, ReflectResourceFns}; /// A [`Resource`] storing [`TypeRegistry`] for /// type registrations relevant to a whole app. -#[derive(Resource, Clone)] +#[derive(Resource, Clone, Default)] pub struct AppTypeRegistry(pub TypeRegistryArc); -impl Default for AppTypeRegistry { - fn default() -> Self { - let registry_arc = TypeRegistryArc::default(); - registry_arc.write().register_derived_types(); - Self(registry_arc) - } -} - impl Deref for AppTypeRegistry { type Target = TypeRegistryArc; @@ -53,6 +45,17 @@ impl DerefMut for AppTypeRegistry { } } +impl AppTypeRegistry { + /// Creates [`AppTypeRegistry`] and calls [`register_derived_types`](TypeRegistry::register_derived_types) on it. + /// + /// See [`register_derived_types`](TypeRegistry::register_derived_types) for more details. + pub fn new_with_derived_types() -> Self { + let app_registry = AppTypeRegistry::default(); + app_registry.write().register_derived_types(); + app_registry + } +} + /// A [`Resource`] storing [`FunctionRegistry`] for /// function registrations relevant to a whole app. /// From edf5f8740e572b9386e0ce5225c57d55af3f68ab Mon Sep 17 00:00:00 2001 From: eugineerd Date: Fri, 6 Sep 2024 08:38:39 +0000 Subject: [PATCH 18/32] revert changes to examples that use `TypeRegistry` --- examples/reflection/dynamic_types.rs | 3 ++- examples/reflection/type_data.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/reflection/dynamic_types.rs b/examples/reflection/dynamic_types.rs index 98fdc21ceec0e..18227cb59810e 100644 --- a/examples/reflection/dynamic_types.rs +++ b/examples/reflection/dynamic_types.rs @@ -67,7 +67,8 @@ fn main() { // They generally can't know how to construct a type ahead of time, // so they instead build and return these dynamic representations. let input = "(id: 123)"; - let registry = TypeRegistry::default(); + let mut registry = TypeRegistry::default(); + registry.register::(); let registration = registry.get(std::any::TypeId::of::()).unwrap(); let deserialized = TypedReflectDeserializer::new(registration, ®istry) .deserialize(&mut ron::Deserializer::from_str(input).unwrap()) diff --git a/examples/reflection/type_data.rs b/examples/reflection/type_data.rs index 7f34e31236e5e..f1f9022b233b7 100644 --- a/examples/reflection/type_data.rs +++ b/examples/reflection/type_data.rs @@ -75,7 +75,7 @@ fn main() { // With all this done, we're ready to make use of `ReflectDamageable`! // It starts with registering our type along with its type data: - let mut registry = TypeRegistry::empty(); + let mut registry = TypeRegistry::default(); registry.register::(); registry.register_type_data::(); From e9f67f033163547b36118b82f60dd6ded62bfe36 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Fri, 6 Sep 2024 09:10:05 +0000 Subject: [PATCH 19/32] hide automatic reflect registration in `__macro_exports` --- crates/bevy_reflect/derive/src/impls/common.rs | 18 +++++++++--------- crates/bevy_reflect/src/lib.rs | 16 +++++++++++++--- crates/bevy_reflect/src/type_registry.rs | 18 +++++------------- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/crates/bevy_reflect/derive/src/impls/common.rs b/crates/bevy_reflect/derive/src/impls/common.rs index 666285133a34b..4d87b0eea3152 100644 --- a/crates/bevy_reflect/derive/src/impls/common.rs +++ b/crates/bevy_reflect/derive/src/impls/common.rs @@ -164,24 +164,24 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option()); + .expect("Failed to get write lock for automatic reflect type registration") + .push(<#type_path as #bevy_reflect_path::__macro_exports::RegisterForReflection>::__register); } #[cfg(not(target_family = "wasm"))] - #bevy_reflect_path::inventory::submit!( - #bevy_reflect_path::AUTOMATIC_REFLECT_REGISTRATIONS( - |registry| registry.register::<#type_path>() + #bevy_reflect_path::__macro_exports::inventory::submit!( + #bevy_reflect_path::__macro_exports::AUTOMATIC_REFLECT_REGISTRATIONS( + <#type_path as #bevy_reflect_path::__macro_exports::RegisterForReflection>::__register ) ); }) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 240bc3bd6112a..c0b2c108298c0 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -597,9 +597,6 @@ pub use type_registry::*; pub use bevy_reflect_derive::*; pub use erased_serde; -pub extern crate inventory; -pub extern crate wasm_init; - extern crate alloc; /// Exports used by the reflection macros. @@ -607,6 +604,9 @@ extern crate alloc; /// These are not meant to be used directly and are subject to breaking changes. #[doc(hidden)] pub mod __macro_exports { + pub extern crate inventory; + pub extern crate wasm_init; + use crate::{ DynamicArray, DynamicEnum, DynamicList, DynamicMap, DynamicStruct, DynamicTuple, DynamicTupleStruct, GetTypeRegistration, TypeRegistry, @@ -644,6 +644,16 @@ pub mod __macro_exports { impl RegisterForReflection for DynamicArray {} impl RegisterForReflection for DynamicTuple {} + + /// Stores registration functions of all reflect types that can be automatically registered. + #[cfg(not(target_family = "wasm"))] + #[allow(non_camel_case_types)] + pub struct AUTOMATIC_REFLECT_REGISTRATIONS(pub fn(&mut TypeRegistry)); + #[cfg(not(target_family = "wasm"))] + inventory::collect!(AUTOMATIC_REFLECT_REGISTRATIONS); + #[cfg(target_family = "wasm")] + pub static AUTOMATIC_REFLECT_REGISTRATIONS: std::sync::RwLock> = + std::sync::RwLock::new(Vec::new()); } #[cfg(test)] diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 45b16b3fa09f0..9114fc2a1a14b 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -9,16 +9,6 @@ use std::{ sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}, }; -/// Stores registration functions of all reflect types that can be automatically registered. -#[cfg(not(target_family = "wasm"))] -#[allow(non_camel_case_types)] -pub struct AUTOMATIC_REFLECT_REGISTRATIONS(pub fn(&mut TypeRegistry)); -#[cfg(not(target_family = "wasm"))] -inventory::collect!(AUTOMATIC_REFLECT_REGISTRATIONS); -#[cfg(target_family = "wasm")] -pub static AUTOMATIC_REFLECT_REGISTRATIONS: RwLock> = - RwLock::new(Vec::new()); - /// A registry of [reflected] types. /// /// This struct is used as the central store for type information. @@ -161,16 +151,18 @@ impl TypeRegistry { wasm_init::wasm_init(); #[cfg(target_family = "wasm")] - for registration_fn in AUTOMATIC_REFLECT_REGISTRATIONS + for registration_fn in crate::__macro_exports::AUTOMATIC_REFLECT_REGISTRATIONS .read() - .expect("Failed to get lock to read types for automatic type registration") + .expect("Failed to get read lock for automatic reflect type registration") .iter() { registration_fn(self); } #[cfg(not(target_family = "wasm"))] - for registration_fn in inventory::iter:: { + for registration_fn in + inventory::iter:: + { registration_fn.0(self); } } From 301b60a709082fdee920956b58c7ba38f99e65d0 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Fri, 6 Sep 2024 12:25:24 +0000 Subject: [PATCH 20/32] add note about `no_auto_register` to `Reflect` derive doc --- crates/bevy_reflect/derive/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/bevy_reflect/derive/src/lib.rs b/crates/bevy_reflect/derive/src/lib.rs index 2fdb055b60040..9d7205e454feb 100644 --- a/crates/bevy_reflect/derive/src/lib.rs +++ b/crates/bevy_reflect/derive/src/lib.rs @@ -310,6 +310,12 @@ fn match_reflect_impls(ast: DeriveInput, source: ReflectImplSource) -> TokenStre /// #[reflect(@Required, @EditorTooltip::new("An ID is required!"))] /// struct Id(u8); /// ``` +/// ## `#[reflect(no_auto_register)]` +/// +/// This attribute will opt-out of the automatic reflect type registration. +/// +/// All non-generic types annotated with `#[derive(Reflect)]` are usually automatically registered on app startup. +/// If this behavior is not desired, this attribute may be used to disable it for the annotated type. /// /// # Field Attributes /// From 6ad0a234cc8ca5ec67b2399266835922208b0d73 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sat, 7 Sep 2024 20:47:30 +0000 Subject: [PATCH 21/32] made `inventory` and `wasm-init` platform specific deps --- crates/bevy_reflect/Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/bevy_reflect/Cargo.toml b/crates/bevy_reflect/Cargo.toml index 401ef4071a48a..66f4a6748f9da 100644 --- a/crates/bevy_reflect/Cargo.toml +++ b/crates/bevy_reflect/Cargo.toml @@ -39,7 +39,11 @@ glam = { version = "0.28", features = ["serde"], optional = true } petgraph = { version = "0.6", features = ["serde-1"], optional = true } smol_str = { version = "0.2.0", optional = true } uuid = { version = "1.0", optional = true, features = ["v4", "serde"] } + +# deps for automatic type registration +[target.'cfg(not(target_family = "wasm"))'.dependencies] inventory = "0.3" +[target.'cfg(target_family = "wasm")'.dependencies] wasm-init = "0.2" [dev-dependencies] From 1279d62360b06e2679755057ee4ba36d5ebe3aa8 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sat, 7 Sep 2024 21:00:28 +0000 Subject: [PATCH 22/32] tried to abstract platform-dependent code away --- .../bevy_reflect/derive/src/impls/common.rs | 14 +--- crates/bevy_reflect/src/lib.rs | 76 ++++++++++++++++--- crates/bevy_reflect/src/type_registry.rs | 21 +---- 3 files changed, 71 insertions(+), 40 deletions(-) diff --git a/crates/bevy_reflect/derive/src/impls/common.rs b/crates/bevy_reflect/derive/src/impls/common.rs index 4d87b0eea3152..c67b09aa102eb 100644 --- a/crates/bevy_reflect/derive/src/impls/common.rs +++ b/crates/bevy_reflect/derive/src/impls/common.rs @@ -171,18 +171,10 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option::__register); - } - #[cfg(not(target_family = "wasm"))] - #bevy_reflect_path::__macro_exports::inventory::submit!( - #bevy_reflect_path::__macro_exports::AUTOMATIC_REFLECT_REGISTRATIONS( + #bevy_reflect_path::__macro_exports::auto_register_function!{ + #bevy_reflect_path::__macro_exports::AutomaticReflectRegistrations::add( <#type_path as #bevy_reflect_path::__macro_exports::RegisterForReflection>::__register ) - ); + } }) } diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index c0b2c108298c0..ca6452deb3dad 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -604,9 +604,6 @@ extern crate alloc; /// These are not meant to be used directly and are subject to breaking changes. #[doc(hidden)] pub mod __macro_exports { - pub extern crate inventory; - pub extern crate wasm_init; - use crate::{ DynamicArray, DynamicEnum, DynamicList, DynamicMap, DynamicStruct, DynamicTuple, DynamicTupleStruct, GetTypeRegistration, TypeRegistry, @@ -646,14 +643,75 @@ pub mod __macro_exports { impl RegisterForReflection for DynamicTuple {} /// Stores registration functions of all reflect types that can be automatically registered. + /// + /// Intended to be used as follows: + /// ```rs + /// // Adding a type + /// auto_register_function!{ + /// AutomaticReflectRegistrations::add() + /// } + /// + /// // Registering collected types + /// let mut registry = TypeRegistry::default(); + /// AutomaticReflectRegistrations::register(&mut registry); + /// ``` + pub struct AutomaticReflectRegistrations; + #[cfg(not(target_family = "wasm"))] - #[allow(non_camel_case_types)] - pub struct AUTOMATIC_REFLECT_REGISTRATIONS(pub fn(&mut TypeRegistry)); - #[cfg(not(target_family = "wasm"))] - inventory::collect!(AUTOMATIC_REFLECT_REGISTRATIONS); + mod __automatic_type_registration_impl { + use super::*; + + pub use inventory::submit as auto_register_function; + + pub struct AutomaticReflectRegistrationsImpl(fn(&mut TypeRegistry)); + + impl AutomaticReflectRegistrations { + // Must be const to allow usage in static context + pub const fn add(func: fn(&mut TypeRegistry)) -> AutomaticReflectRegistrationsImpl { + AutomaticReflectRegistrationsImpl(func) + } + pub fn register(registry: &mut TypeRegistry) { + for registration_fn in inventory::iter:: { + registration_fn.0(registry); + } + } + } + + inventory::collect!(AutomaticReflectRegistrationsImpl); + } + #[cfg(target_family = "wasm")] - pub static AUTOMATIC_REFLECT_REGISTRATIONS: std::sync::RwLock> = - std::sync::RwLock::new(Vec::new()); + mod __automatic_type_registration_impl { + use super::*; + pub use wasm_init::wasm_init as auto_register_function; + + static AUTOMATIC_REFLECT_REGISTRATIONS: std::sync::RwLock> = + std::sync::RwLock::new(Vec::new()); + + impl AutomaticReflectRegistrations { + pub fn add(func: fn(&mut TypeRegistry)) { + AUTOMATIC_REFLECT_REGISTRATIONS + .write() + .expect("Failed to get write lock for automatic reflect type registration") + .push(func); + } + pub fn register(registry: &mut TypeRegistry) { + // wasm_init must be called at least once to run all init code. + // Calling it multiple times is ok and doesn't do anything. + wasm_init::wasm_init(); + + for registration_fn in AUTOMATIC_REFLECT_REGISTRATIONS + .read() + .expect("Failed to get read lock for automatic reflect type registration") + .iter() + { + registration_fn(registry); + } + } + } + } + + pub use __automatic_type_registration_impl::*; } #[cfg(test)] diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 9114fc2a1a14b..0eb57b403f733 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -145,26 +145,7 @@ impl TypeRegistry { /// assert!(type_registry.get_type_data::(TypeId::of::()).is_some()); /// ``` pub fn register_derived_types(&mut self) { - // wasm_init must be called at least once to run all init code. - // Calling it multiple times is ok and doesn't do anything. - #[cfg(target_family = "wasm")] - wasm_init::wasm_init(); - - #[cfg(target_family = "wasm")] - for registration_fn in crate::__macro_exports::AUTOMATIC_REFLECT_REGISTRATIONS - .read() - .expect("Failed to get read lock for automatic reflect type registration") - .iter() - { - registration_fn(self); - } - - #[cfg(not(target_family = "wasm"))] - for registration_fn in - inventory::iter:: - { - registration_fn.0(self); - } + crate::__macro_exports::AutomaticReflectRegistrations::register(self); } /// Attempts to register the type `T` if it has not yet been registered already. From 74337f748e38939a7b5f98137997df9b9a39f665 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sat, 7 Sep 2024 21:19:15 +0000 Subject: [PATCH 23/32] update `bevy_reflect`'s module-level doc's "Manual Registration" section --- crates/bevy_reflect/src/lib.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index ca6452deb3dad..3f38715f39840 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -460,12 +460,11 @@ //! For generic methods, the same can be done but will typically require manual monomorphization //! (i.e. manually specifying the types the generic method can take). //! -//! ## Manual Registration +//! ## Manual Registration of Generic Types //! -//! Since Rust doesn't provide built-in support for running initialization code before `main`, -//! there is no way for `bevy_reflect` to automatically register types into the [type registry]. -//! This means types must manually be registered, including their desired monomorphized -//! representations if generic. +//! `bevy_reflect` automatically collects all types that derive [`Reflect`] on startup, +//! and [`TypeRegistry::register_derived_types`] can be used to register these types at any point in the program. +//! However, this does not apply to types with generics: their desired monomorphized representations must be registered manually. //! //! # Features //! From 943dde9660cc842243c769e479d1854ab25e391a Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sun, 1 Dec 2024 15:12:16 +0000 Subject: [PATCH 24/32] added test for ignored auto reflect registration --- crates/bevy_reflect/src/lib.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 14cbb278e6043..ffa865891e3de 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -3045,6 +3045,20 @@ bevy_reflect::tests::Test { ); } + #[test] + fn should_ignore_auto_reflect_registration() { + #[derive(Reflect)] + #[reflect(no_auto_register)] + struct NoAutomaticStruct { + a: usize, + } + + let mut registry = TypeRegistry::default(); + registry.register_derived_types(); + + assert!(!registry.contains(TypeId::of::())) + } + #[cfg(feature = "glam")] mod glam { use super::*; From 36f49f0bc9299a50a2d2353167984238a027d82c Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sun, 1 Dec 2024 15:37:55 +0000 Subject: [PATCH 25/32] clippy --- crates/bevy_reflect/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index ffa865891e3de..f256ac51d9868 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -3056,7 +3056,7 @@ bevy_reflect::tests::Test { let mut registry = TypeRegistry::default(); registry.register_derived_types(); - assert!(!registry.contains(TypeId::of::())) + assert!(!registry.contains(TypeId::of::())); } #[cfg(feature = "glam")] From e50610bf6133dea7e2aee86386764aee6488778f Mon Sep 17 00:00:00 2001 From: eugineerd <70062110+eugineerd@users.noreply.github.com> Date: Sun, 1 Dec 2024 23:16:58 +0300 Subject: [PATCH 26/32] apply suggested doc fix Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> --- crates/bevy_ecs/src/reflect/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/reflect/mod.rs b/crates/bevy_ecs/src/reflect/mod.rs index aad0dd65064f4..5298ca884c10c 100644 --- a/crates/bevy_ecs/src/reflect/mod.rs +++ b/crates/bevy_ecs/src/reflect/mod.rs @@ -50,9 +50,9 @@ impl DerefMut for AppTypeRegistry { } impl AppTypeRegistry { - /// Creates [`AppTypeRegistry`] and calls [`register_derived_types`](TypeRegistry::register_derived_types) on it. + /// Creates [`AppTypeRegistry`] and automatically registers all types deriving [`Reflect`]. /// - /// See [`register_derived_types`](TypeRegistry::register_derived_types) for more details. + /// See [`TypeRegistry::register_derived_types`] for more details. pub fn new_with_derived_types() -> Self { let app_registry = AppTypeRegistry::default(); app_registry.write().register_derived_types(); From b0f243811d2f651cbbbc8d0bd8d9aef46e7ba362 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sun, 1 Dec 2024 20:37:40 +0000 Subject: [PATCH 27/32] implement reflect auto register for opaque types --- crates/bevy_reflect/derive/src/impls/opaque.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/bevy_reflect/derive/src/impls/opaque.rs b/crates/bevy_reflect/derive/src/impls/opaque.rs index eb8fb9f4e01fa..6f54a1db9dc37 100644 --- a/crates/bevy_reflect/derive/src/impls/opaque.rs +++ b/crates/bevy_reflect/derive/src/impls/opaque.rs @@ -1,5 +1,8 @@ use crate::{ - impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed}, + impls::{ + common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed, + reflect_auto_registration, + }, where_clause_options::WhereClauseOptions, ReflectMeta, }; @@ -55,6 +58,8 @@ pub(crate) fn impl_opaque(meta: &ReflectMeta) -> proc_macro2::TokenStream { #[cfg(feature = "functions")] let function_impls = crate::impls::impl_function_traits(meta, &where_clause_options); + let auto_register = reflect_auto_registration(meta); + let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl(); let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); let get_type_registration_impl = meta.get_type_registration(&where_clause_options); @@ -70,6 +75,8 @@ pub(crate) fn impl_opaque(meta: &ReflectMeta) -> proc_macro2::TokenStream { #function_impls + #auto_register + impl #impl_generics #bevy_reflect_path::PartialReflect for #type_path #ty_generics #where_reflect_clause { #[inline] fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> { From 2ae4bb964d21ae91dae9b9c70ebebcfff8b894eb Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sun, 1 Dec 2024 20:43:15 +0000 Subject: [PATCH 28/32] add test for auto reflect registration on all supported types --- crates/bevy_reflect/src/lib.rs | 51 ++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index f256ac51d9868..a1d35cc44458d 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -3059,6 +3059,57 @@ bevy_reflect::tests::Test { assert!(!registry.contains(TypeId::of::())); } + #[test] + fn should_auto_register_reflect_for_all_supported_types() { + // Struct + #[derive(Reflect)] + struct StructReflect { + a: usize, + } + + // ZST struct + #[derive(Reflect)] + struct ZSTStructReflect; + + // Tuple struct + #[derive(Reflect)] + struct TupleStructReflect(pub u32); + + // Enum + #[derive(Reflect)] + enum EnumReflect { + A, + B, + } + + // ZST enum + #[derive(Reflect)] + enum ZSTEnumReflect {} + + // Opaque struct + #[derive(Reflect, Clone)] + #[reflect(opaque)] + struct OpaqueStructReflect { + _a: usize, + } + + // ZST opaque struct + #[derive(Reflect, Clone)] + #[reflect(opaque)] + struct ZSTOpaqueStructReflect; + + let mut registry = TypeRegistry::default(); + registry.register_derived_types(); + + assert!(registry.contains(TypeId::of::())); + assert!(registry.contains(TypeId::of::())); + assert!(registry.contains(TypeId::of::())); + assert!(registry.contains(TypeId::of::())); + assert!(registry.contains(TypeId::of::())); + assert!(registry.contains(TypeId::of::())); + assert!(registry.contains(TypeId::of::())); + } + #[cfg(feature = "glam")] mod glam { use super::*; From 7c7bd79444ac75a602e10a822ff5d69a7e8b01ef Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sun, 1 Dec 2024 22:19:02 +0000 Subject: [PATCH 29/32] put automatic type registration behind feature gate --- Cargo.toml | 4 + crates/bevy_app/Cargo.toml | 5 + crates/bevy_app/src/app.rs | 5 +- crates/bevy_ecs/Cargo.toml | 1 + crates/bevy_ecs/src/reflect/mod.rs | 1 + crates/bevy_internal/Cargo.toml | 6 + crates/bevy_reflect/Cargo.toml | 11 +- crates/bevy_reflect/derive/Cargo.toml | 2 + .../derive/src/container_attributes.rs | 2 + .../bevy_reflect/derive/src/impls/common.rs | 11 +- crates/bevy_reflect/derive/src/impls/enums.rs | 10 +- crates/bevy_reflect/derive/src/impls/mod.rs | 6 +- .../bevy_reflect/derive/src/impls/opaque.rs | 10 +- .../bevy_reflect/derive/src/impls/structs.rs | 10 +- .../derive/src/impls/tuple_structs.rs | 10 +- crates/bevy_reflect/src/lib.rs | 229 +++++++++--------- crates/bevy_reflect/src/type_registry.rs | 3 +- docs/cargo_features.md | 1 + 18 files changed, 187 insertions(+), 140 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dc4fb9be494a4..f86facb15db60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -130,6 +130,7 @@ default = [ "hdr", "multi_threaded", "png", + "reflect_auto_register", "smaa_luts", "sysinfo_plugin", "tonemapping_luts", @@ -454,6 +455,9 @@ track_change_detection = ["bevy_internal/track_change_detection"] # Enable function reflection reflect_functions = ["bevy_internal/reflect_functions"] +# Enable automatic reflect registration +reflect_auto_register = ["bevy_internal/reflect_auto_register"] + # Enable winit custom cursor support custom_cursor = ["bevy_internal/custom_cursor"] diff --git a/crates/bevy_app/Cargo.toml b/crates/bevy_app/Cargo.toml index 9f02f25554d1a..b8d4f5686a3ec 100644 --- a/crates/bevy_app/Cargo.toml +++ b/crates/bevy_app/Cargo.toml @@ -18,6 +18,11 @@ reflect_functions = [ "bevy_reflect/functions", "bevy_ecs/reflect_functions", ] +reflect_auto_register = [ + "bevy_reflect", + "bevy_reflect/auto_register", + "bevy_ecs/reflect_auto_register", +] [dependencies] # bevy diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 9aee4efaf2d8c..576bf2929655f 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -91,7 +91,10 @@ impl Default for App { let mut app = App::empty(); app.sub_apps.main.update_schedule = Some(Main.intern()); - #[cfg(feature = "bevy_reflect")] + #[cfg(all(feature = "bevy_reflect", not(feature = "reflect_auto_register")))] + app.init_resource::(); + + #[cfg(feature = "reflect_auto_register")] app.insert_resource(AppTypeRegistry::new_with_derived_types()); #[cfg(feature = "reflect_functions")] diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index 30117f71277d5..add27d0f10bbe 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -18,6 +18,7 @@ bevy_debug_stepping = [] serialize = ["dep:serde"] track_change_detection = [] reflect_functions = ["bevy_reflect", "bevy_reflect/functions"] +reflect_auto_register = ["bevy_reflect", "bevy_reflect/auto_register"] detailed_trace = [] [dependencies] diff --git a/crates/bevy_ecs/src/reflect/mod.rs b/crates/bevy_ecs/src/reflect/mod.rs index 5298ca884c10c..f55625dd13f79 100644 --- a/crates/bevy_ecs/src/reflect/mod.rs +++ b/crates/bevy_ecs/src/reflect/mod.rs @@ -53,6 +53,7 @@ impl AppTypeRegistry { /// Creates [`AppTypeRegistry`] and automatically registers all types deriving [`Reflect`]. /// /// See [`TypeRegistry::register_derived_types`] for more details. + #[cfg(feature = "reflect_auto_register")] pub fn new_with_derived_types() -> Self { let app_registry = AppTypeRegistry::default(); app_registry.write().register_derived_types(); diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index fc1d9a3d5d9d5..a06c1965f8a63 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -250,6 +250,12 @@ reflect_functions = [ "bevy_app/reflect_functions", "bevy_ecs/reflect_functions", ] +# Enable automatic reflect registration +reflect_auto_register = [ + "bevy_reflect/auto_register", + "bevy_app/reflect_auto_register", + "bevy_ecs/reflect_auto_register", +] # Enable winit custom cursor support custom_cursor = ["bevy_winit/custom_cursor"] diff --git a/crates/bevy_reflect/Cargo.toml b/crates/bevy_reflect/Cargo.toml index e54b1dd4e193b..1b3892a94c43f 100644 --- a/crates/bevy_reflect/Cargo.toml +++ b/crates/bevy_reflect/Cargo.toml @@ -26,6 +26,12 @@ debug_stack = [] documentation = ["bevy_reflect_derive/documentation"] # Enables function reflection functions = ["bevy_reflect_derive/functions"] +# Enables automatic reflect registration +auto_register = [ + "bevy_reflect_derive/auto_register", + "dep:inventory", + "dep:wasm-init", +] alloc = [] [dependencies] @@ -55,9 +61,10 @@ wgpu-types = { version = "23", features = ["serde"], optional = true } # deps for automatic type registration [target.'cfg(not(target_family = "wasm"))'.dependencies] -inventory = "0.3" +inventory = { version = "0.3", optional = true } [target.'cfg(target_family = "wasm")'.dependencies] -wasm-init = "0.2" +wasm-init = { version = "0.2", optional = true } + [dev-dependencies] ron = "0.8.0" diff --git a/crates/bevy_reflect/derive/Cargo.toml b/crates/bevy_reflect/derive/Cargo.toml index 468e89e8fbff5..ad9e388911c4f 100644 --- a/crates/bevy_reflect/derive/Cargo.toml +++ b/crates/bevy_reflect/derive/Cargo.toml @@ -17,6 +17,8 @@ default = [] documentation = [] # Enables macro logic related to function reflection functions = [] +# Enables automatic reflect registration +auto_register = [] [dependencies] bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.15.0-dev" } diff --git a/crates/bevy_reflect/derive/src/container_attributes.rs b/crates/bevy_reflect/derive/src/container_attributes.rs index 471eee577c7e8..7ece726909d49 100644 --- a/crates/bevy_reflect/derive/src/container_attributes.rs +++ b/crates/bevy_reflect/derive/src/container_attributes.rs @@ -558,6 +558,8 @@ impl ContainerAttributes { } /// Returns true if the `no_auto_register` attribute was found on this type. + // This is not feature-gated because derive macro shouldn't break if auto_register feature is disabled. + #[allow(dead_code)] pub fn no_auto_register(&self) -> bool { self.no_auto_register } diff --git a/crates/bevy_reflect/derive/src/impls/common.rs b/crates/bevy_reflect/derive/src/impls/common.rs index 03617acaf225f..7fa8f0a95a87e 100644 --- a/crates/bevy_reflect/derive/src/impls/common.rs +++ b/crates/bevy_reflect/derive/src/impls/common.rs @@ -1,6 +1,6 @@ use bevy_macro_utils::fq_std::{FQAny, FQBox, FQOption, FQResult}; -use quote::{quote, ToTokens}; +use quote::quote; use crate::{derive_data::ReflectMeta, where_clause_options::WhereClauseOptions}; @@ -157,7 +157,10 @@ pub fn common_partial_reflect_methods( } } +#[cfg(feature = "auto_register")] pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option { + use quote::ToTokens; + if meta.attrs().no_auto_register() { return None; } @@ -171,9 +174,9 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option::__register + #bevy_reflect_path::__macro_exports::auto_register::auto_register_function!{ + #bevy_reflect_path::__macro_exports::auto_register::AutomaticReflectRegistrations::add( + <#type_path as #bevy_reflect_path::__macro_exports::auto_register::RegisterForReflection>::__register ) } }) diff --git a/crates/bevy_reflect/derive/src/impls/enums.rs b/crates/bevy_reflect/derive/src/impls/enums.rs index 90e5c6f2de1a7..19a0fc1b8e011 100644 --- a/crates/bevy_reflect/derive/src/impls/enums.rs +++ b/crates/bevy_reflect/derive/src/impls/enums.rs @@ -1,10 +1,7 @@ use crate::{ derive_data::{EnumVariantFields, ReflectEnum, StructField}, enum_utility::{EnumVariantOutputData, TryApplyVariantBuilder, VariantBuilder}, - impls::{ - common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed, - reflect_auto_registration, - }, + impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed}, }; use bevy_macro_utils::fq_std::{FQBox, FQOption, FQResult}; use proc_macro2::{Ident, Span}; @@ -85,7 +82,10 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream let (impl_generics, ty_generics, where_clause) = reflect_enum.meta().type_path().generics().split_for_impl(); - let auto_register = reflect_auto_registration(reflect_enum.meta()); + #[cfg(not(feature = "auto_register"))] + let auto_register = None::; + #[cfg(feature = "auto_register")] + let auto_register = crate::impls::reflect_auto_registration(reflect_enum.meta()); let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); diff --git a/crates/bevy_reflect/derive/src/impls/mod.rs b/crates/bevy_reflect/derive/src/impls/mod.rs index 15a09a176a3f5..48c8c84621d46 100644 --- a/crates/bevy_reflect/derive/src/impls/mod.rs +++ b/crates/bevy_reflect/derive/src/impls/mod.rs @@ -9,9 +9,9 @@ mod tuple_structs; mod typed; pub(crate) use assertions::impl_assertions; -pub(crate) use common::{ - common_partial_reflect_methods, impl_full_reflect, reflect_auto_registration, -}; +#[cfg(feature = "auto_register")] +pub(crate) use common::reflect_auto_registration; +pub(crate) use common::{common_partial_reflect_methods, impl_full_reflect}; pub(crate) use enums::impl_enum; #[cfg(feature = "functions")] pub(crate) use func::impl_function_traits; diff --git a/crates/bevy_reflect/derive/src/impls/opaque.rs b/crates/bevy_reflect/derive/src/impls/opaque.rs index 6f54a1db9dc37..757151fb451dc 100644 --- a/crates/bevy_reflect/derive/src/impls/opaque.rs +++ b/crates/bevy_reflect/derive/src/impls/opaque.rs @@ -1,8 +1,5 @@ use crate::{ - impls::{ - common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed, - reflect_auto_registration, - }, + impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed}, where_clause_options::WhereClauseOptions, ReflectMeta, }; @@ -58,7 +55,10 @@ pub(crate) fn impl_opaque(meta: &ReflectMeta) -> proc_macro2::TokenStream { #[cfg(feature = "functions")] let function_impls = crate::impls::impl_function_traits(meta, &where_clause_options); - let auto_register = reflect_auto_registration(meta); + #[cfg(not(feature = "auto_register"))] + let auto_register = None::; + #[cfg(feature = "auto_register")] + let auto_register = crate::impls::reflect_auto_registration(meta); let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl(); let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); diff --git a/crates/bevy_reflect/derive/src/impls/structs.rs b/crates/bevy_reflect/derive/src/impls/structs.rs index b946e425f2d2d..c1ee1abcdaec4 100644 --- a/crates/bevy_reflect/derive/src/impls/structs.rs +++ b/crates/bevy_reflect/derive/src/impls/structs.rs @@ -1,8 +1,5 @@ use crate::{ - impls::{ - common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed, - reflect_auto_registration, - }, + impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed}, struct_utility::FieldAccessors, ReflectStruct, }; @@ -65,7 +62,10 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS .generics() .split_for_impl(); - let auto_register = reflect_auto_registration(reflect_struct.meta()); + #[cfg(not(feature = "auto_register"))] + let auto_register = None::; + #[cfg(feature = "auto_register")] + let auto_register = crate::impls::reflect_auto_registration(reflect_struct.meta()); let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); diff --git a/crates/bevy_reflect/derive/src/impls/tuple_structs.rs b/crates/bevy_reflect/derive/src/impls/tuple_structs.rs index 68cca30c2453d..635718997cb81 100644 --- a/crates/bevy_reflect/derive/src/impls/tuple_structs.rs +++ b/crates/bevy_reflect/derive/src/impls/tuple_structs.rs @@ -1,8 +1,5 @@ use crate::{ - impls::{ - common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed, - reflect_auto_registration, - }, + impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed}, struct_utility::FieldAccessors, ReflectStruct, }; @@ -53,7 +50,10 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2:: .generics() .split_for_impl(); - let auto_register = reflect_auto_registration(reflect_struct.meta()); + #[cfg(not(feature = "auto_register"))] + let auto_register = None::; + #[cfg(feature = "auto_register")] + let auto_register = crate::impls::reflect_auto_registration(reflect_struct.meta()); let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index a1d35cc44458d..adae3686b0b26 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -687,76 +687,82 @@ pub mod __macro_exports { impl RegisterForReflection for DynamicTuple {} - /// Stores registration functions of all reflect types that can be automatically registered. - /// - /// Intended to be used as follows: - /// ```rs - /// // Adding a type - /// auto_register_function!{ - /// AutomaticReflectRegistrations::add() - /// } - /// - /// // Registering collected types - /// let mut registry = TypeRegistry::default(); - /// AutomaticReflectRegistrations::register(&mut registry); - /// ``` - pub struct AutomaticReflectRegistrations; - - #[cfg(not(target_family = "wasm"))] - mod __automatic_type_registration_impl { - use super::*; + /// Automatic reflect registration implementation + #[cfg(feature = "auto_register")] + pub mod auto_register { + pub use super::*; + + /// Stores registration functions of all reflect types that can be automatically registered. + /// + /// Intended to be used as follows: + /// ```rs + /// // Adding a type + /// auto_register_function!{ + /// AutomaticReflectRegistrations::add() + /// } + /// + /// // Registering collected types + /// let mut registry = TypeRegistry::default(); + /// AutomaticReflectRegistrations::register(&mut registry); + /// ``` + pub struct AutomaticReflectRegistrations; + + #[cfg(not(target_family = "wasm"))] + mod __automatic_type_registration_impl { + use super::*; - pub use inventory::submit as auto_register_function; + pub use inventory::submit as auto_register_function; - pub struct AutomaticReflectRegistrationsImpl(fn(&mut TypeRegistry)); + pub struct AutomaticReflectRegistrationsImpl(fn(&mut TypeRegistry)); - impl AutomaticReflectRegistrations { - // Must be const to allow usage in static context - pub const fn add(func: fn(&mut TypeRegistry)) -> AutomaticReflectRegistrationsImpl { - AutomaticReflectRegistrationsImpl(func) - } - pub fn register(registry: &mut TypeRegistry) { - for registration_fn in inventory::iter:: { - registration_fn.0(registry); + impl AutomaticReflectRegistrations { + // Must be const to allow usage in static context + pub const fn add(func: fn(&mut TypeRegistry)) -> AutomaticReflectRegistrationsImpl { + AutomaticReflectRegistrationsImpl(func) + } + pub fn register(registry: &mut TypeRegistry) { + for registration_fn in inventory::iter:: { + registration_fn.0(registry); + } } } - } - inventory::collect!(AutomaticReflectRegistrationsImpl); - } + inventory::collect!(AutomaticReflectRegistrationsImpl); + } - #[cfg(target_family = "wasm")] - mod __automatic_type_registration_impl { - use super::*; - pub use wasm_init::wasm_init as auto_register_function; + #[cfg(target_family = "wasm")] + mod __automatic_type_registration_impl { + use super::*; + pub use wasm_init::wasm_init as auto_register_function; - static AUTOMATIC_REFLECT_REGISTRATIONS: std::sync::RwLock> = - std::sync::RwLock::new(Vec::new()); + static AUTOMATIC_REFLECT_REGISTRATIONS: std::sync::RwLock> = + std::sync::RwLock::new(Vec::new()); - impl AutomaticReflectRegistrations { - pub fn add(func: fn(&mut TypeRegistry)) { - AUTOMATIC_REFLECT_REGISTRATIONS - .write() - .expect("Failed to get write lock for automatic reflect type registration") - .push(func); - } - pub fn register(registry: &mut TypeRegistry) { - // wasm_init must be called at least once to run all init code. - // Calling it multiple times is ok and doesn't do anything. - wasm_init::wasm_init(); - - for registration_fn in AUTOMATIC_REFLECT_REGISTRATIONS - .read() - .expect("Failed to get read lock for automatic reflect type registration") - .iter() - { - registration_fn(registry); + impl AutomaticReflectRegistrations { + pub fn add(func: fn(&mut TypeRegistry)) { + AUTOMATIC_REFLECT_REGISTRATIONS + .write() + .expect("Failed to get write lock for automatic reflect type registration") + .push(func); + } + pub fn register(registry: &mut TypeRegistry) { + // wasm_init must be called at least once to run all init code. + // Calling it multiple times is ok and doesn't do anything. + wasm_init::wasm_init(); + + for registration_fn in AUTOMATIC_REFLECT_REGISTRATIONS + .read() + .expect("Failed to get read lock for automatic reflect type registration") + .iter() + { + registration_fn(registry); + } } } } - } - pub use __automatic_type_registration_impl::*; + pub use __automatic_type_registration_impl::*; + } } #[cfg(test)] @@ -3045,69 +3051,74 @@ bevy_reflect::tests::Test { ); } - #[test] - fn should_ignore_auto_reflect_registration() { - #[derive(Reflect)] - #[reflect(no_auto_register)] - struct NoAutomaticStruct { - a: usize, - } + #[cfg(feature = "auto_register")] + mod auto_register_reflect { + use super::*; - let mut registry = TypeRegistry::default(); - registry.register_derived_types(); + #[test] + fn should_ignore_auto_reflect_registration() { + #[derive(Reflect)] + #[reflect(no_auto_register)] + struct NoAutomaticStruct { + a: usize, + } - assert!(!registry.contains(TypeId::of::())); - } + let mut registry = TypeRegistry::default(); + registry.register_derived_types(); - #[test] - fn should_auto_register_reflect_for_all_supported_types() { - // Struct - #[derive(Reflect)] - struct StructReflect { - a: usize, + assert!(!registry.contains(TypeId::of::())); } - // ZST struct - #[derive(Reflect)] - struct ZSTStructReflect; + #[test] + fn should_auto_register_reflect_for_all_supported_types() { + // Struct + #[derive(Reflect)] + struct StructReflect { + a: usize, + } - // Tuple struct - #[derive(Reflect)] - struct TupleStructReflect(pub u32); + // ZST struct + #[derive(Reflect)] + struct ZSTStructReflect; - // Enum - #[derive(Reflect)] - enum EnumReflect { - A, - B, - } + // Tuple struct + #[derive(Reflect)] + struct TupleStructReflect(pub u32); - // ZST enum - #[derive(Reflect)] - enum ZSTEnumReflect {} + // Enum + #[derive(Reflect)] + enum EnumReflect { + A, + B, + } - // Opaque struct - #[derive(Reflect, Clone)] - #[reflect(opaque)] - struct OpaqueStructReflect { - _a: usize, - } + // ZST enum + #[derive(Reflect)] + enum ZSTEnumReflect {} - // ZST opaque struct - #[derive(Reflect, Clone)] - #[reflect(opaque)] - struct ZSTOpaqueStructReflect; + // Opaque struct + #[derive(Reflect, Clone)] + #[reflect(opaque)] + struct OpaqueStructReflect { + _a: usize, + } - let mut registry = TypeRegistry::default(); - registry.register_derived_types(); - - assert!(registry.contains(TypeId::of::())); - assert!(registry.contains(TypeId::of::())); - assert!(registry.contains(TypeId::of::())); - assert!(registry.contains(TypeId::of::())); - assert!(registry.contains(TypeId::of::())); - assert!(registry.contains(TypeId::of::())); - assert!(registry.contains(TypeId::of::())); + // ZST opaque struct + #[derive(Reflect, Clone)] + #[reflect(opaque)] + struct ZSTOpaqueStructReflect; + + let mut registry = TypeRegistry::default(); + registry.register_derived_types(); + + assert!(registry.contains(TypeId::of::())); + assert!(registry.contains(TypeId::of::())); + assert!(registry.contains(TypeId::of::())); + assert!(registry.contains(TypeId::of::())); + assert!(registry.contains(TypeId::of::())); + assert!(registry.contains(TypeId::of::())); + assert!(registry.contains(TypeId::of::())); + } } #[cfg(feature = "glam")] diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index a63e8ea8d3a74..a7b2233b4feed 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -150,8 +150,9 @@ impl TypeRegistry { /// // Its type data /// assert!(type_registry.get_type_data::(TypeId::of::()).is_some()); /// ``` + #[cfg(feature = "auto_register")] pub fn register_derived_types(&mut self) { - crate::__macro_exports::AutomaticReflectRegistrations::register(self); + crate::__macro_exports::auto_register::AutomaticReflectRegistrations::register(self); } /// Attempts to register the type `T` if it has not yet been registered already. diff --git a/docs/cargo_features.md b/docs/cargo_features.md index 1cc83b9e1102f..acb13a8e05722 100644 --- a/docs/cargo_features.md +++ b/docs/cargo_features.md @@ -41,6 +41,7 @@ The default feature set enables most of the expected features of a game engine, |ktx2|KTX2 compressed texture support| |multi_threaded|Enables multithreaded parallelism in the engine. Disabling it forces all engine tasks to run on a single thread.| |png|PNG image format support| +|reflect_auto_register|Enable automatic reflect registration| |smaa_luts|Include SMAA Look Up Tables KTX2 Files| |sysinfo_plugin|Enables system information diagnostic plugin| |tonemapping_luts|Include tonemapping Look Up Tables KTX2 files. If everything is pink, you need to enable this feature or change the `Tonemapping` method for your `Camera2d` or `Camera3d`.| From 03ce54345622b309e44e6b0f831d38656dadcd39 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sun, 1 Dec 2024 22:57:20 +0000 Subject: [PATCH 30/32] add reason to `no_auto_register` allow --- crates/bevy_reflect/derive/src/container_attributes.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/bevy_reflect/derive/src/container_attributes.rs b/crates/bevy_reflect/derive/src/container_attributes.rs index 7ece726909d49..e2ea0fa38094e 100644 --- a/crates/bevy_reflect/derive/src/container_attributes.rs +++ b/crates/bevy_reflect/derive/src/container_attributes.rs @@ -559,7 +559,10 @@ impl ContainerAttributes { /// Returns true if the `no_auto_register` attribute was found on this type. // This is not feature-gated because derive macro shouldn't break if auto_register feature is disabled. - #[allow(dead_code)] + #[allow( + dead_code, + reason = "This is flagged as dead code if auto_register feature is not enabled." + )] pub fn no_auto_register(&self) -> bool { self.no_auto_register } From 67f4d021a3d36789d590bf5de597470ce44b70a3 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sun, 1 Dec 2024 22:58:02 +0000 Subject: [PATCH 31/32] use `impl_is_generic` instead of converting to token stream. Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> --- crates/bevy_reflect/derive/src/impls/common.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/bevy_reflect/derive/src/impls/common.rs b/crates/bevy_reflect/derive/src/impls/common.rs index 7fa8f0a95a87e..b136cb5b98d2c 100644 --- a/crates/bevy_reflect/derive/src/impls/common.rs +++ b/crates/bevy_reflect/derive/src/impls/common.rs @@ -159,17 +159,14 @@ pub fn common_partial_reflect_methods( #[cfg(feature = "auto_register")] pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option { - use quote::ToTokens; - if meta.attrs().no_auto_register() { return None; } let bevy_reflect_path = meta.bevy_reflect_path(); let type_path = meta.type_path(); - let generics = meta.type_path().generics(); - if !generics.into_token_stream().is_empty() { + if type_path.impl_is_generic() { return None; }; From 69bbc17cc663bacfb77d897942a4cde813f3746c Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sun, 8 Dec 2024 22:12:18 +0000 Subject: [PATCH 32/32] fix missing `[package]` in `bevy_reflect/Cargo.toml` (How did that even happen?) --- crates/bevy_reflect/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bevy_reflect/Cargo.toml b/crates/bevy_reflect/Cargo.toml index c5f574f6f6323..3b74baeccd234 100644 --- a/crates/bevy_reflect/Cargo.toml +++ b/crates/bevy_reflect/Cargo.toml @@ -1,3 +1,4 @@ +[package] name = "bevy_reflect" version = "0.15.0-dev" edition = "2021"