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