From 4b48e235bc97c5239ee8c223cf2a195ceca92d83 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Mon, 2 Sep 2024 21:56:25 +0000 Subject: [PATCH 01/61] 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 da41efbc15..b502e32f64 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 5cfbf83300..401ef4071a 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 15b11d4dd9..8095cda8c3 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 29829130fe..240bc3bd61 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 1263b0bbed..0f9f087257 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 283c1c6d18..4cb6003116 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/61] 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 | 38 ++++++++++++------- 8 files changed, 84 insertions(+), 39 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index b502e32f64..2b7a088dff 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 f01fd96e04..25ba89fcd3 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 15d5c01ffa..595372cce4 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 0905d43bd2..5c0eacdc31 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 8095cda8c3..9c8baebe27 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 fc2228e70e..9e91d056aa 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 3a804f3bed..820fee6ad3 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 0f9f087257..0a2b4b813b 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,17 +116,26 @@ 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.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_family = "wasm"))] + for ty in inventory::iter:: { + ty.0(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 f3614b8ed3eb46f04a5593cd1d2dfbd8d3efa34f Mon Sep 17 00:00:00 2001 From: eugineerd Date: Tue, 3 Sep 2024 11:58:50 +0000 Subject: [PATCH 03/61] 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 b5f0f906b3..d713fc3d8d 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 25ba89fcd3..245a284d4f 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/61] 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 3708e1d45b..99cb3b5ca0 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 2cd6e22162..7281a759e3 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 401ef4071a..20ef6c29ba 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 0df5d9af23..259d9c1836 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 595372cce4..07adda9a16 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 9c8baebe27..3a72c89711 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 9e91d056aa..90751b00ff 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 820fee6ad3..9e190182e3 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 240bc3bd61..5788047e10 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 0a2b4b813b..0745727f64 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/61] 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 245a284d4f..9e0d4bc207 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 0745727f64..05c4309fbf 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/61] 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 c360c2eb8c..0a0a788b2f 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/61] 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 9e0d4bc207..666285133a 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 05c4309fbf..ce8933b65e 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/61] 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 99cb3b5ca0..3708e1d45b 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 2b7a088dff..da41efbc15 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 7281a759e3..2cd6e22162 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 20ef6c29ba..30655cee3b 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 259d9c1836..0df5d9af23 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 07adda9a16..bc09ecd02d 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 3a72c89711..9c8baebe27 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 90751b00ff..04c2cfb306 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 9e190182e3..820fee6ad3 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 5788047e10..240bc3bd61 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 ce8933b65e..21a1e68d3d 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 0a0a788b2f..c360c2eb8c 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/61] 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 21a1e68d3d..625e8816fd 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/61] 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 18227cb598..98fdc21cee 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 4cb6003116..e2a4502043 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 f1f9022b23..7f34e31236 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 e5f18d1bdd..b6ad596f5a 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/61] 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 625e8816fd..cc75b505cd 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/61] 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 30655cee3b..401ef4071a 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/61] 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 bc09ecd02d..ae695fd5f4 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 9c8baebe27..444a3f8e6d 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 04c2cfb306..b6695b06cb 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/61] 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 cc75b505cd..e57e720d2e 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/61] 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 ae02af8913..77547baf56 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 e57e720d2e..c3b2ee1d79 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/61] 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 c3b2ee1d79..45b16b3fa0 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/61] 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 da41efbc15..86202aa33b 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 77547baf56..6e0088aee4 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/61] 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 98fdc21cee..18227cb598 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 7f34e31236..f1f9022b23 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/61] 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 666285133a..4d87b0eea3 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 240bc3bd61..c0b2c10829 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 45b16b3fa0..9114fc2a1a 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/61] 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 2fdb055b60..9d7205e454 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/61] 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 401ef4071a..66f4a6748f 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/61] 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 4d87b0eea3..c67b09aa10 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 c0b2c10829..ca6452deb3 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 9114fc2a1a..0eb57b403f 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/61] 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 ca6452deb3..3f38715f39 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/61] 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 14cbb278e6..ffa865891e 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/61] 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 ffa865891e..f256ac51d9 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/61] 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 aad0dd6506..5298ca884c 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/61] 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 eb8fb9f4e0..6f54a1db9d 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/61] 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 f256ac51d9..a1d35cc444 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/61] 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 | 247 +++++++++--------- crates/bevy_reflect/src/type_registry.rs | 3 +- docs/cargo_features.md | 1 + 18 files changed, 196 insertions(+), 149 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dc4fb9be49..f86facb15d 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 9f02f25554..b8d4f5686a 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 9aee4efaf2..576bf29296 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 30117f7127..add27d0f10 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 5298ca884c..f55625dd13 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 fc1d9a3d5d..a06c1965f8 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 e54b1dd4e1..1b3892a94c 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 468e89e8fb..ad9e388911 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 471eee577c..7ece726909 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 03617acaf2..7fa8f0a95a 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 90e5c6f2de..19a0fc1b8e 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 15a09a176a..48c8c84621 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 6f54a1db9d..757151fb45 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 b946e425f2..c1ee1abcda 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 68cca30c24..635718997c 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 a1d35cc444..adae3686b0 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; + /// Automatic reflect registration implementation + #[cfg(feature = "auto_register")] + pub mod auto_register { + pub use super::*; - #[cfg(not(target_family = "wasm"))] - mod __automatic_type_registration_impl { - 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; - pub use inventory::submit as auto_register_function; + #[cfg(not(target_family = "wasm"))] + mod __automatic_type_registration_impl { + use super::*; - pub struct AutomaticReflectRegistrationsImpl(fn(&mut TypeRegistry)); + pub use inventory::submit as auto_register_function; - impl AutomaticReflectRegistrations { - // Must be const to allow usage in static context - pub const fn add(func: fn(&mut TypeRegistry)) -> AutomaticReflectRegistrationsImpl { - AutomaticReflectRegistrationsImpl(func) + 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); + } + } } - pub fn register(registry: &mut TypeRegistry) { - for registration_fn in inventory::iter:: { - registration_fn.0(registry); + + inventory::collect!(AutomaticReflectRegistrationsImpl); + } + + #[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()); + + 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); + } } } } - inventory::collect!(AutomaticReflectRegistrationsImpl); + pub use __automatic_type_registration_impl::*; } - - #[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()); - - 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)] @@ -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::*; + + #[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::())); } - 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); + + // 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::())); } - - // 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")] diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index a63e8ea8d3..a7b2233b4f 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 1cc83b9e11..acb13a8e05 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/61] 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 7ece726909..e2ea0fa380 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/61] 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 7fa8f0a95a..b136cb5b98 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/61] 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 c5f574f6f6..3b74baeccd 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" From 58b847c3a2ad71916dcc281a1bd6b8ccd4e62f36 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sat, 4 Jan 2025 13:37:34 +0000 Subject: [PATCH 33/61] add `no_std` support on wasm --- crates/bevy_reflect/src/lib.rs | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index cc07186523..21a4aa14ec 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -759,28 +759,42 @@ pub mod __macro_exports { #[cfg(target_family = "wasm")] mod __automatic_type_registration_impl { use super::*; + use alloc::vec::Vec; + + #[cfg(feature = "std")] + use std::sync::RwLock; + + #[cfg(not(feature = "std"))] + use spin::rwlock::RwLock; + 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: RwLock> = + RwLock::new(Vec::new()); impl AutomaticReflectRegistrations { pub fn add(func: fn(&mut TypeRegistry)) { - AUTOMATIC_REFLECT_REGISTRATIONS + #[cfg(feature = "std")] + let mut registrations = AUTOMATIC_REFLECT_REGISTRATIONS .write() - .expect("Failed to get write lock for automatic reflect type registration") - .push(func); + .expect("Failed to get write lock for automatic reflect type registration"); + #[cfg(not(feature = "std"))] + let mut registrations = AUTOMATIC_REFLECT_REGISTRATIONS.write(); + registrations.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 + #[cfg(feature = "std")] + let registrations = AUTOMATIC_REFLECT_REGISTRATIONS .read() - .expect("Failed to get read lock for automatic reflect type registration") - .iter() - { + .expect("Failed to get read lock for automatic reflect type registration"); + #[cfg(not(feature = "std"))] + let registrations = AUTOMATIC_REFLECT_REGISTRATIONS.read(); + + for registration_fn in registrations.iter() { registration_fn(registry); } } From 95593d16dd168eea50c68237b002a274322695d3 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sun, 5 Jan 2025 07:54:08 +0000 Subject: [PATCH 34/61] feature-gate `no_auto_register` attribute getter --- crates/bevy_reflect/derive/src/container_attributes.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/bevy_reflect/derive/src/container_attributes.rs b/crates/bevy_reflect/derive/src/container_attributes.rs index 9646e13347..72583856fd 100644 --- a/crates/bevy_reflect/derive/src/container_attributes.rs +++ b/crates/bevy_reflect/derive/src/container_attributes.rs @@ -552,11 +552,7 @@ 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, - reason = "This is flagged as dead code if auto_register feature is not enabled." - )] + #[cfg(feature = "auto_register")] pub fn no_auto_register(&self) -> bool { self.no_auto_register } From 1bb9ee85de96ed5b806c0e1ed3e6a7b8f67d540a Mon Sep 17 00:00:00 2001 From: eugineerd Date: Fri, 21 Feb 2025 19:25:58 +0000 Subject: [PATCH 35/61] remove `wasm-init` and just use `inventory` instead --- crates/bevy_reflect/Cargo.toml | 15 +-- .../bevy_reflect/derive/src/impls/common.rs | 6 +- crates/bevy_reflect/src/lib.rs | 110 ++++++------------ crates/bevy_reflect/src/type_registry.rs | 7 +- 4 files changed, 47 insertions(+), 91 deletions(-) diff --git a/crates/bevy_reflect/Cargo.toml b/crates/bevy_reflect/Cargo.toml index da2ca38c32..aabc38d997 100644 --- a/crates/bevy_reflect/Cargo.toml +++ b/crates/bevy_reflect/Cargo.toml @@ -78,11 +78,7 @@ portable-atomic = [ "bevy_utils/portable-atomic", ] # Enables automatic reflect registration -auto_register = [ - "bevy_reflect_derive/auto_register", - "dep:inventory", - "dep:wasm-init", -] +auto_register = ["bevy_reflect_derive/auto_register", "dep:inventory"] [dependencies] # bevy @@ -124,15 +120,12 @@ uuid = { version = "1.13.1", default-features = false, optional = true, features variadics_please = "1.1" wgpu-types = { version = "24", features = ["serde"], optional = true } +# deps for automatic type registration +inventory = { version = "0.3", optional = true } + [target.'cfg(target_arch = "wasm32")'.dependencies] uuid = { version = "1.13.1", default-features = false, features = ["js"] } -# deps for automatic type registration -[target.'cfg(not(target_family = "wasm"))'.dependencies] -inventory = { version = "0.3", optional = true } -[target.'cfg(target_family = "wasm")'.dependencies] -wasm-init = { version = "0.2", optional = true } - [dev-dependencies] ron = "0.8.0" diff --git a/crates/bevy_reflect/derive/src/impls/common.rs b/crates/bevy_reflect/derive/src/impls/common.rs index 4c942d4706..e3fc1e726f 100644 --- a/crates/bevy_reflect/derive/src/impls/common.rs +++ b/crates/bevy_reflect/derive/src/impls/common.rs @@ -171,9 +171,9 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option::__register + #bevy_reflect_path::__macro_exports::auto_register::inventory::submit!{ + #bevy_reflect_path::__macro_exports::auto_register::AutomaticReflectRegistrations( + <#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 4cecf8315b..1b73fe52eb 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -718,92 +718,50 @@ pub mod __macro_exports { /// Automatic reflect registration implementation #[cfg(feature = "auto_register")] pub mod auto_register { - pub use super::*; + 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 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); - } + pub use inventory; #[cfg(target_family = "wasm")] - mod __automatic_type_registration_impl { - use super::*; - use alloc::vec::Vec; + mod wasm_support { + use bevy_platform_support::sync::atomic::{AtomicBool, Ordering}; - #[cfg(feature = "std")] - use std::sync::RwLock; + static INIT_DONE: AtomicBool = AtomicBool::new(false); - #[cfg(not(feature = "std"))] - use spin::rwlock::RwLock; + #[expect(unsafe_code, reason = "This function is generated by linker.")] + unsafe extern "C" { + fn __wasm_call_ctors(); + } - pub use wasm_init::wasm_init as auto_register_function; - - static AUTOMATIC_REFLECT_REGISTRATIONS: RwLock> = - RwLock::new(Vec::new()); - - impl AutomaticReflectRegistrations { - pub fn add(func: fn(&mut TypeRegistry)) { - #[cfg(feature = "std")] - let mut registrations = AUTOMATIC_REFLECT_REGISTRATIONS - .write() - .expect("Failed to get write lock for automatic reflect type registration"); - #[cfg(not(feature = "std"))] - let mut registrations = AUTOMATIC_REFLECT_REGISTRATIONS.write(); - registrations.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(); - - #[cfg(feature = "std")] - let registrations = AUTOMATIC_REFLECT_REGISTRATIONS - .read() - .expect("Failed to get read lock for automatic reflect type registration"); - #[cfg(not(feature = "std"))] - let registrations = AUTOMATIC_REFLECT_REGISTRATIONS.read(); - - for registration_fn in registrations.iter() { - registration_fn(registry); - } + /// This function must be called before using [`inventory::iter`] on [`AutomaticReflectRegistrations`] to run constructors on all platforms. + pub fn init() { + if INIT_DONE.swap(true, Ordering::Relaxed) { + return; + }; + // SAFETY: + // This will call constructors on wasm platforms at most once (as long as `init` is the only function that calls `__wasm_call_ctors`). + // + // For more information see: https://docs.rs/inventory/latest/inventory/#webassembly-and-constructors + #[expect( + unsafe_code, + reason = "This function must be called to use inventory on wasm." + )] + unsafe { + __wasm_call_ctors(); } } } - pub use __automatic_type_registration_impl::*; + /// This function must be called before using [`inventory::iter`] on [`AutomaticReflectRegistrations`] to run constructors on all platforms. + pub fn init() { + #[cfg(target_family = "wasm")] + wasm_support::init(); + } + + /// Stores registration function to be used during automatic reflect registration. + pub struct AutomaticReflectRegistrations(pub fn(&mut TypeRegistry)); + + inventory::collect!(AutomaticReflectRegistrations); } } diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 8a2247f53c..6d3bf91bc1 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -154,7 +154,12 @@ impl TypeRegistry { /// ``` #[cfg(feature = "auto_register")] pub fn register_derived_types(&mut self) { - crate::__macro_exports::auto_register::AutomaticReflectRegistrations::register(self); + crate::__macro_exports::auto_register::init(); + for registration in inventory::iter::< + crate::__macro_exports::auto_register::AutomaticReflectRegistrations, + >() { + registration.0(self); + } } /// Attempts to register the type `T` if it has not yet been registered already. From 2eb5d1bdfc996dc1c09831e537da82946b2c5e7f Mon Sep 17 00:00:00 2001 From: eugineerd Date: Wed, 12 Mar 2025 11:21:41 +0000 Subject: [PATCH 36/61] remove empty enum from tests --- crates/bevy_reflect/src/lib.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 054088aa77..93b5dfd404 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -3405,10 +3405,6 @@ bevy_reflect::tests::Test { B, } - // ZST enum - #[derive(Reflect)] - enum ZSTEnumReflect {} - // Opaque struct #[derive(Reflect, Clone)] #[reflect(opaque)] @@ -3428,7 +3424,6 @@ bevy_reflect::tests::Test { 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::())); } From 1448ee66b048a411898723309ed96364848de2b4 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Wed, 12 Mar 2025 20:28:18 +0000 Subject: [PATCH 37/61] Disable auto registration by default --- Cargo.toml | 1 - crates/bevy_reflect/src/lib.rs | 23 +++++++++++++++++++---- examples/reflection/reflection.rs | 4 +++- examples/scene/scene.rs | 3 +++ 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4e2bb459ea..53c2a23f23 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -154,7 +154,6 @@ default = [ "hdr", "multi_threaded", "png", - "reflect_auto_register", "smaa_luts", "sysinfo_plugin", "tonemapping_luts", diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 93b5dfd404..200b63b474 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -464,11 +464,13 @@ //! typically require manual monomorphization (i.e. manually specifying the types the generic method can //! take). //! -//! ## Manual Registration of Generic Types +//! ## Manual Registration //! -//! `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. +//! 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] by default +//! (see `auto_register` feature if this functionality is required). +//! This means types must manually be registered, including their desired monomorphized +//! representations if generic. //! //! # Features //! @@ -519,6 +521,19 @@ //! which enables capturing the type stack when serializing or deserializing a type //! and displaying it in error messages. //! +//! ## `auto_register` +//! +//! | Default | Dependencies | +//! | :-----: | :-------------------------------: | +//! | ❌ | [`bevy_reflect_derive/auto_register`] | +//! +//! This feature enables automatic registration of types that derive [`Reflect`] +//! for supported platforms (Linux, macOS, iOS, FreeBSD, Android, Windows, WebAssembly) using the `inventory` crate. +//! +//! When this feature is enabled `bevy_reflect` will automatically collects all types that derive [`Reflect`] on app 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. +//! //! [Reflection]: https://en.wikipedia.org/wiki/Reflective_programming //! [Bevy]: https://bevyengine.org/ //! [limitations]: #limitations diff --git a/examples/reflection/reflection.rs b/examples/reflection/reflection.rs index ba5e15e74b..f2130c35de 100644 --- a/examples/reflection/reflection.rs +++ b/examples/reflection/reflection.rs @@ -16,13 +16,15 @@ 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. All types without generics that `derive(Reflect)` are automatically registered. +/// Reflect. /// /// 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. diff --git a/examples/scene/scene.rs b/examples/scene/scene.rs index 16d6016ce5..e0072df170 100644 --- a/examples/scene/scene.rs +++ b/examples/scene/scene.rs @@ -35,6 +35,9 @@ 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 54ac872bd05c0055cac062c99f91c8af762d44f9 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Wed, 12 Mar 2025 21:22:22 +0000 Subject: [PATCH 38/61] update features --- docs/cargo_features.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cargo_features.md b/docs/cargo_features.md index 6bbddc4051..0fe0484b81 100644 --- a/docs/cargo_features.md +++ b/docs/cargo_features.md @@ -44,7 +44,6 @@ 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| |std|Allows access to the `std` crate.| |sysinfo_plugin|Enables system information diagnostic plugin| @@ -98,6 +97,7 @@ The default feature set enables most of the expected features of a game engine, |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| |qoi|QOI image format support| +|reflect_auto_register|Enable automatic reflect registration| |reflect_documentation|Enable documentation reflection| |reflect_functions|Enable function reflection| |serialize|Enable serialization support through serde| From 098cff9aa1ea349fb02f181f0a177c2943b98533 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 20 Mar 2025 12:42:59 +0000 Subject: [PATCH 39/61] Revert "remove empty enum from tests" This reverts commit 2eb5d1bdfc996dc1c09831e537da82946b2c5e7f. --- crates/bevy_reflect/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 200b63b474..6e012ef562 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -3420,6 +3420,10 @@ bevy_reflect::tests::Test { B, } + // ZST enum + #[derive(Reflect)] + enum ZSTEnumReflect {} + // Opaque struct #[derive(Reflect, Clone)] #[reflect(opaque)] @@ -3439,6 +3443,7 @@ bevy_reflect::tests::Test { 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::())); } From 21a5ba087381439592afb6e129d1cfe459d089d4 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Tue, 25 Mar 2025 12:11:20 +0000 Subject: [PATCH 40/61] implement automatic type registrations without relying on `inventory` --- Cargo.toml | 4 + crates/bevy_internal/Cargo.toml | 8 ++ crates/bevy_reflect/Cargo.toml | 5 +- crates/bevy_reflect/derive/Cargo.toml | 2 + .../bevy_reflect/derive/src/impls/common.rs | 59 +++++++-- crates/bevy_reflect/derive/src/lib.rs | 52 ++++++++ crates/bevy_reflect/src/lib.rs | 119 +++++++++++++----- crates/bevy_reflect/src/type_registry.rs | 7 +- docs/cargo_features.md | 3 +- 9 files changed, 211 insertions(+), 48 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ff3793511c..944221c7dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -157,6 +157,7 @@ default = [ "hdr", "multi_threaded", "png", + "reflect_auto_register", "smaa_luts", "sysinfo_plugin", "tonemapping_luts", @@ -519,6 +520,9 @@ reflect_documentation = ["bevy_internal/reflect_documentation"] # Enable automatic reflect registration reflect_auto_register = ["bevy_internal/reflect_auto_register"] +# Enable automatic reflect registration without inventory. See `reflect::load_type_registrations` for more info. +reflect_auto_register_static = ["bevy_internal/reflect_auto_register_static"] + # Enable winit custom cursor support custom_cursor = ["bevy_internal/custom_cursor"] diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index 4a2b31ce64..58291e1782 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -275,6 +275,7 @@ reflect_functions = [ "bevy_app/reflect_functions", "bevy_ecs/reflect_functions", ] + # Enable automatic reflect registration reflect_auto_register = [ "bevy_reflect/auto_register", @@ -282,6 +283,13 @@ reflect_auto_register = [ "bevy_ecs/reflect_auto_register", ] +# Enable automatic reflect registration without inventory. See `reflect::load_type_registrations` for more info. +reflect_auto_register_static = [ + "bevy_reflect/auto_register_static", + "bevy_app/reflect_auto_register", + "bevy_ecs/reflect_auto_register", +] + # Enable documentation reflection reflect_documentation = ["bevy_reflect/documentation"] diff --git a/crates/bevy_reflect/Cargo.toml b/crates/bevy_reflect/Cargo.toml index bbf812e681..d19c8aabd5 100644 --- a/crates/bevy_reflect/Cargo.toml +++ b/crates/bevy_reflect/Cargo.toml @@ -68,8 +68,11 @@ critical-section = [ "bevy_platform_support/critical-section", "bevy_utils/critical-section", ] -# Enables automatic reflect registration + +## Enables automatic reflect registration auto_register = ["bevy_reflect_derive/auto_register", "dep:inventory"] +## Enable automatic reflect registration without inventory. See `load_type_registrations` for more info. +auto_register_static = ["bevy_reflect_derive/auto_register_static"] ## Enables use of browser APIs. ## Note this is currently only applicable on `wasm32` architectures. diff --git a/crates/bevy_reflect/derive/Cargo.toml b/crates/bevy_reflect/derive/Cargo.toml index dcea9e70c9..f865f57e20 100644 --- a/crates/bevy_reflect/derive/Cargo.toml +++ b/crates/bevy_reflect/derive/Cargo.toml @@ -19,6 +19,8 @@ documentation = [] functions = [] # Enables automatic reflect registration auto_register = [] +# Enables automatic reflection on platforms not supported by inventory. See `load_type_registrations` for more info. +auto_register_static = [] [dependencies] bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.16.0-dev" } diff --git a/crates/bevy_reflect/derive/src/impls/common.rs b/crates/bevy_reflect/derive/src/impls/common.rs index e3fc1e726f..8927f3d8f7 100644 --- a/crates/bevy_reflect/derive/src/impls/common.rs +++ b/crates/bevy_reflect/derive/src/impls/common.rs @@ -170,11 +170,56 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option::__register - ) - } - }) + if cfg!(feature = "auto_register_static") { + use std::{env, fs, io::Write, path::PathBuf, sync::LazyLock, sync::Mutex}; + + // Names of registrations functions will be stored in this file. + // To allow writing to this file from multiple threads during compilation it is protected by mutex. + // This static is valid for the duration of compilation of one crate and we have one file per crate, + // so it is enough to protect compilation threads from overwriting each other. + // This file is reset on every recompilation. + static REGISTRATION_FNS_EXPORT: LazyLock> = LazyLock::new(|| { + let path = PathBuf::from("target").join("type_registrations"); + fs::DirBuilder::new() + .recursive(true) + .create(&path) + .unwrap_or_else(|_| panic!("Failed to create {:?}", path)); + let file_path = path.join( + env::var("CARGO_CRATE_NAME") + .expect("Expected cargo to set CARGO_CRATE_NAME env var"), + ); + let file = fs::OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(&file_path) + .unwrap_or_else(|_| panic!("Failed to create {:?}", file_path)); + Mutex::new(file) + }); + + let export_name = format!("_bevy_reflect_register_{}", uuid::Uuid::new_v4().as_u128()); + if env::var("BEVY_REFLECT_AUTO_REGISTER_STATIC").is_ok_and(|v| v != "0") { + let mut file = REGISTRATION_FNS_EXPORT.lock().unwrap(); + writeln!(file, "{}", export_name) + .unwrap_or_else(|_| panic!("Failed to write registration function")); + // We must sync_data to ensure all content is written before releasing the mutex. + file.sync_data().unwrap(); + }; + Some(quote! { + /// # Safety + /// This function must only be used by the `load_type_registrations` macro. + #[unsafe(export_name=#export_name)] + pub unsafe extern "C" fn bevy_register_type(registry: &mut #bevy_reflect_path::TypeRegistry) { + <#type_path as #bevy_reflect_path::__macro_exports::RegisterForReflection>::__register(registry); + } + }) + } else { + Some(quote! { + #bevy_reflect_path::__macro_exports::auto_register::inventory::submit!{ + #bevy_reflect_path::__macro_exports::auto_register::AutomaticReflectRegistrations( + <#type_path as #bevy_reflect_path::__macro_exports::auto_register::RegisterForReflection>::__register + ) + } + }) + } } diff --git a/crates/bevy_reflect/derive/src/lib.rs b/crates/bevy_reflect/derive/src/lib.rs index a997c501b9..d5a7383ffb 100644 --- a/crates/bevy_reflect/derive/src/lib.rs +++ b/crates/bevy_reflect/derive/src/lib.rs @@ -40,6 +40,8 @@ mod trait_reflection; mod type_path; mod where_clause_options; +use std::{fs, io::Read, path::PathBuf}; + use crate::derive_data::{ReflectDerive, ReflectMeta, ReflectStruct}; use container_attributes::ContainerAttributes; use derive_data::{ReflectImplSource, ReflectProvenance, ReflectTraitToImpl, ReflectTypePath}; @@ -849,3 +851,53 @@ pub fn impl_type_path(input: TokenStream) -> TokenStream { }; }) } + +/// Collects and loads type registrations when using `auto_register_static` feature. +/// The steps to using it correctly require the following: +/// 1. This macro must be called **last** during compilation. This can be achieved by putting your main function +/// in a separate crate or restructuring your project to be separated into `bin` and `lib`, and putting this macro in `bin`. +/// Any automatic type registrations using `#[derive(Reflect)]` within the same crate as this macro are not guaranteed to run. +/// 2. Your project must be compiled with `auto_register_static` feature **and** `BEVY_REFLECT_AUTO_REGISTER_STATIC=1` env variable. +/// Enabling the feature generates registration functions while setting the variable enables export and +/// caching of registration function names. +/// 3. Must be called before creating `App` or using `TypeRegistry::register_derived_types`. +/// +/// If you're experiencing linking issues try running `cargo clean` before rebuilding. +#[proc_macro] +pub fn load_type_registrations(_input: TokenStream) -> TokenStream { + if !cfg!(feature = "auto_register_static") { + return TokenStream::new(); + } + + let Ok(dir) = fs::read_dir(PathBuf::from("target").join("type_registrations")) else { + return TokenStream::new(); + }; + let mut str_buf = String::new(); + let mut registration_fns = Vec::new(); + for file_path in dir { + let mut file = fs::OpenOptions::new() + .read(true) + .open(file_path.unwrap().path()) + .unwrap(); + file.read_to_string(&mut str_buf).unwrap(); + registration_fns.extend(str_buf.lines().filter(|s| !s.is_empty()).map(|s| { + s.parse::() + .expect("Unexpected function name") + })); + str_buf.clear(); + } + let bevy_reflect_path = meta::get_bevy_reflect_path(); + TokenStream::from(quote! { + { + fn _register_types(){ + unsafe extern "C" { + #( fn #registration_fns(registry_ptr: &mut #bevy_reflect_path::TypeRegistry); )* + }; + unsafe { + #( #bevy_reflect_path::__macro_exports::auto_register::push_registration_fn(#registration_fns); )* + }; + } + _register_types(); + } + }) +} diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 4021f413bd..083d2c3608 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -733,52 +733,105 @@ pub mod __macro_exports { impl RegisterForReflection for DynamicTuple {} /// Automatic reflect registration implementation - #[cfg(feature = "auto_register")] + #[cfg(any(feature = "auto_register_static", feature = "auto_register"))] pub mod auto_register { - use super::*; + pub use super::*; - pub use inventory; + /// inventory impl + #[cfg(all(not(feature = "auto_register_static"), feature = "auto_register"))] + mod __automatic_type_registration_impl { + use super::*; - #[cfg(target_family = "wasm")] - mod wasm_support { - use bevy_platform_support::sync::atomic::{AtomicBool, Ordering}; + pub use inventory; - static INIT_DONE: AtomicBool = AtomicBool::new(false); + /// Stores type registration functions + pub struct AutomaticReflectRegistrations(pub fn(&mut TypeRegistry)); - #[expect(unsafe_code, reason = "This function is generated by linker.")] - unsafe extern "C" { - fn __wasm_call_ctors(); + /// Registers all collected types. + pub fn register_types(registry: &mut TypeRegistry) { + #[cfg(target_family = "wasm")] + crate::__macro_exports::wasm_support::init(); + for registration_fn in inventory::iter:: { + registration_fn.0(registry); + } } - /// This function must be called before using [`inventory::iter`] on [`AutomaticReflectRegistrations`] to run constructors on all platforms. - pub fn init() { - if INIT_DONE.swap(true, Ordering::Relaxed) { - return; - }; - // SAFETY: - // This will call constructors on wasm platforms at most once (as long as `init` is the only function that calls `__wasm_call_ctors`). - // - // For more information see: https://docs.rs/inventory/latest/inventory/#webassembly-and-constructors - #[expect( - unsafe_code, - reason = "This function must be called to use inventory on wasm." - )] - unsafe { - __wasm_call_ctors(); + inventory::collect!(AutomaticReflectRegistrations); + + #[cfg(target_family = "wasm")] + mod wasm_support { + use bevy_platform_support::sync::atomic::{AtomicBool, Ordering}; + + static INIT_DONE: AtomicBool = AtomicBool::new(false); + + #[expect(unsafe_code, reason = "This function is generated by linker.")] + unsafe extern "C" { + fn __wasm_call_ctors(); + } + + /// This function must be called before using [`inventory::iter`] on [`AutomaticReflectRegistrations`] to run constructors on all platforms. + pub fn init() { + if INIT_DONE.swap(true, Ordering::Relaxed) { + return; + }; + // SAFETY: + // This will call constructors on wasm platforms at most once (as long as `init` is the only function that calls `__wasm_call_ctors`). + // + // For more information see: https://docs.rs/inventory/latest/inventory/#webassembly-and-constructors + #[expect( + unsafe_code, + reason = "This function must be called to use inventory on wasm." + )] + unsafe { + __wasm_call_ctors(); + } } } } - /// This function must be called before using [`inventory::iter`] on [`AutomaticReflectRegistrations`] to run constructors on all platforms. - pub fn init() { - #[cfg(target_family = "wasm")] - wasm_support::init(); + /// static impl + #[cfg(feature = "auto_register_static")] + mod __automatic_type_registration_impl { + use super::*; + use alloc::vec::Vec; + use bevy_platform_support::sync::Mutex; + + static REGISTRATION_FNS: Mutex> = Mutex::new(Vec::new()); + + /// # Safety + /// This function is expected to be used only by `load_type_registrations` macro. + /// It is unsafe to use it in any other way. + #[expect( + unsafe_code, + reason = "This function is unsafe to use outside of the intended macro." + )] + pub unsafe fn push_registration_fn( + registration_fn: unsafe extern "C" fn(&mut TypeRegistry), + ) { + REGISTRATION_FNS.lock().unwrap().push( + // SAFETY: The caller is responsible for passing only valid functions here. + #[expect( + unsafe_code, + reason = "The caller is responsible for passing only valid functions here." + )] + unsafe { + core::mem::transmute::< + for<'a> unsafe extern "C" fn(&'a mut TypeRegistry), + for<'a> fn(&'a mut TypeRegistry), + >(registration_fn) + }, + ); + } + + /// Registers all collected types. + pub fn register_types(registry: &mut TypeRegistry) { + for func in REGISTRATION_FNS.lock().unwrap().iter() { + (func)(registry); + } + } } - /// Stores registration function to be used during automatic reflect registration. - pub struct AutomaticReflectRegistrations(pub fn(&mut TypeRegistry)); - - inventory::collect!(AutomaticReflectRegistrations); + pub use __automatic_type_registration_impl::*; } } diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 6d3bf91bc1..061da67a66 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -154,12 +154,7 @@ impl TypeRegistry { /// ``` #[cfg(feature = "auto_register")] pub fn register_derived_types(&mut self) { - crate::__macro_exports::auto_register::init(); - for registration in inventory::iter::< - crate::__macro_exports::auto_register::AutomaticReflectRegistrations, - >() { - registration.0(self); - } + crate::__macro_exports::auto_register::register_types(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 5fc0329597..71acf5a686 100644 --- a/docs/cargo_features.md +++ b/docs/cargo_features.md @@ -45,6 +45,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| |std|Allows access to the `std` crate.| |sysinfo_plugin|Enables system information diagnostic plugin| @@ -99,7 +100,7 @@ The default feature set enables most of the expected features of a game engine, |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| |qoi|QOI image format support| -|reflect_auto_register|Enable automatic reflect registration| +|reflect_auto_register_static|Enable automatic reflect registration without inventory. See `reflect::load_type_registrations` for more info.| |reflect_documentation|Enable documentation reflection| |reflect_functions|Enable function reflection| |serialize|Enable serialization support through serde| From fa60c51f4448b0eb0abbd60ce6a9c0f0a84e90ca Mon Sep 17 00:00:00 2001 From: eugineerd Date: Tue, 25 Mar 2025 13:10:02 +0000 Subject: [PATCH 41/61] add an example for static automatic registration --- Cargo.toml | 13 ++++++ .../auto_register_static/Cargo.toml | 18 ++++++++ .../reflection/auto_register_static/Makefile | 4 ++ .../reflection/auto_register_static/README.md | 16 +++++++ .../auto_register_static/src/lib.rs | 44 +++++++++++++++++++ .../auto_register_static/src/main.rs | 10 +++++ 6 files changed, 105 insertions(+) create mode 100644 examples/reflection/auto_register_static/Cargo.toml create mode 100644 examples/reflection/auto_register_static/Makefile create mode 100644 examples/reflection/auto_register_static/README.md create mode 100644 examples/reflection/auto_register_static/src/lib.rs create mode 100644 examples/reflection/auto_register_static/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 944221c7dd..61524ca720 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,8 @@ members = [ "examples/mobile", # Examples of using Bevy on no_std platforms. "examples/no_std/*", + # Examples of compiling Bevy with automatic reflect type registration for platforms without `inventory` support. + "examples/reflection/auto_register_static", # Benchmarks "benches", # Internal tools that are not published. @@ -2669,6 +2671,17 @@ description = "Demonstrates how to create and use type data" category = "Reflection" wasm = false +[[example]] +name = "auto_register_static" +path = "examples/reflection/auto_register_static/src/main.rs" +doc-scrape-examples = true + +[package.metadata.example.auto_register_static] +name = "Automatic types registartion" +description = "Demonstrates how to set up automatic reflect types registration for platforms without `inventory` support" +category = "Reflection" +wasm = false + # Scene [[example]] name = "scene" diff --git a/examples/reflection/auto_register_static/Cargo.toml b/examples/reflection/auto_register_static/Cargo.toml new file mode 100644 index 0000000000..a72120a7d3 --- /dev/null +++ b/examples/reflection/auto_register_static/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "auto_register_static" +version = "0.1.0" +edition = "2024" + +[lib] +# Our app must be a lib for static auto registration to work. +crate-type = ["lib"] + +[[bin]] +# Name of the binary must be the same as the name of the lib. +name = "auto_register_static" + +[dependencies] +bevy = { path = "../../../" } + +[lints] +workspace = true diff --git a/examples/reflection/auto_register_static/Makefile b/examples/reflection/auto_register_static/Makefile new file mode 100644 index 0000000000..7b47a20e13 --- /dev/null +++ b/examples/reflection/auto_register_static/Makefile @@ -0,0 +1,4 @@ +.PHONEY: run + +run: + BEVY_REFLECT_AUTO_REGISTER_STATIC=1 cargo run --features bevy/reflect_auto_register_static \ No newline at end of file diff --git a/examples/reflection/auto_register_static/README.md b/examples/reflection/auto_register_static/README.md new file mode 100644 index 0000000000..ac9f506116 --- /dev/null +++ b/examples/reflection/auto_register_static/README.md @@ -0,0 +1,16 @@ +# Automatic registration example for platforms without inventory support + +This example illustrates how to use automatic type registration of `bevy_reflect` on platforms that don't support `inventory`. + +To run the example, use the provided `Makefile` with `make run` or run manually by setting env var and enabling the required feature: +```sh +BEVY_REFLECT_AUTO_REGISTER_STATIC=1 cargo run --features bevy/reflect_auto_register_static +``` +This approach should generally work on all platforms, however it is less convenient and a slows down linking and it's recommended to use it only as a fallback. + +Here's a list of caveats of this approach: +1. `load_type_registrations!` macro must be called before constructing `App` or using `TypeRegistry::register_derived_types`. +2. All of the types to be automatically registered must be declared in a separate from `load_type_registrations!` crate. This is why this example uses separate `lib` and `bin` setup. +3. Registration function names are cached in `target/type_registrations`. Due to incremental compilation the only way to rebuild this cache is to build without `bevy/reflect_auto_register_static` (or `auto_register_static` if just using `bevy_reflect`) feature enabled and then rebuild again with this feature and `BEVY_REFLECT_AUTO_REGISTER_STATIC=1` environment variable set. Running `cargo clean` before recompiling is also an option, but it is even slower to do. + +If you're experiencing linking issues try running `cargo clean` before rebuilding. diff --git a/examples/reflection/auto_register_static/src/lib.rs b/examples/reflection/auto_register_static/src/lib.rs new file mode 100644 index 0000000000..e43f563e95 --- /dev/null +++ b/examples/reflection/auto_register_static/src/lib.rs @@ -0,0 +1,44 @@ +//! Demonstrates how to set up automatic reflect types registration for platforms without `inventory` support +use bevy::prelude::*; + +// The type that should be automatically registered. +// All types subject to automatic registration must be defined in `lib`, +// any `#[derive(Reflect)]` registration without the `bin` are not guaranteed to be registered automatically. +#[derive(Reflect)] +struct Struct { + a: i32, +} + +mod private { + mod very_private { + use bevy::prelude::*; + + // Works with private types too! + #[derive(Reflect)] + struct PrivateStruct { + a: i32, + } + } +} + +/// This is the main entrypoint, bin just forwards to it. +pub fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_systems(Startup, startup) + .run(); +} + +fn startup(reg: Res) { + let registry = reg.read(); + info!( + "Is `Struct` get registered? {}", + registry.contains(core::any::TypeId::of::()) + ); + info!( + "Type info of `PrivateStruct`: {:?}", + registry + .get_with_short_type_path("PrivateStruct") + .expect("Not registered") + ); +} diff --git a/examples/reflection/auto_register_static/src/main.rs b/examples/reflection/auto_register_static/src/main.rs new file mode 100644 index 0000000000..b9359d11a0 --- /dev/null +++ b/examples/reflection/auto_register_static/src/main.rs @@ -0,0 +1,10 @@ +//! Demonstrates how to set up automatic reflect types registration for platforms without `inventory` support +use auto_register_static::main as lib_main; +use bevy::reflect::load_type_registrations; + +fn main() { + // This must be called before our main to collect all type registration functions. + load_type_registrations!(); + // After running load_type_registrations! we just forward to our main. + lib_main(); +} From 2958700eb2fe44bcf3bdc311d2fdee10005bb7e5 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Tue, 25 Mar 2025 13:32:10 +0000 Subject: [PATCH 42/61] Add `auto_register_inventory` feature to `bevy_reflect` --- crates/bevy_internal/Cargo.toml | 4 ++-- crates/bevy_reflect/Cargo.toml | 19 +++++++++++++++---- crates/bevy_reflect/derive/Cargo.toml | 7 +++++-- .../bevy_reflect/derive/src/impls/common.rs | 4 +++- crates/bevy_reflect/src/lib.rs | 7 +++++-- 5 files changed, 30 insertions(+), 11 deletions(-) diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index 58291e1782..9957d793bf 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -276,9 +276,9 @@ reflect_functions = [ "bevy_ecs/reflect_functions", ] -# Enable automatic reflect registration +# Enable automatic reflect registration using inventory. reflect_auto_register = [ - "bevy_reflect/auto_register", + "bevy_reflect/auto_register_inventory", "bevy_app/reflect_auto_register", "bevy_ecs/reflect_auto_register", ] diff --git a/crates/bevy_reflect/Cargo.toml b/crates/bevy_reflect/Cargo.toml index d19c8aabd5..d7c5db2387 100644 --- a/crates/bevy_reflect/Cargo.toml +++ b/crates/bevy_reflect/Cargo.toml @@ -69,10 +69,21 @@ critical-section = [ "bevy_utils/critical-section", ] -## Enables automatic reflect registration -auto_register = ["bevy_reflect_derive/auto_register", "dep:inventory"] -## Enable automatic reflect registration without inventory. See `load_type_registrations` for more info. -auto_register_static = ["bevy_reflect_derive/auto_register_static"] +# Enables automatic reflect registration. Does nothing by itself, +# must select `auto_register_inventory` or `auto_register_static` to make it work. +auto_register = [] +## Enables automatic reflect registration using inventory. Not supported on all platforms. +auto_register_inventory = [ + "auto_register", + "bevy_reflect_derive/auto_register_inventory", + "dep:inventory", +] +## Enable automatic reflect registration without inventory. This feature has precedence over `auto_register_inventory`. +## See `load_type_registrations` for more info. +auto_register_static = [ + "auto_register", + "bevy_reflect_derive/auto_register_static", +] ## Enables use of browser APIs. ## Note this is currently only applicable on `wasm32` architectures. diff --git a/crates/bevy_reflect/derive/Cargo.toml b/crates/bevy_reflect/derive/Cargo.toml index f865f57e20..2445c15e4a 100644 --- a/crates/bevy_reflect/derive/Cargo.toml +++ b/crates/bevy_reflect/derive/Cargo.toml @@ -17,10 +17,13 @@ default = [] documentation = [] # Enables macro logic related to function reflection functions = [] -# Enables automatic reflect registration +# Enables automatic reflect registration. Does nothing by itself, +# must select `auto_register_inventory` or `auto_register_static` to make it work. auto_register = [] +# Enables automatic reflection using inventory. Not supported on all platforms. +auto_register_inventory = ["auto_register"] # Enables automatic reflection on platforms not supported by inventory. See `load_type_registrations` for more info. -auto_register_static = [] +auto_register_static = ["auto_register"] [dependencies] bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.16.0-dev" } diff --git a/crates/bevy_reflect/derive/src/impls/common.rs b/crates/bevy_reflect/derive/src/impls/common.rs index 8927f3d8f7..508bcd4ebc 100644 --- a/crates/bevy_reflect/derive/src/impls/common.rs +++ b/crates/bevy_reflect/derive/src/impls/common.rs @@ -213,7 +213,7 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option::__register(registry); } }) - } else { + } else if cfg!(feature = "auto_register_inventory") { Some(quote! { #bevy_reflect_path::__macro_exports::auto_register::inventory::submit!{ #bevy_reflect_path::__macro_exports::auto_register::AutomaticReflectRegistrations( @@ -221,5 +221,7 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option Date: Tue, 25 Mar 2025 14:07:30 +0000 Subject: [PATCH 43/61] example types fixes --- examples/reflection/auto_register_static/README.md | 4 ++-- examples/reflection/auto_register_static/src/lib.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/reflection/auto_register_static/README.md b/examples/reflection/auto_register_static/README.md index ac9f506116..4fd18875cc 100644 --- a/examples/reflection/auto_register_static/README.md +++ b/examples/reflection/auto_register_static/README.md @@ -6,11 +6,11 @@ To run the example, use the provided `Makefile` with `make run` or run manually ```sh BEVY_REFLECT_AUTO_REGISTER_STATIC=1 cargo run --features bevy/reflect_auto_register_static ``` -This approach should generally work on all platforms, however it is less convenient and a slows down linking and it's recommended to use it only as a fallback. +This approach should generally work on all platforms, however it is less convenient and slows down linking. It's recommended to use it only as a fallback. Here's a list of caveats of this approach: 1. `load_type_registrations!` macro must be called before constructing `App` or using `TypeRegistry::register_derived_types`. 2. All of the types to be automatically registered must be declared in a separate from `load_type_registrations!` crate. This is why this example uses separate `lib` and `bin` setup. -3. Registration function names are cached in `target/type_registrations`. Due to incremental compilation the only way to rebuild this cache is to build without `bevy/reflect_auto_register_static` (or `auto_register_static` if just using `bevy_reflect`) feature enabled and then rebuild again with this feature and `BEVY_REFLECT_AUTO_REGISTER_STATIC=1` environment variable set. Running `cargo clean` before recompiling is also an option, but it is even slower to do. +3. Registration function names are cached in `target/type_registrations`. Due to incremental compilation the only way to rebuild this cache is to build with `bevy/reflect_auto_register_static` (or `auto_register_static` if just using `bevy_reflect`) feature disabled, then delete `target/type_registrations` and rebuild again with this feature enabled and `BEVY_REFLECT_AUTO_REGISTER_STATIC=1` environment variable set. Running `cargo clean` before recompiling is also an option, but it is even slower to do. If you're experiencing linking issues try running `cargo clean` before rebuilding. diff --git a/examples/reflection/auto_register_static/src/lib.rs b/examples/reflection/auto_register_static/src/lib.rs index e43f563e95..8f7446ab96 100644 --- a/examples/reflection/auto_register_static/src/lib.rs +++ b/examples/reflection/auto_register_static/src/lib.rs @@ -2,8 +2,8 @@ use bevy::prelude::*; // The type that should be automatically registered. -// All types subject to automatic registration must be defined in `lib`, -// any `#[derive(Reflect)]` registration without the `bin` are not guaranteed to be registered automatically. +// All types subject to automatic registration must be defined not be define in the same crate as `load_type_registrations!``. +// Any `#[derive(Reflect)]` types within the `bin` crate are not guaranteed to be registered automatically. #[derive(Reflect)] struct Struct { a: i32, @@ -32,7 +32,7 @@ pub fn main() { fn startup(reg: Res) { let registry = reg.read(); info!( - "Is `Struct` get registered? {}", + "Is `Struct` registered? {}", registry.contains(core::any::TypeId::of::()) ); info!( From ff78027fc79cc7f7f9047b930adec80a737c6110 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 17 Apr 2025 18:29:52 +0000 Subject: [PATCH 44/61] fix `auto_register_static` example --- examples/reflection/auto_register_static/Cargo.toml | 9 ++++----- .../auto_register_static/src/{ => bin}/main.rs | 0 2 files changed, 4 insertions(+), 5 deletions(-) rename examples/reflection/auto_register_static/src/{ => bin}/main.rs (100%) diff --git a/examples/reflection/auto_register_static/Cargo.toml b/examples/reflection/auto_register_static/Cargo.toml index a72120a7d3..8d4c812831 100644 --- a/examples/reflection/auto_register_static/Cargo.toml +++ b/examples/reflection/auto_register_static/Cargo.toml @@ -1,18 +1,17 @@ [package] name = "auto_register_static" -version = "0.1.0" +version = "0.0.0" edition = "2024" +publish = false +license = "MIT OR Apache-2.0" [lib] # Our app must be a lib for static auto registration to work. crate-type = ["lib"] - -[[bin]] -# Name of the binary must be the same as the name of the lib. name = "auto_register_static" [dependencies] -bevy = { path = "../../../" } +bevy = { path = "../../../", default-features = false, features = ["trace"] } [lints] workspace = true diff --git a/examples/reflection/auto_register_static/src/main.rs b/examples/reflection/auto_register_static/src/bin/main.rs similarity index 100% rename from examples/reflection/auto_register_static/src/main.rs rename to examples/reflection/auto_register_static/src/bin/main.rs From 8ba6dad3d608fea63a6589f14fef3e21e9a8d414 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 17 Apr 2025 18:34:54 +0000 Subject: [PATCH 45/61] enable `auto_register_inventory` by default for `bevy_reflect` --- crates/bevy_reflect/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/Cargo.toml b/crates/bevy_reflect/Cargo.toml index f5750c2f5d..ae0c257ce1 100644 --- a/crates/bevy_reflect/Cargo.toml +++ b/crates/bevy_reflect/Cargo.toml @@ -10,7 +10,7 @@ keywords = ["bevy"] rust-version = "1.85.0" [features] -default = ["std", "smallvec", "debug"] +default = ["std", "smallvec", "debug", "auto_register_inventory"] # Features From da7768b7a3164d467449188eb5d72374a9a0873f Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 17 Apr 2025 18:40:44 +0000 Subject: [PATCH 46/61] update examples readme --- Cargo.toml | 2 +- examples/README.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 48b74f05c7..09c9ed80b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2675,7 +2675,7 @@ wasm = false [[example]] name = "auto_register_static" -path = "examples/reflection/auto_register_static/src/main.rs" +path = "examples/reflection/auto_register_static/src/lib.rs" doc-scrape-examples = true [package.metadata.example.auto_register_static] diff --git a/examples/README.md b/examples/README.md index 202c41a4f1..86197971ce 100644 --- a/examples/README.md +++ b/examples/README.md @@ -416,6 +416,7 @@ Example | Description Example | Description --- | --- +[Automatic types registartion](../examples/reflection/auto_register_static/src/lib.rs) | Demonstrates how to set up automatic reflect types registration for platforms without `inventory` support [Custom Attributes](../examples/reflection/custom_attributes.rs) | Registering and accessing custom attributes on reflected types [Dynamic Types](../examples/reflection/dynamic_types.rs) | How dynamic types are used with reflection [Function Reflection](../examples/reflection/function_reflection.rs) | Demonstrates how functions can be called dynamically using reflection From 22e6f27f5f00209e6da72fe3ca1eb85fabcc2593 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 17 Apr 2025 18:42:51 +0000 Subject: [PATCH 47/61] fix markdownlint errors --- examples/reflection/auto_register_static/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/reflection/auto_register_static/README.md b/examples/reflection/auto_register_static/README.md index 4fd18875cc..3ce8ab8418 100644 --- a/examples/reflection/auto_register_static/README.md +++ b/examples/reflection/auto_register_static/README.md @@ -3,14 +3,17 @@ This example illustrates how to use automatic type registration of `bevy_reflect` on platforms that don't support `inventory`. To run the example, use the provided `Makefile` with `make run` or run manually by setting env var and enabling the required feature: + ```sh BEVY_REFLECT_AUTO_REGISTER_STATIC=1 cargo run --features bevy/reflect_auto_register_static ``` + This approach should generally work on all platforms, however it is less convenient and slows down linking. It's recommended to use it only as a fallback. Here's a list of caveats of this approach: + 1. `load_type_registrations!` macro must be called before constructing `App` or using `TypeRegistry::register_derived_types`. 2. All of the types to be automatically registered must be declared in a separate from `load_type_registrations!` crate. This is why this example uses separate `lib` and `bin` setup. 3. Registration function names are cached in `target/type_registrations`. Due to incremental compilation the only way to rebuild this cache is to build with `bevy/reflect_auto_register_static` (or `auto_register_static` if just using `bevy_reflect`) feature disabled, then delete `target/type_registrations` and rebuild again with this feature enabled and `BEVY_REFLECT_AUTO_REGISTER_STATIC=1` environment variable set. Running `cargo clean` before recompiling is also an option, but it is even slower to do. -If you're experiencing linking issues try running `cargo clean` before rebuilding. +If you're experiencing linking issues try running `cargo clean` before rebuilding. \ No newline at end of file From 96712233770be296a54b8145d0cd916c7b3c00b8 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 17 Apr 2025 18:45:45 +0000 Subject: [PATCH 48/61] typos --- Cargo.toml | 2 +- examples/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 09c9ed80b3..28b08e0b77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2679,7 +2679,7 @@ path = "examples/reflection/auto_register_static/src/lib.rs" doc-scrape-examples = true [package.metadata.example.auto_register_static] -name = "Automatic types registartion" +name = "Automatic types registration" description = "Demonstrates how to set up automatic reflect types registration for platforms without `inventory` support" category = "Reflection" wasm = false diff --git a/examples/README.md b/examples/README.md index 86197971ce..f9e5fd5f65 100644 --- a/examples/README.md +++ b/examples/README.md @@ -416,7 +416,7 @@ Example | Description Example | Description --- | --- -[Automatic types registartion](../examples/reflection/auto_register_static/src/lib.rs) | Demonstrates how to set up automatic reflect types registration for platforms without `inventory` support +[Automatic types registration](../examples/reflection/auto_register_static/src/lib.rs) | Demonstrates how to set up automatic reflect types registration for platforms without `inventory` support [Custom Attributes](../examples/reflection/custom_attributes.rs) | Registering and accessing custom attributes on reflected types [Dynamic Types](../examples/reflection/dynamic_types.rs) | How dynamic types are used with reflection [Function Reflection](../examples/reflection/function_reflection.rs) | Demonstrates how functions can be called dynamically using reflection From e257d75b4ed850110d161edc4e13cb961ab76070 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 17 Apr 2025 18:49:07 +0000 Subject: [PATCH 49/61] fix trailing spaces --- examples/reflection/auto_register_static/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/reflection/auto_register_static/README.md b/examples/reflection/auto_register_static/README.md index 3ce8ab8418..de60e00584 100644 --- a/examples/reflection/auto_register_static/README.md +++ b/examples/reflection/auto_register_static/README.md @@ -16,4 +16,4 @@ Here's a list of caveats of this approach: 2. All of the types to be automatically registered must be declared in a separate from `load_type_registrations!` crate. This is why this example uses separate `lib` and `bin` setup. 3. Registration function names are cached in `target/type_registrations`. Due to incremental compilation the only way to rebuild this cache is to build with `bevy/reflect_auto_register_static` (or `auto_register_static` if just using `bevy_reflect`) feature disabled, then delete `target/type_registrations` and rebuild again with this feature enabled and `BEVY_REFLECT_AUTO_REGISTER_STATIC=1` environment variable set. Running `cargo clean` before recompiling is also an option, but it is even slower to do. -If you're experiencing linking issues try running `cargo clean` before rebuilding. \ No newline at end of file +If you're experiencing linking issues try running `cargo clean` before rebuilding. From 7c60aa8c5e8fcd10b458228c720b8c703398f905 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 17 Apr 2025 19:15:22 +0000 Subject: [PATCH 50/61] fix `wasm_support` path --- 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 f069295e4f..bafece34b9 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -753,7 +753,7 @@ pub mod __macro_exports { /// Registers all collected types. pub fn register_types(registry: &mut TypeRegistry) { #[cfg(target_family = "wasm")] - crate::__macro_exports::wasm_support::init(); + wasm_support::init(); for registration_fn in inventory::iter:: { registration_fn.0(registry); } From 202e59f336186722115d9beeed150564a23def76 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Fri, 18 Apr 2025 11:21:24 +0000 Subject: [PATCH 51/61] use proper calling convention for extern fns --- crates/bevy_reflect/derive/src/impls/common.rs | 2 +- crates/bevy_reflect/derive/src/lib.rs | 2 +- crates/bevy_reflect/src/lib.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bevy_reflect/derive/src/impls/common.rs b/crates/bevy_reflect/derive/src/impls/common.rs index 508bcd4ebc..446658b200 100644 --- a/crates/bevy_reflect/derive/src/impls/common.rs +++ b/crates/bevy_reflect/derive/src/impls/common.rs @@ -209,7 +209,7 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option::__register(registry); } }) diff --git a/crates/bevy_reflect/derive/src/lib.rs b/crates/bevy_reflect/derive/src/lib.rs index d5a7383ffb..f848c06547 100644 --- a/crates/bevy_reflect/derive/src/lib.rs +++ b/crates/bevy_reflect/derive/src/lib.rs @@ -890,7 +890,7 @@ pub fn load_type_registrations(_input: TokenStream) -> TokenStream { TokenStream::from(quote! { { fn _register_types(){ - unsafe extern "C" { + unsafe extern "Rust" { #( fn #registration_fns(registry_ptr: &mut #bevy_reflect_path::TypeRegistry); )* }; unsafe { diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index bafece34b9..30e890f7c9 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -809,7 +809,7 @@ pub mod __macro_exports { reason = "This function is unsafe to use outside of the intended macro." )] pub unsafe fn push_registration_fn( - registration_fn: unsafe extern "C" fn(&mut TypeRegistry), + registration_fn: unsafe extern "Rust" fn(&mut TypeRegistry), ) { REGISTRATION_FNS.lock().unwrap().push( // SAFETY: The caller is responsible for passing only valid functions here. @@ -819,7 +819,7 @@ pub mod __macro_exports { )] unsafe { core::mem::transmute::< - for<'a> unsafe extern "C" fn(&'a mut TypeRegistry), + for<'a> unsafe extern "Rust" fn(&'a mut TypeRegistry), for<'a> fn(&'a mut TypeRegistry), >(registration_fn) }, From d6a254faefab070f6593db5a74e19cca674f09b4 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Fri, 18 Apr 2025 11:35:49 +0000 Subject: [PATCH 52/61] remove some unsafe from `auto_register_static` --- crates/bevy_reflect/derive/src/lib.rs | 2 +- crates/bevy_reflect/src/lib.rs | 26 +++----------------------- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/crates/bevy_reflect/derive/src/lib.rs b/crates/bevy_reflect/derive/src/lib.rs index f848c06547..1f4fd9a191 100644 --- a/crates/bevy_reflect/derive/src/lib.rs +++ b/crates/bevy_reflect/derive/src/lib.rs @@ -891,7 +891,7 @@ pub fn load_type_registrations(_input: TokenStream) -> TokenStream { { fn _register_types(){ unsafe extern "Rust" { - #( fn #registration_fns(registry_ptr: &mut #bevy_reflect_path::TypeRegistry); )* + #( safe fn #registration_fns(registry_ptr: &mut #bevy_reflect_path::TypeRegistry); )* }; unsafe { #( #bevy_reflect_path::__macro_exports::auto_register::push_registration_fn(#registration_fns); )* diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 30e890f7c9..61951ba503 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -801,29 +801,9 @@ pub mod __macro_exports { static REGISTRATION_FNS: Mutex> = Mutex::new(Vec::new()); - /// # Safety - /// This function is expected to be used only by `load_type_registrations` macro. - /// It is unsafe to use it in any other way. - #[expect( - unsafe_code, - reason = "This function is unsafe to use outside of the intended macro." - )] - pub unsafe fn push_registration_fn( - registration_fn: unsafe extern "Rust" fn(&mut TypeRegistry), - ) { - REGISTRATION_FNS.lock().unwrap().push( - // SAFETY: The caller is responsible for passing only valid functions here. - #[expect( - unsafe_code, - reason = "The caller is responsible for passing only valid functions here." - )] - unsafe { - core::mem::transmute::< - for<'a> unsafe extern "Rust" fn(&'a mut TypeRegistry), - for<'a> fn(&'a mut TypeRegistry), - >(registration_fn) - }, - ); + /// Adds adds a new registration function for [`TypeRegistry`] + pub fn push_registration_fn(registration_fn: fn(&mut TypeRegistry)) { + REGISTRATION_FNS.lock().unwrap().push(registration_fn); } /// Registers all collected types. From 567683a87caf3c1993edb52d8d268d6c00980dea Mon Sep 17 00:00:00 2001 From: eugineerd Date: Fri, 25 Apr 2025 17:30:01 +0000 Subject: [PATCH 53/61] add release notes --- .../reflect_auto_registration.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 release-content/release-notes/reflect_auto_registration.md diff --git a/release-content/release-notes/reflect_auto_registration.md b/release-content/release-notes/reflect_auto_registration.md new file mode 100644 index 0000000000..7608dc65d7 --- /dev/null +++ b/release-content/release-notes/reflect_auto_registration.md @@ -0,0 +1,50 @@ +--- +title: Reflect auto registration +authors: ["@eugineerd"] +pull_requests: [15030] +--- + +# Automatic [`Reflect`] registration +Deriving [`Reflect`] on types opts into **Bevy's** runtime reflection infrastructure, which is used to power systems like component runtime inspection and serialization. Before **Bevy 0.17**, any top-level +types that derive [`Reflect`] (not used as a field in some other [`Reflect`]-ed type) had to be manually registered using [`register_type`] for the runtime reflection to work with them. With this release, +all types that [`#[derive(Reflect)]`] are now automatically registered! This works for any types without generic type parameters and should reduce the boilerplate needed when adding functionality that depends on [`Reflect`]. + +```rs +fn main() { + // No need to manually call .register_type::() + App::new() + .add_plugins(DefaultPlugins) + .add_systems(Startup, setup) + .run(); +} + +#[derive(Reflect)] +pub struct Foo { + a: usize, +} + +fn setup(type_registry: Res) { + let type_registry = type_registry.read(); + assert!(type_registry.contains(TypeId::of::())); +} +``` + +In cases where automatic registration is undesirable, it can be opted-out of by adding #[reflect(no_auto_register)] reflect attribute to a type: +```rs +#[derive(Reflect)] +#[reflect(no_auto_register)] +pub struct Foo { + a: usize, +} +``` + +# Unsupported platforms +This feature relies on the [`inventory`] crate to collect all type registrations at compile-time. However, not all platforms are supported by [`inventory`], and while it would be best for +any unsupported platforms to be supported upstream, sometimes it might not be possible. For this reason, there is a different implementation of this feature that works on all platforms. +It comes with some caveats with regards to project structure and might increase compile time, so it is better used as a backup solution. The detailed instructions on how to use this feature +can be found in this [`example`]. + +[`Reflect`]: https://docs.rs/bevy/0.17.0/bevy/prelude/trait.Reflect.html +[`inventory`]: https://github.com/dtolnay/inventory +[`example`]: https://github.com/bevyengine/bevy/tree/release-0.17.0/examples/reflection/auto_register_static +[`register_type`]: https://docs.rs/bevy/0.17.0/bevy/prelude/struct.App.html#method.register_type \ No newline at end of file From b49063ddc2a63e5b72de98012b428f4a865d1ae7 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Fri, 25 Apr 2025 17:38:03 +0000 Subject: [PATCH 54/61] markdown lints --- .../release-notes/reflect_auto_registration.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/release-content/release-notes/reflect_auto_registration.md b/release-content/release-notes/reflect_auto_registration.md index 7608dc65d7..9af7d5b03a 100644 --- a/release-content/release-notes/reflect_auto_registration.md +++ b/release-content/release-notes/reflect_auto_registration.md @@ -4,8 +4,9 @@ authors: ["@eugineerd"] pull_requests: [15030] --- -# Automatic [`Reflect`] registration -Deriving [`Reflect`] on types opts into **Bevy's** runtime reflection infrastructure, which is used to power systems like component runtime inspection and serialization. Before **Bevy 0.17**, any top-level +## Automatic [`Reflect`] registration + +Deriving [`Reflect`] on types opts into **Bevy's** runtime reflection infrastructure, which is used to power systems like component runtime inspection and serialization. Before **Bevy 0.17**, any top-level types that derive [`Reflect`] (not used as a field in some other [`Reflect`]-ed type) had to be manually registered using [`register_type`] for the runtime reflection to work with them. With this release, all types that [`#[derive(Reflect)]`] are now automatically registered! This works for any types without generic type parameters and should reduce the boilerplate needed when adding functionality that depends on [`Reflect`]. @@ -30,6 +31,7 @@ fn setup(type_registry: Res) { ``` In cases where automatic registration is undesirable, it can be opted-out of by adding #[reflect(no_auto_register)] reflect attribute to a type: + ```rs #[derive(Reflect)] #[reflect(no_auto_register)] @@ -38,13 +40,14 @@ pub struct Foo { } ``` -# Unsupported platforms +## Unsupported platforms + This feature relies on the [`inventory`] crate to collect all type registrations at compile-time. However, not all platforms are supported by [`inventory`], and while it would be best for any unsupported platforms to be supported upstream, sometimes it might not be possible. For this reason, there is a different implementation of this feature that works on all platforms. -It comes with some caveats with regards to project structure and might increase compile time, so it is better used as a backup solution. The detailed instructions on how to use this feature +It comes with some caveats with regards to project structure and might increase compile time, so it is better used as a backup solution. The detailed instructions on how to use this feature can be found in this [`example`]. [`Reflect`]: https://docs.rs/bevy/0.17.0/bevy/prelude/trait.Reflect.html [`inventory`]: https://github.com/dtolnay/inventory [`example`]: https://github.com/bevyengine/bevy/tree/release-0.17.0/examples/reflection/auto_register_static -[`register_type`]: https://docs.rs/bevy/0.17.0/bevy/prelude/struct.App.html#method.register_type \ No newline at end of file +[`register_type`]: https://docs.rs/bevy/0.17.0/bevy/prelude/struct.App.html#method.register_type From baf5e1e04a4bfd2875466a4d96fc4f9774c4725b Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sun, 8 Jun 2025 16:08:49 +0000 Subject: [PATCH 55/61] support hotpatching --- crates/bevy_app/src/hotpatch.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/bevy_app/src/hotpatch.rs b/crates/bevy_app/src/hotpatch.rs index 1f9da40730..b0d2cb1be8 100644 --- a/crates/bevy_app/src/hotpatch.rs +++ b/crates/bevy_app/src/hotpatch.rs @@ -38,5 +38,13 @@ impl Plugin for HotPatchPlugin { } }, ); + + #[cfg(feature = "reflect_auto_register")] + app.add_systems( + crate::First, + move |registry: bevy_ecs::system::Res| { + registry.write().register_derived_types(); + }, + ); } } From 5f342233ffa39421a5a787defd1bbd946ae7a24b Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sun, 8 Jun 2025 17:03:39 +0000 Subject: [PATCH 56/61] actually run only on event --- crates/bevy_app/src/hotpatch.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/bevy_app/src/hotpatch.rs b/crates/bevy_app/src/hotpatch.rs index b0d2cb1be8..ca7c71fd4f 100644 --- a/crates/bevy_app/src/hotpatch.rs +++ b/crates/bevy_app/src/hotpatch.rs @@ -3,6 +3,8 @@ extern crate alloc; use alloc::sync::Arc; +#[cfg(feature = "reflect_auto_register")] +use bevy_ecs::schedule::IntoScheduleConfigs; use bevy_ecs::{event::EventWriter, HotPatched}; #[cfg(not(target_family = "wasm"))] use dioxus_devtools::connect_subsecond; @@ -42,9 +44,10 @@ impl Plugin for HotPatchPlugin { #[cfg(feature = "reflect_auto_register")] app.add_systems( crate::First, - move |registry: bevy_ecs::system::Res| { + (move |registry: bevy_ecs::system::Res| { registry.write().register_derived_types(); - }, + }) + .run_if(bevy_ecs::schedule::common_conditions::on_event::), ); } } From ca6827e7b94fcebce37667e71032cb447ebf52e9 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Wed, 16 Jul 2025 12:43:23 +0000 Subject: [PATCH 57/61] use new `proc_macro::Span` apis to produce stable names for registration functions --- .../bevy_reflect/derive/src/impls/common.rs | 59 +++++++++++++------ crates/bevy_reflect/derive/src/lib.rs | 7 +-- 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/crates/bevy_reflect/derive/src/impls/common.rs b/crates/bevy_reflect/derive/src/impls/common.rs index 1226aea308..8824fdcb10 100644 --- a/crates/bevy_reflect/derive/src/impls/common.rs +++ b/crates/bevy_reflect/derive/src/impls/common.rs @@ -169,40 +169,67 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option> = LazyLock::new(|| { - let path = PathBuf::from("target").join("type_registrations"); + let path = PathBuf::from("target").join("bevy_reflect_type_registrations"); fs::DirBuilder::new() .recursive(true) .create(&path) - .unwrap_or_else(|_| panic!("Failed to create {:?}", path)); - let file_path = path.join( - env::var("CARGO_CRATE_NAME") - .expect("Expected cargo to set CARGO_CRATE_NAME env var"), - ); + .unwrap_or_else(|_| panic!("Failed to create {path:?}")); + let file_path = path.join(env::var("CARGO_CRATE_NAME").unwrap()); let file = fs::OpenOptions::new() .create(true) .write(true) .truncate(true) .open(&file_path) - .unwrap_or_else(|_| panic!("Failed to create {:?}", file_path)); + .unwrap_or_else(|_| panic!("Failed to create {file_path:?}")); Mutex::new(file) }); - let export_name = format!("_bevy_reflect_register_{}", uuid::Uuid::new_v4().as_u128()); - if env::var("BEVY_REFLECT_AUTO_REGISTER_STATIC").is_ok_and(|v| v != "0") { + let crate_name = + env::var("CARGO_CRATE_NAME").expect("Expected cargo to set CARGO_CRATE_NAME env var"); + let span = Span::call_site(); + let mut hasher = DefaultHasher::new(); + span.file().hash(&mut hasher); + let file_path_hash = hasher.finish(); + + let export_name = format!( + "_bevy_reflect_register_{}_{}_{}_{}", + crate_name, + file_path_hash, + span.line(), + span.column(), + ); + + { let mut file = REGISTRATION_FNS_EXPORT.lock().unwrap(); - writeln!(file, "{}", export_name) - .unwrap_or_else(|_| panic!("Failed to write registration function")); - // We must sync_data to ensure all content is written before releasing the mutex. + writeln!(file, "{export_name}") + .unwrap_or_else(|_| panic!("Failed to write registration function {export_name}")); + // We must sync_data to ensure all content is written before releasing the lock. file.sync_data().unwrap(); }; + Some(quote! { /// # Safety /// This function must only be used by the `load_type_registrations` macro. @@ -211,7 +238,7 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option::__register(registry); } }) - } else if cfg!(feature = "auto_register_inventory") { + } else { Some(quote! { #bevy_reflect_path::__macro_exports::auto_register::inventory::submit!{ #bevy_reflect_path::__macro_exports::auto_register::AutomaticReflectRegistrations( @@ -219,7 +246,5 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option TokenStream { return TokenStream::new(); } - let Ok(dir) = fs::read_dir(PathBuf::from("target").join("type_registrations")) else { + let Ok(dir) = fs::read_dir(PathBuf::from("target").join("bevy_reflect_type_registrations")) + else { return TokenStream::new(); }; let mut str_buf = String::new(); @@ -893,9 +894,7 @@ pub fn load_type_registrations(_input: TokenStream) -> TokenStream { unsafe extern "Rust" { #( safe fn #registration_fns(registry_ptr: &mut #bevy_reflect_path::TypeRegistry); )* }; - unsafe { - #( #bevy_reflect_path::__macro_exports::auto_register::push_registration_fn(#registration_fns); )* - }; + #( #bevy_reflect_path::__macro_exports::auto_register::push_registration_fn(#registration_fns); )* } _register_types(); } From 5dd2e99c7c9f917788557fa84a96a0b5256cdf71 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Wed, 16 Jul 2025 13:02:16 +0000 Subject: [PATCH 58/61] update docs --- crates/bevy_reflect/derive/src/lib.rs | 3 ++- crates/bevy_reflect/src/lib.rs | 21 +++++++++------------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/crates/bevy_reflect/derive/src/lib.rs b/crates/bevy_reflect/derive/src/lib.rs index 52bc125175..c979c4db1f 100644 --- a/crates/bevy_reflect/derive/src/lib.rs +++ b/crates/bevy_reflect/derive/src/lib.rs @@ -853,7 +853,8 @@ pub fn impl_type_path(input: TokenStream) -> TokenStream { } /// Collects and loads type registrations when using `auto_register_static` feature. -/// The steps to using it correctly require the following: +/// +/// Correctly using this macro requires following: /// 1. This macro must be called **last** during compilation. This can be achieved by putting your main function /// in a separate crate or restructuring your project to be separated into `bin` and `lib`, and putting this macro in `bin`. /// Any automatic type registrations using `#[derive(Reflect)]` within the same crate as this macro are not guaranteed to run. diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 87b8e8777a..401faa297c 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -463,14 +463,6 @@ //! typically require manual monomorphization (i.e. manually specifying the types the generic method can //! take). //! -//! ## Manual Registration -//! -//! 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] by default -//! (see `auto_register` feature if this functionality is required). -//! This means types must manually be registered, including their desired monomorphized -//! representations if generic. -//! //! # Features //! //! ## `bevy` @@ -520,14 +512,19 @@ //! which enables capturing the type stack when serializing or deserializing a type //! and displaying it in error messages. //! -//! ## `auto_register` +//! ## `auto_register_inventory`/`auto_register_static` //! //! | Default | Dependencies | //! | :-----: | :-------------------------------: | -//! | ❌ | [`bevy_reflect_derive/auto_register`] | +//! | ✅ | [`bevy_reflect_derive/auto_register_inventory`] | +//! | ❌ | [`bevy_reflect_derive/auto_register_static`] | //! -//! This feature enables automatic registration of types that derive [`Reflect`] -//! for supported platforms (Linux, macOS, iOS, FreeBSD, Android, Windows, WebAssembly) using the `inventory` crate. +//! These features enable automatic registration of types that derive [`Reflect`]. +//! +//! - `auto_register_inventory` uses `inventory` to collect types on supported platforms (Linux, macOS, iOS, FreeBSD, Android, Windows, WebAssembly). +//! - `auto_register_static` uses platform-independent way to collect types, but requires additional setup and might +//! slow down compilation, so it should only be used on platforms not supported by `inventory`. +//! See documentation for [`load_type_registrations`] macro for more info //! //! When this feature is enabled `bevy_reflect` will automatically collects all types that derive [`Reflect`] on app startup, //! and [`TypeRegistry::register_derived_types`] can be used to register these types at any point in the program. From 655a2f81b1c3b47c13924c299e2ff672b78175c3 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Wed, 16 Jul 2025 21:59:54 +0000 Subject: [PATCH 59/61] use uuid again since `proc_macro::Span` isn't unique enough --- crates/bevy_reflect/derive/Cargo.toml | 5 ++++- crates/bevy_reflect/derive/src/impls/common.rs | 18 +----------------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/crates/bevy_reflect/derive/Cargo.toml b/crates/bevy_reflect/derive/Cargo.toml index 3a2e8ef008..fbe2a00bcd 100644 --- a/crates/bevy_reflect/derive/Cargo.toml +++ b/crates/bevy_reflect/derive/Cargo.toml @@ -23,7 +23,7 @@ auto_register = [] # Enables automatic reflection using inventory. Not supported on all platforms. auto_register_inventory = ["auto_register"] # Enables automatic reflection on platforms not supported by inventory. See `load_type_registrations` for more info. -auto_register_static = ["auto_register"] +auto_register_static = ["auto_register", "dep:uuid"] [dependencies] bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.17.0-dev" } @@ -31,6 +31,9 @@ indexmap = "2.0" proc-macro2 = "1.0" quote = "1.0" syn = { version = "2.0", features = ["full", "extra-traits"] } +uuid = { version = "1.13.1", default-features = false, features = [ + "v4", +], optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] # TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption. diff --git a/crates/bevy_reflect/derive/src/impls/common.rs b/crates/bevy_reflect/derive/src/impls/common.rs index 8824fdcb10..eed7e22455 100644 --- a/crates/bevy_reflect/derive/src/impls/common.rs +++ b/crates/bevy_reflect/derive/src/impls/common.rs @@ -169,11 +169,8 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option Option Date: Wed, 16 Jul 2025 22:11:02 +0000 Subject: [PATCH 60/61] fix feature gates --- crates/bevy_reflect/derive/src/impls/common.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/bevy_reflect/derive/src/impls/common.rs b/crates/bevy_reflect/derive/src/impls/common.rs index eed7e22455..c6507c6917 100644 --- a/crates/bevy_reflect/derive/src/impls/common.rs +++ b/crates/bevy_reflect/derive/src/impls/common.rs @@ -168,7 +168,8 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option Option::__register(registry); } }) - } else { + } + + #[cfg(all( + feature = "auto_register_inventory", + not(feature = "auto_register_static") + ))] + { Some(quote! { #bevy_reflect_path::__macro_exports::auto_register::inventory::submit!{ #bevy_reflect_path::__macro_exports::auto_register::AutomaticReflectRegistrations( From b9a5e45d09f3779eced852a12fe93dc56b613812 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Wed, 16 Jul 2025 22:16:22 +0000 Subject: [PATCH 61/61] fix(?) toml formatting by taplo --- crates/bevy_reflect/derive/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/derive/Cargo.toml b/crates/bevy_reflect/derive/Cargo.toml index fbe2a00bcd..7b56184881 100644 --- a/crates/bevy_reflect/derive/Cargo.toml +++ b/crates/bevy_reflect/derive/Cargo.toml @@ -32,7 +32,7 @@ proc-macro2 = "1.0" quote = "1.0" syn = { version = "2.0", features = ["full", "extra-traits"] } uuid = { version = "1.13.1", default-features = false, features = [ - "v4", + "v4", ], optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies]