put automatic type registration behind feature gate

This commit is contained in:
eugineerd 2024-12-01 22:19:02 +00:00
parent 2ae4bb964d
commit 7c7bd79444
18 changed files with 196 additions and 149 deletions

View File

@ -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"]

View File

@ -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

View File

@ -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::<AppTypeRegistry>();
#[cfg(feature = "reflect_auto_register")]
app.insert_resource(AppTypeRegistry::new_with_derived_types());
#[cfg(feature = "reflect_functions")]

View File

@ -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]

View File

@ -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();

View File

@ -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"]

View File

@ -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"

View File

@ -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" }

View File

@ -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
}

View File

@ -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<proc_macro2::TokenStream> {
use quote::ToTokens;
if meta.attrs().no_auto_register() {
return None;
}
@ -171,9 +174,9 @@ pub fn reflect_auto_registration(meta: &ReflectMeta) -> Option<proc_macro2::Toke
};
Some(quote! {
#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
#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
)
}
})

View File

@ -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::<proc_macro2::TokenStream>;
#[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);

View File

@ -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;

View File

@ -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::<proc_macro2::TokenStream>;
#[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);

View File

@ -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::<proc_macro2::TokenStream>;
#[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);

View File

@ -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::<proc_macro2::TokenStream>;
#[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);

View File

