From 0441a42d5602eb5856a273a66a8661204ad9b841 Mon Sep 17 00:00:00 2001 From: Hennadii Chernyshchyk Date: Sat, 2 Jul 2022 18:59:56 +0300 Subject: [PATCH 1/5] Add reflection for resources --- crates/bevy_core_pipeline/src/clear_color.rs | 3 +- crates/bevy_core_pipeline/src/lib.rs | 3 +- crates/bevy_ecs/src/change_detection.rs | 2 +- crates/bevy_ecs/src/lib.rs | 2 +- crates/bevy_ecs/src/reflect.rs | 85 ++++++++++++++++++++ crates/bevy_pbr/src/lib.rs | 3 + crates/bevy_pbr/src/light.rs | 9 ++- crates/bevy_pbr/src/wireframe.rs | 6 +- crates/bevy_render/src/view/mod.rs | 7 +- 9 files changed, 109 insertions(+), 11 deletions(-) diff --git a/crates/bevy_core_pipeline/src/clear_color.rs b/crates/bevy_core_pipeline/src/clear_color.rs index 1c8b6f1035400..6160f2256f039 100644 --- a/crates/bevy_core_pipeline/src/clear_color.rs +++ b/crates/bevy_core_pipeline/src/clear_color.rs @@ -17,7 +17,8 @@ pub enum ClearColorConfig { /// /// This color appears as the "background" color for simple apps, when /// there are portions of the screen with nothing rendered. -#[derive(Component, Clone, Debug, Deref, DerefMut, ExtractResource)] +#[derive(Component, Clone, Debug, Deref, DerefMut, ExtractResource, Reflect)] +#[reflect(Resource)] pub struct ClearColor(pub Color); impl Default for ClearColor { diff --git a/crates/bevy_core_pipeline/src/lib.rs b/crates/bevy_core_pipeline/src/lib.rs index f8f5db70a3d8c..28304608e7661 100644 --- a/crates/bevy_core_pipeline/src/lib.rs +++ b/crates/bevy_core_pipeline/src/lib.rs @@ -20,7 +20,8 @@ pub struct CorePipelinePlugin; impl Plugin for CorePipelinePlugin { fn build(&self, app: &mut App) { - app.init_resource::() + app.register_type::() + .init_resource::() .add_plugin(ExtractResourcePlugin::::default()) .add_plugin(Core2dPlugin) .add_plugin(Core3dPlugin); diff --git a/crates/bevy_ecs/src/change_detection.rs b/crates/bevy_ecs/src/change_detection.rs index 2b5ae2bd2f5cb..b96f4e48c16c9 100644 --- a/crates/bevy_ecs/src/change_detection.rs +++ b/crates/bevy_ecs/src/change_detection.rs @@ -216,7 +216,7 @@ change_detection_impl!(Mut<'a, T>, T,); impl_into_inner!(Mut<'a, T>, T,); impl_debug!(Mut<'a, T>,); -/// Unique mutable borrow of a Reflected component +/// Unique mutable borrow of a reflected component or resource #[cfg(feature = "bevy_reflect")] pub struct ReflectMut<'a> { pub(crate) value: &'a mut dyn Reflect, diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 30a373a9d5ae0..f91ff0a6c80be 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -23,7 +23,7 @@ pub use bevy_ptr as ptr; pub mod prelude { #[doc(hidden)] #[cfg(feature = "bevy_reflect")] - pub use crate::reflect::ReflectComponent; + pub use crate::reflect::{ReflectComponent, ReflectResource}; #[doc(hidden)] pub use crate::{ bundle::Bundle, diff --git a/crates/bevy_ecs/src/reflect.rs b/crates/bevy_ecs/src/reflect.rs index bcd41971c7c9e..9f98bd11502c7 100644 --- a/crates/bevy_ecs/src/reflect.rs +++ b/crates/bevy_ecs/src/reflect.rs @@ -4,6 +4,7 @@ pub use crate::change_detection::ReflectMut; use crate::{ component::Component, entity::{Entity, EntityMap, MapEntities, MapEntitiesError}, + system::Resource, world::{FromWorld, World}, }; use bevy_reflect::{ @@ -123,6 +124,90 @@ impl FromType for ReflectComponent { } } +#[derive(Clone)] +pub struct ReflectResource { + insert_resource: fn(&mut World, &dyn Reflect), + apply_resource: fn(&mut World, &dyn Reflect), + remove_resource: fn(&mut World), + reflect_resource: fn(&World) -> Option<&dyn Reflect>, + reflect_resource_unchecked_mut: unsafe fn(&World) -> Option, + copy_resource: fn(&World, &mut World), +} + +impl ReflectResource { + pub fn insert_resource(&self, world: &mut World, resource: &dyn Reflect) { + (self.insert_resource)(world, resource); + } + + pub fn apply_resource(&self, world: &mut World, resource: &dyn Reflect) { + (self.apply_resource)(world, resource); + } + + pub fn remove_resource(&self, world: &mut World) { + (self.remove_resource)(world); + } + + pub fn reflect_resource<'a>(&self, world: &'a World) -> Option<&'a dyn Reflect> { + (self.reflect_resource)(world) + } + + pub fn reflect_resource_mut<'a>(&self, world: &'a mut World) -> Option> { + // SAFE: unique world access + unsafe { (self.reflect_resource_unchecked_mut)(world) } + } + + /// # Safety + /// This method does not prevent you from having two mutable pointers to the same data, + /// violating Rust's aliasing rules. To avoid this: + /// * Only call this method in an exclusive system to avoid sharing across threads (or use a + /// scheduler that enforces safe memory access). + /// * Don't call this method more than once in the same scope for a given resource. + pub unsafe fn reflect_resource_unckecked_mut<'a>( + &self, + world: &'a World, + ) -> Option> { + (self.reflect_resource_unchecked_mut)(world) + } + + pub fn copy_resource(&self, source_world: &World, destination_world: &mut World) { + (self.copy_resource)(source_world, destination_world); + } +} + +impl FromType for ReflectResource { + fn from_type() -> Self { + ReflectResource { + insert_resource: |world, reflected_resource| { + let mut resource = C::from_world(world); + resource.apply(reflected_resource); + world.insert_resource(resource); + }, + apply_resource: |world, reflected_resource| { + let mut resource = world.get_resource_mut::().unwrap(); + resource.apply(reflected_resource); + }, + remove_resource: |world| { + world.remove_resource::(); + }, + reflect_resource: |world| world.get_resource::().map(|res| res as &dyn Reflect), + reflect_resource_unchecked_mut: |world| unsafe { + world + .get_resource_unchecked_mut::() + .map(|res| ReflectMut { + value: res.value as &mut dyn Reflect, + ticks: res.ticks, + }) + }, + copy_resource: |source_world, destination_world| { + let source_resource = source_world.get_resource::().unwrap(); + let mut destination_resource = C::from_world(destination_world); + destination_resource.apply(source_resource); + destination_world.insert_resource(destination_resource); + }, + } + } +} + impl_reflect_value!(Entity(Hash, PartialEq, Serialize, Deserialize)); impl_from_reflect_value!(Entity); diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index e3ac3914512cb..35c2cc9a6a428 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -125,6 +125,9 @@ impl Plugin for PbrPlugin { .register_type::() .add_plugin(MeshRenderPlugin) .add_plugin(MaterialPlugin::::default()) + .register_type::() + .register_type::() + .register_type::() .init_resource::() .init_resource::() .init_resource::() diff --git a/crates/bevy_pbr/src/light.rs b/crates/bevy_pbr/src/light.rs index 575c926b4ce40..8c1e46ba127d3 100644 --- a/crates/bevy_pbr/src/light.rs +++ b/crates/bevy_pbr/src/light.rs @@ -75,7 +75,8 @@ impl PointLight { pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 0.6; } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Reflect)] +#[reflect(Resource)] pub struct PointLightShadowMap { pub size: usize, } @@ -153,7 +154,8 @@ impl DirectionalLight { pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 0.6; } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Reflect)] +#[reflect(Resource)] pub struct DirectionalLightShadowMap { pub size: usize, } @@ -168,7 +170,8 @@ impl Default for DirectionalLightShadowMap { } /// An ambient light, which lights the entire scene equally. -#[derive(Clone, Debug, ExtractResource)] +#[derive(Clone, Debug, ExtractResource, Reflect)] +#[reflect(Resource)] pub struct AmbientLight { pub color: Color, /// A direct scale factor multiplied with `color` before being passed to the shader. diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index 6a7a4c3c297c2..c16e396f8ea6f 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -35,7 +35,8 @@ impl Plugin for WireframePlugin { Shader::from_wgsl ); - app.init_resource::() + app.register_type::() + .init_resource::() .add_plugin(ExtractResourcePlugin::::default()); if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { @@ -60,7 +61,8 @@ fn extract_wireframes(mut commands: Commands, query: Query() + app.register_type::() + .init_resource::() // NOTE: windows.is_changed() handles cases where a window was resized .add_plugin(ExtractResourcePlugin::::default()) .add_plugin(VisibilityPlugin); @@ -45,7 +47,6 @@ impl Plugin for ViewPlugin { } } -#[derive(Clone, ExtractResource)] /// Configuration resource for [Multi-Sample Anti-Aliasing](https://en.wikipedia.org/wiki/Multisample_anti-aliasing). /// /// # Example @@ -56,6 +57,8 @@ impl Plugin for ViewPlugin { /// .insert_resource(Msaa { samples: 4 }) /// .run(); /// ``` +#[derive(Clone, ExtractResource, Reflect)] +#[reflect(Resource)] pub struct Msaa { /// The number of samples to run for Multi-Sample Anti-Aliasing. Higher numbers result in /// smoother edges. From 1704a265f9a4f5a2b1e412698379b785520bb092 Mon Sep 17 00:00:00 2001 From: Hennadii Chernyshchyk Date: Sat, 2 Jul 2022 20:59:31 +0300 Subject: [PATCH 2/5] Use sugar from Bevy 0.8 --- crates/bevy_ecs/src/reflect.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/reflect.rs b/crates/bevy_ecs/src/reflect.rs index 9f98bd11502c7..4604b70f2669d 100644 --- a/crates/bevy_ecs/src/reflect.rs +++ b/crates/bevy_ecs/src/reflect.rs @@ -183,7 +183,7 @@ impl FromType for ReflectResource { world.insert_resource(resource); }, apply_resource: |world, reflected_resource| { - let mut resource = world.get_resource_mut::().unwrap(); + let mut resource = world.resource_mut::(); resource.apply(reflected_resource); }, remove_resource: |world| { @@ -199,7 +199,7 @@ impl FromType for ReflectResource { }) }, copy_resource: |source_world, destination_world| { - let source_resource = source_world.get_resource::().unwrap(); + let source_resource = source_world.resource::(); let mut destination_resource = C::from_world(destination_world); destination_resource.apply(source_resource); destination_world.insert_resource(destination_resource); From ceee28385fcc93554aa76e2d9f77a5e978b5427c Mon Sep 17 00:00:00 2001 From: Hennadii Chernyshchyk Date: Sat, 2 Jul 2022 21:02:24 +0300 Subject: [PATCH 3/5] Extend documentation --- crates/bevy_ecs/src/reflect.rs | 40 ++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/reflect.rs b/crates/bevy_ecs/src/reflect.rs index 4604b70f2669d..78cfac3a96828 100644 --- a/crates/bevy_ecs/src/reflect.rs +++ b/crates/bevy_ecs/src/reflect.rs @@ -12,6 +12,10 @@ use bevy_reflect::{ ReflectSerialize, }; +/// A struct used to operate on reflected [`Component`] of a type. +/// +/// A [`ReflectComponent`] for type `T` can be obtained via +/// [`bevy_reflect::TypeRegistration::data`]. #[derive(Clone)] pub struct ReflectComponent { add_component: fn(&mut World, Entity, &dyn Reflect), @@ -23,18 +27,26 @@ pub struct ReflectComponent { } impl ReflectComponent { + /// Insert a reflected [`Component`] into the entity like [`insert()`](crate::world::EntityMut::insert). pub fn add_component(&self, world: &mut World, entity: Entity, component: &dyn Reflect) { (self.add_component)(world, entity, component); } + /// Uses reflection to set the value of this [`Component`] type in the entity to the given value. + /// + /// # Panics + /// + /// Panics if there is no [`Component`] of the given type. pub fn apply_component(&self, world: &mut World, entity: Entity, component: &dyn Reflect) { (self.apply_component)(world, entity, component); } + /// Removes this [`Component`] type from the entity. pub fn remove_component(&self, world: &mut World, entity: Entity) { (self.remove_component)(world, entity); } + /// Gets the value of this [`Component`] type from the entity as a reflected reference. pub fn reflect_component<'a>( &self, world: &'a World, @@ -43,6 +55,7 @@ impl ReflectComponent { (self.reflect_component)(world, entity) } + /// Gets the value of this [`Component`] type from the entity as a mutable reflected reference. pub fn reflect_component_mut<'a>( &self, world: &'a mut World, @@ -57,7 +70,7 @@ impl ReflectComponent { /// violating Rust's aliasing rules. To avoid this: /// * Only call this method in an exclusive system to avoid sharing across threads (or use a /// scheduler that enforces safe memory access). - /// * Don't call this method more than once in the same scope for a given component. + /// * Don't call this method more than once in the same scope for a given [`Component`]. pub unsafe fn reflect_component_unchecked_mut<'a>( &self, world: &'a World, @@ -66,6 +79,11 @@ impl ReflectComponent { (self.reflect_component_mut)(world, entity) } + /// Gets the value of this [`Component`] type from entity from `source_world` and [applies](Self::apply_component()) it to the value of this [`Component`] type in entity in `destination_world`. + /// + /// # Panics + /// + /// Panics if there is no [`Component`] of the given type. pub fn copy_component( &self, source_world: &World, @@ -124,6 +142,10 @@ impl FromType for ReflectComponent { } } +/// A struct used to operate on reflected [`Resource`] of a type. +/// +/// A [`ReflectResource`] for type `T` can be obtained via +/// [`bevy_reflect::TypeRegistration::data`]. #[derive(Clone)] pub struct ReflectResource { insert_resource: fn(&mut World, &dyn Reflect), @@ -135,22 +157,31 @@ pub struct ReflectResource { } impl ReflectResource { + /// Insert a reflected [`Resource`] into the world like [`insert_resource()`](World::insert_resource). pub fn insert_resource(&self, world: &mut World, resource: &dyn Reflect) { (self.insert_resource)(world, resource); } + /// Uses reflection to set the value of this [`Resource`] type in the world to the given value. + /// + /// # Panics + /// + /// Panics if there is no [`Resource`] of the given type. pub fn apply_resource(&self, world: &mut World, resource: &dyn Reflect) { (self.apply_resource)(world, resource); } + /// Removes this [`Resource`] type from the world. pub fn remove_resource(&self, world: &mut World) { (self.remove_resource)(world); } + /// Gets the value of this [`Resource`] type from the world as a reflected reference. pub fn reflect_resource<'a>(&self, world: &'a World) -> Option<&'a dyn Reflect> { (self.reflect_resource)(world) } + /// Gets the value of this [`Resource`] type from the world as a mutable reflected reference. pub fn reflect_resource_mut<'a>(&self, world: &'a mut World) -> Option> { // SAFE: unique world access unsafe { (self.reflect_resource_unchecked_mut)(world) } @@ -161,7 +192,7 @@ impl ReflectResource { /// violating Rust's aliasing rules. To avoid this: /// * Only call this method in an exclusive system to avoid sharing across threads (or use a /// scheduler that enforces safe memory access). - /// * Don't call this method more than once in the same scope for a given resource. + /// * Don't call this method more than once in the same scope for a given [`Resource`]. pub unsafe fn reflect_resource_unckecked_mut<'a>( &self, world: &'a World, @@ -169,6 +200,11 @@ impl ReflectResource { (self.reflect_resource_unchecked_mut)(world) } + /// Gets the value of this [`Resource`] type from `source_world` and [applies](Self::apply_resource()) it to the value of this [`Resource`] type in `destination_world`. + /// + /// # Panics + /// + /// Panics if there is no [`Resource`] of the given type. pub fn copy_resource(&self, source_world: &World, destination_world: &mut World) { (self.copy_resource)(source_world, destination_world); } From c9827e104c32433310577d884e1647ae1a307af5 Mon Sep 17 00:00:00 2001 From: Hennadii Chernyshchyk Date: Sat, 2 Jul 2022 21:41:43 +0300 Subject: [PATCH 4/5] Update information about edge cases --- crates/bevy_ecs/src/reflect.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/reflect.rs b/crates/bevy_ecs/src/reflect.rs index 78cfac3a96828..cd08a1e5f2872 100644 --- a/crates/bevy_ecs/src/reflect.rs +++ b/crates/bevy_ecs/src/reflect.rs @@ -28,6 +28,10 @@ pub struct ReflectComponent { impl ReflectComponent { /// Insert a reflected [`Component`] into the entity like [`insert()`](crate::world::EntityMut::insert). + /// + /// # Panics + /// + /// Panics if there is no such entity. pub fn add_component(&self, world: &mut World, entity: Entity, component: &dyn Reflect) { (self.add_component)(world, entity, component); } @@ -36,12 +40,16 @@ impl ReflectComponent { /// /// # Panics /// - /// Panics if there is no [`Component`] of the given type. + /// Panics if there is no [`Component`] of the given type or such entity. pub fn apply_component(&self, world: &mut World, entity: Entity, component: &dyn Reflect) { (self.apply_component)(world, entity, component); } - /// Removes this [`Component`] type from the entity. + /// Removes this [`Component`] type from the entity. Does nothing if it doesn't exist. + /// + /// # Panics + /// + /// Panics if there is no such entity pub fn remove_component(&self, world: &mut World, entity: Entity) { (self.remove_component)(world, entity); } @@ -83,7 +91,7 @@ impl ReflectComponent { /// /// # Panics /// - /// Panics if there is no [`Component`] of the given type. + /// Panics if there is no [`Component`] of the given type or such entities. pub fn copy_component( &self, source_world: &World, @@ -171,7 +179,7 @@ impl ReflectResource { (self.apply_resource)(world, resource); } - /// Removes this [`Resource`] type from the world. + /// Removes this [`Resource`] type from the world. Does nothing if it doesn't exist. pub fn remove_resource(&self, world: &mut World) { (self.remove_resource)(world); } From 6230616b1e300d252497f54d2c77a964c1a23291 Mon Sep 17 00:00:00 2001 From: Hennadii Chernyshchyk Date: Sat, 2 Jul 2022 22:24:29 +0300 Subject: [PATCH 5/5] Apply suggestions from code review Co-authored-by: Alice Cecile --- crates/bevy_ecs/src/reflect.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/reflect.rs b/crates/bevy_ecs/src/reflect.rs index cd08a1e5f2872..46871b59a1906 100644 --- a/crates/bevy_ecs/src/reflect.rs +++ b/crates/bevy_ecs/src/reflect.rs @@ -40,7 +40,7 @@ impl ReflectComponent { /// /// # Panics /// - /// Panics if there is no [`Component`] of the given type or such entity. + /// Panics if there is no [`Component`] of the given type or the `entity` does not exist. pub fn apply_component(&self, world: &mut World, entity: Entity, component: &dyn Reflect) { (self.apply_component)(world, entity, component); } @@ -49,7 +49,7 @@ impl ReflectComponent { /// /// # Panics /// - /// Panics if there is no such entity + /// Panics if there is no [`Component`] of the given type or the `entity` does not exist. pub fn remove_component(&self, world: &mut World, entity: Entity) { (self.remove_component)(world, entity); } @@ -91,7 +91,7 @@ impl ReflectComponent { /// /// # Panics /// - /// Panics if there is no [`Component`] of the given type or such entities. + /// Panics if there is no [`Component`] of the given type or either entity does not exist. pub fn copy_component( &self, source_world: &World,