@ -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(<type_registration_function>)
/// }
///
/// // Registering collected types
/// let mut registry = TypeRegistry::default();
/// AutomaticReflectRegistrations::register(&mut registry);
/// ```
pub struct AutomaticReflectRegistrations;
/// Automatic reflect registration implementation
#[cfg(feature = "auto_register")]
pub mod auto_register {
pub use super::*;
#[cfg(not(target_family = "wasm"))]
mod __automatic_type_registration_impl {
use super::*;
/// Stores registration functions of all reflect types that can be automatically registered.
///
/// Intended to be used as follows:
/// ```rs
/// // Adding a type
/// auto_register_function!{
/// AutomaticReflectRegistrations::add(<type_registration_function>)
/// }
///
/// // Registering collected types
/// let mut registry = TypeRegistry::default();
/// AutomaticReflectRegistrations::register(&mut registry);
/// ```
pub struct AutomaticReflectRegistrations;
pub use inventory::submit as auto_register_function;
#[cfg(not(target_family = "wasm"))]
mod __automatic_type_registration_impl {
use super::*;
pub struct AutomaticReflectRegistrationsImpl(fn(&mut TypeRegistry));
pub use inventory::submit as auto_register_function;
impl AutomaticReflectRegistrations {
// Must be const to allow usage in static context
pub const fn add(func: fn(&mut TypeRegistry)) -> AutomaticReflectRegistrationsImpl {
AutomaticReflectRegistrationsImpl(func)
pub struct AutomaticReflectRegistrationsImpl(fn(&mut TypeRegistry));
impl AutomaticReflectRegistrations {
// Must be const to allow usage in static context
pub const fn add(func: fn(&mut TypeRegistry)) -> AutomaticReflectRegistrationsImpl {
AutomaticReflectRegistrationsImpl(func)
}
pub fn register(registry: &mut TypeRegistry) {
for registration_fn in inventory::iter::<AutomaticReflectRegistrationsImpl> {
registration_fn.0(registry);
}
}
}
pub fn register(registry: &mut TypeRegistry) {
for registration_fn in inventory::iter::<AutomaticReflectRegistrationsImpl> {
registration_fn.0(registry);
inventory::collect!(AutomaticReflectRegistrationsImpl);
}
#[cfg(target_family = "wasm")]
mod __automatic_type_registration_impl {
use super::*;
pub use wasm_init::wasm_init as auto_register_function;
static AUTOMATIC_REFLECT_REGISTRATIONS: std::sync::RwLock<Vec<fn(&mut TypeRegistry)>> =
std::sync::RwLock::new(Vec::new());
impl AutomaticReflectRegistrations {
pub fn add(func: fn(&mut TypeRegistry)) {
AUTOMATIC_REFLECT_REGISTRATIONS
.write()
.expect("Failed to get write lock for automatic reflect type registration")
.push(func);
}
pub fn register(registry: &mut TypeRegistry) {
// wasm_init must be called at least once to run all init code.
// Calling it multiple times is ok and doesn't do anything.
wasm_init::wasm_init();
for registration_fn in AUTOMATIC_REFLECT_REGISTRATIONS
.read()
.expect("Failed to get read lock for automatic reflect type registration")
.iter()
{
registration_fn(registry);
}
}
}
}
inventory::collect!(AutomaticReflectRegistrationsImpl);
pub use __automatic_type_registration_impl::*;
}
#[cfg(target_family = "wasm")]
mod __automatic_type_registration_impl {
use super::*;
pub use wasm_init::wasm_init as auto_register_function;
static AUTOMATIC_REFLECT_REGISTRATIONS: std::sync::RwLock<Vec<fn(&mut TypeRegistry)>> =
std::sync::RwLock::new(Vec::new());
impl AutomaticReflectRegistrations {
pub fn add(func: fn(&mut TypeRegistry)) {
AUTOMATIC_REFLECT_REGISTRATIONS
.write()
.expect("Failed to get write lock for automatic reflect type registration")
.push(func);
}
pub fn register(registry: &mut TypeRegistry) {
// wasm_init must be called at least once to run all init code.
// Calling it multiple times is ok and doesn't do anything.
wasm_init::wasm_init();
for registration_fn in AUTOMATIC_REFLECT_REGISTRATIONS
.read()
.expect("Failed to get read lock for automatic reflect type registration")
.iter()
{
registration_fn(registry);
}
}
}
}
pub use __automatic_type_registration_impl::*;
}
#[cfg(test)]
@ -3045,69 +3051,74 @@ bevy_reflect::tests::Test {
);
}
#[test]
fn should_ignore_auto_reflect_registration() {
#[derive(Reflect)]
#[reflect(no_auto_register)]
struct NoAutomaticStruct {
a: usize,
#[cfg(feature = "auto_register")]
mod auto_register_reflect {
use super::*;
#[test]
fn should_ignore_auto_reflect_registration() {
#[derive(Reflect)]
#[reflect(no_auto_register)]
struct NoAutomaticStruct {
a: usize,
}
let mut registry = TypeRegistry::default();
registry.register_derived_types();
assert!(!registry.contains(TypeId::of::<NoAutomaticStruct>()));
}
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::<NoAutomaticStruct>()));
}
// ZST struct
#[derive(Reflect)]
struct ZSTStructReflect;
#[test]
fn should_auto_register_reflect_for_all_supported_types() {
// Struct
#[derive(Reflect)]
struct StructReflect {
a: usize,
// Tuple struct
#[derive(Reflect)]
struct TupleStructReflect(pub u32);
// Enum
#[derive(Reflect)]
enum EnumReflect {
A,
B,
}
// ZST enum
#[derive(Reflect)]
enum ZSTEnumReflect {}
// Opaque struct
#[derive(Reflect, Clone)]
#[reflect(opaque)]
struct OpaqueStructReflect {
_a: usize,
}
// ZST opaque struct
#[derive(Reflect, Clone)]
#[reflect(opaque)]
struct ZSTOpaqueStructReflect;
let mut registry = TypeRegistry::default();
registry.register_derived_types();
assert!(registry.contains(TypeId::of::<StructReflect>()));
assert!(registry.contains(TypeId::of::<ZSTStructReflect>()));
assert!(registry.contains(TypeId::of::<TupleStructReflect>()));
assert!(registry.contains(TypeId::of::<EnumReflect>()));
assert!(registry.contains(TypeId::of::<ZSTEnumReflect>()));
assert!(registry.contains(TypeId::of::<OpaqueStructReflect>()));
assert!(registry.contains(TypeId::of::<ZSTOpaqueStructReflect>()));
}
// 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::<StructReflect>()));
assert!(registry.contains(TypeId::of::<ZSTStructReflect>()));
assert!(registry.contains(TypeId::of::<TupleStructReflect>()));
assert!(registry.contains(TypeId::of::<EnumReflect>()));
assert!(registry.contains(TypeId::of::<ZSTEnumReflect>()));
assert!(registry.contains(TypeId::of::<OpaqueStructReflect>()));
assert!(registry.contains(TypeId::of::<ZSTOpaqueStructReflect>()));
}
#[cfg(feature = "glam")]

View File

@ -150,8 +150,9 @@ impl TypeRegistry {
/// // Its type data
/// assert!(type_registry.get_type_data::<ReflectDefault>(TypeId::of::<Foo>()).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.

View File

@ -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`.|