From 7757c4db4912ba359b77d8a754d3709ce8ffeb38 Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Fri, 7 Jan 2022 17:52:53 +0100 Subject: [PATCH 01/11] Reflect and (de)serialize resources in scenes --- assets/scenes/load_scene_example.scn.ron | 115 +++++++++--------- crates/bevy_ecs/src/change_detection.rs | 2 +- crates/bevy_ecs/src/lib.rs | 2 +- crates/bevy_ecs/src/reflect.rs | 77 ++++++++++++ crates/bevy_ecs/src/world/mod.rs | 19 +-- crates/bevy_scene/src/dynamic_scene.rs | 51 +++++++- crates/bevy_scene/src/scene_spawner.rs | 47 +++++++- crates/bevy_scene/src/serde.rs | 144 +++++++++++++++++++++-- 8 files changed, 371 insertions(+), 86 deletions(-) diff --git a/assets/scenes/load_scene_example.scn.ron b/assets/scenes/load_scene_example.scn.ron index 1f9de9dae7da8..59a51c1e9fe8f 100644 --- a/assets/scenes/load_scene_example.scn.ron +++ b/assets/scenes/load_scene_example.scn.ron @@ -1,64 +1,67 @@ -[ - ( - entity: 0, - components: [ - { - "type": "bevy_transform::components::transform::Transform", - "struct": { - "translation": { - "type": "glam::vec3::Vec3", - "value": (0.0, 0.0, 0.0), - }, - "rotation": { - "type": "glam::quat::Quat", - "value": (0.0, 0.0, 0.0, 1.0), - }, - "scale": { - "type": "glam::vec3::Vec3", - "value": (1.0, 1.0, 1.0), +( + resources: [], + entities: [ + ( + entity: 0, + components: [ + { + "type": "bevy_transform::components::transform::Transform", + "struct": { + "translation": { + "type": "glam::vec3::Vec3", + "value": (0.0, 0.0, 0.0), + }, + "rotation": { + "type": "glam::quat::Quat", + "value": (0.0, 0.0, 0.0, 1.0), + }, + "scale": { + "type": "glam::vec3::Vec3", + "value": (1.0, 1.0, 1.0), + }, }, }, - }, - { - "type": "scene::ComponentB", - "struct": { - "value": { - "type": "alloc::string::String", - "value": "hello", + { + "type": "scene::ComponentB", + "struct": { + "value": { + "type": "alloc::string::String", + "value": "hello", + }, }, }, - }, - { - "type": "scene::ComponentA", - "struct": { - "x": { - "type": "f32", - "value": 1.0, - }, - "y": { - "type": "f32", - "value": 2.0, + { + "type": "scene::ComponentA", + "struct": { + "x": { + "type": "f32", + "value": 1.0, + }, + "y": { + "type": "f32", + "value": 2.0, + }, }, }, - }, - ], - ), - ( - entity: 1, - components: [ - { - "type": "scene::ComponentA", - "struct": { - "x": { - "type": "f32", - "value": 3.0, - }, - "y": { - "type": "f32", - "value": 4.0, + ], + ), + ( + entity: 1, + components: [ + { + "type": "scene::ComponentA", + "struct": { + "x": { + "type": "f32", + "value": 3.0, + }, + "y": { + "type": "f32", + "value": 4.0, + }, }, }, - }, - ], - ), -] + ], + ), + ], +) \ No newline at end of file diff --git a/crates/bevy_ecs/src/change_detection.rs b/crates/bevy_ecs/src/change_detection.rs index d7dd0067fe75e..027a13fae89a1 100644 --- a/crates/bevy_ecs/src/change_detection.rs +++ b/crates/bevy_ecs/src/change_detection.rs @@ -188,7 +188,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 30d70ca17efab..5c3e8cd5b742f 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -18,7 +18,7 @@ pub mod world; 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 4881dd83f0edd..cd903e973bc8e 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::{ @@ -122,6 +123,82 @@ 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_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) + } + + /// # 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_mut<'a>(&self, world: &'a World) -> Option> { + (self.reflect_resource_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_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_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 739a1d9d30f31..e8b4783377442 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -644,16 +644,21 @@ impl World { Some(unsafe { ptr.cast::().read() }) } + /// Returns `true` if a resource of a given [`TypeId`] exists. Otherwise returns `false`. + #[inline] + pub fn contains_resource_type_id(&self, type_id: TypeId) -> bool { + let component_id = if let Some(component_id) = self.components.get_resource_id(type_id) { + component_id + } else { + return false; + }; + self.get_populated_resource_column(component_id).is_some() + } + /// Returns `true` if a resource of type `T` exists. Otherwise returns `false`. #[inline] pub fn contains_resource(&self) -> bool { - let component_id = - if let Some(component_id) = self.components.get_resource_id(TypeId::of::()) { - component_id - } else { - return false; - }; - self.get_populated_resource_column(component_id).is_some() + self.contains_resource_type_id(TypeId::of::()) } /// Gets a reference to the resource of the given type, if it exists. Otherwise returns [None] diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index ab15cbd61dbdd..3f8d2a2bccf46 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -2,7 +2,7 @@ use crate::{serde::SceneSerializer, Scene, SceneSpawnError}; use anyhow::Result; use bevy_ecs::{ entity::EntityMap, - reflect::{ReflectComponent, ReflectMapEntities}, + reflect::{ReflectComponent, ReflectMapEntities, ReflectResource}, world::World, }; use bevy_reflect::{Reflect, TypeRegistryArc, TypeUuid}; @@ -12,6 +12,7 @@ use serde::Serialize; #[derive(Default, TypeUuid)] #[uuid = "749479b1-fb8c-4ff8-a775-623aa76014f5"] pub struct DynamicScene { + pub resources: Vec>, pub entities: Vec, } @@ -35,7 +36,28 @@ impl DynamicScene { let mut scene = DynamicScene::default(); let type_registry = type_registry.read(); - for archetype in world.archetypes().iter() { + let mut archetypes = world.archetypes().iter(); + + // Empty archetype + let _ = archetypes.next().unwrap(); + + // Resources archetype + let resources_archetype = archetypes.next().unwrap(); + for component_id in resources_archetype.components() { + let reflect_resource = world + .components() + .get_info(component_id) + .and_then(|info| type_registry.get(info.type_id().unwrap())) + .and_then(|registration| registration.data::()); + if let Some(reflect_resource) = reflect_resource { + if let Some(resource) = reflect_resource.reflect_resource(world) { + scene.resources.push(resource.clone_value()); + } + } + } + + // Other archetypes + for archetype in archetypes { let entities_offset = scene.entities.len(); // Create a new dynamic entity for each entity of the given archetype @@ -78,9 +100,30 @@ impl DynamicScene { &self, world: &mut World, entity_map: &mut EntityMap, + type_registry: &TypeRegistryArc, ) -> Result<(), SceneSpawnError> { - let registry = world.get_resource::().unwrap().clone(); - let type_registry = registry.read(); + let type_registry = type_registry.read(); + + for resource in self.resources.iter() { + let registration = type_registry + .get_with_name(resource.type_name()) + .ok_or_else(|| SceneSpawnError::UnregisteredType { + type_name: resource.type_name().to_string(), + })?; + let reflect_resource = registration.data::().ok_or_else(|| { + SceneSpawnError::UnregisteredResource { + type_name: resource.type_name().to_string(), + } + })?; + + // If the entity already has the given resource attached, + // just apply the (possibly) new value, otherwise insert the resource + if world.contains_resource_type_id(registration.type_id()) { + reflect_resource.apply_resource(world, &**resource); + } else { + reflect_resource.insert_resource(world, &**resource); + } + } for scene_entity in self.entities.iter() { // Fetch the entity with the given entity id from the `entity_map` diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index 7015195757d79..dee51f460fe42 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -3,13 +3,16 @@ use bevy_app::{Events, ManualEventReader}; use bevy_asset::{AssetEvent, Assets, Handle}; use bevy_ecs::{ entity::{Entity, EntityMap}, - reflect::{ReflectComponent, ReflectMapEntities}, + reflect::{ReflectComponent, ReflectMapEntities, ReflectResource}, system::Command, world::{Mut, World}, }; use bevy_reflect::TypeRegistryArc; use bevy_transform::{hierarchy::AddChild, prelude::Parent}; -use bevy_utils::{tracing::error, HashMap}; +use bevy_utils::{ + tracing::{debug, error}, + HashMap, +}; use thiserror::Error; use uuid::Uuid; @@ -43,6 +46,8 @@ pub struct SceneSpawner { pub enum SceneSpawnError { #[error("scene contains the unregistered component `{type_name}`. consider adding `#[reflect(Component)]` to your type")] UnregisteredComponent { type_name: String }, + #[error("scene contains the unregistered resource `{type_name}`. consider adding `#[reflect(Resource)]` to your type")] + UnregisteredResource { type_name: String }, #[error("scene contains the unregistered type `{type_name}`. consider registering the type using `app.register_type::()`")] UnregisteredType { type_name: String }, #[error("scene does not exist")] @@ -123,7 +128,9 @@ impl SceneSpawner { .ok_or_else(|| SceneSpawnError::NonExistentScene { handle: scene_handle.clone_weak(), })?; - scene.write_to_world(world, entity_map) + world.resource_scope(|world, type_registry: Mut| { + scene.write_to_world(world, entity_map, &type_registry) + }) }) } @@ -154,7 +161,39 @@ impl SceneSpawner { handle: scene_handle.clone(), })?; - for archetype in scene.world.archetypes().iter() { + let mut archetypes = scene.world.archetypes().iter(); + + // Empty archetype + let _ = archetypes.next().unwrap(); + + // Resources archetype + let resources_archetype = archetypes.next().unwrap(); + for component_id in resources_archetype.components() { + let component_info = scene + .world + .components() + .get_info(component_id) + .expect("component_ids in archetypes should have ComponentInfo"); + + // Resources that are not registered are ignored + if let Some(registration) = type_registry.get(component_info.type_id().unwrap()) { + let reflect_resource = + registration.data::().ok_or_else(|| { + SceneSpawnError::UnregisteredResource { + type_name: component_info.name().to_string(), + } + })?; + reflect_resource.copy_resource(&scene.world, world); + } else { + debug!( + "Ignored unregistered resource when loading scene: {}", + component_info.name() + ); + } + } + + // Other archetypes + for archetype in archetypes { for scene_entity in archetype.entities() { let entity = *instance_info .entity_map diff --git a/crates/bevy_scene/src/serde.rs b/crates/bevy_scene/src/serde.rs index 38af61851033f..defbd60ec314d 100644 --- a/crates/bevy_scene/src/serde.rs +++ b/crates/bevy_scene/src/serde.rs @@ -26,8 +26,43 @@ impl<'a> Serialize for SceneSerializer<'a> { where S: serde::Serializer, { - let mut state = serializer.serialize_seq(Some(self.scene.entities.len()))?; - for entity in self.scene.entities.iter() { + let mut state = serializer.serialize_struct(SCENE_STRUCT, 2)?; + state.serialize_field( + SCENE_FIELD_RESOURCES, + &ComponentsSerializer { + components: &self.scene.resources, + registry: self.registry, + }, + )?; + state.serialize_field( + SCENE_FIELD_ENTITIES, + &EntitiesSerializer { + entities: &self.scene.entities, + registry: self.registry, + }, + )?; + state.end() + } +} + +pub struct EntitiesSerializer<'a> { + pub entities: &'a [DynamicEntity], + pub registry: &'a TypeRegistryArc, +} + +impl<'a> EntitiesSerializer<'a> { + pub fn new(entities: &'a [DynamicEntity], registry: &'a TypeRegistryArc) -> Self { + EntitiesSerializer { entities, registry } + } +} + +impl<'a> Serialize for EntitiesSerializer<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_seq(Some(self.entities.len()))?; + for entity in self.entities.iter() { state.serialize_element(&EntitySerializer { entity, registry: self.registry, @@ -92,19 +127,102 @@ impl<'a, 'de> DeserializeSeed<'de> for SceneDeserializer<'a> { where D: serde::Deserializer<'de>, { - Ok(DynamicScene { - entities: deserializer.deserialize_seq(SceneEntitySeqVisitor { + deserializer.deserialize_struct( + SCENE_STRUCT, + &[SCENE_FIELD_RESOURCES, SCENE_FIELD_ENTITIES], + SceneVisitor { type_registry: self.type_registry, - })?, + }, + ) + } +} + +#[derive(Deserialize)] +#[serde(field_identifier, rename_all = "lowercase")] +enum SceneField { + Resources, + Entities, +} + +pub const SCENE_STRUCT: &str = "Scene"; +pub const SCENE_FIELD_RESOURCES: &str = "resources"; +pub const SCENE_FIELD_ENTITIES: &str = "entities"; + +struct SceneVisitor<'a> { + pub type_registry: &'a TypeRegistry, +} + +impl<'a, 'de> Visitor<'de> for SceneVisitor<'a> { + type Value = DynamicScene; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("scene") + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let mut resources = None; + let mut entities = None; + while let Some(key) = map.next_key()? { + match key { + SceneField::Resources => { + if resources.is_some() { + return Err(Error::duplicate_field(SCENE_FIELD_RESOURCES)); + } + resources = Some(map.next_value_seed(ComponentVecDeserializer { + registry: self.type_registry, + })?); + } + SceneField::Entities => { + if entities.is_some() { + return Err(Error::duplicate_field(SCENE_FIELD_ENTITIES)); + } + + entities = Some(map.next_value_seed(EntityVecDeserializer { + type_registry: self.type_registry, + })?); + } + } + } + + let resources = resources + .take() + .ok_or_else(|| Error::missing_field(ENTITY_FIELD_ENTITY))?; + + let entities = entities + .take() + .ok_or_else(|| Error::missing_field(ENTITY_FIELD_COMPONENTS))?; + Ok(DynamicScene { + resources, + entities, + }) + } +} + +pub struct EntityVecDeserializer<'a> { + pub type_registry: &'a TypeRegistry, +} + +impl<'a, 'de> DeserializeSeed<'de> for EntityVecDeserializer<'a> { + type Value = Vec; + + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_seq(EntitySeqVisitor { + type_registry: self.type_registry, }) } } -struct SceneEntitySeqVisitor<'a> { +struct EntitySeqVisitor<'a> { pub type_registry: &'a TypeRegistry, } -impl<'a, 'de> Visitor<'de> for SceneEntitySeqVisitor<'a> { +impl<'a, 'de> Visitor<'de> for EntitySeqVisitor<'a> { type Value = Vec; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -116,7 +234,7 @@ impl<'a, 'de> Visitor<'de> for SceneEntitySeqVisitor<'a> { A: SeqAccess<'de>, { let mut entities = Vec::new(); - while let Some(entity) = seq.next_element_seed(SceneEntityDeserializer { + while let Some(entity) = seq.next_element_seed(EntityDeserializer { type_registry: self.type_registry, })? { entities.push(entity); @@ -126,11 +244,11 @@ impl<'a, 'de> Visitor<'de> for SceneEntitySeqVisitor<'a> { } } -pub struct SceneEntityDeserializer<'a> { +pub struct EntityDeserializer<'a> { pub type_registry: &'a TypeRegistry, } -impl<'a, 'de> DeserializeSeed<'de> for SceneEntityDeserializer<'a> { +impl<'a, 'de> DeserializeSeed<'de> for EntityDeserializer<'a> { type Value = DynamicEntity; fn deserialize(self, deserializer: D) -> Result @@ -140,7 +258,7 @@ impl<'a, 'de> DeserializeSeed<'de> for SceneEntityDeserializer<'a> { deserializer.deserialize_struct( ENTITY_STRUCT, &[ENTITY_FIELD_ENTITY, ENTITY_FIELD_COMPONENTS], - SceneEntityVisitor { + EntityVisitor { registry: self.type_registry, }, ) @@ -158,11 +276,11 @@ pub const ENTITY_STRUCT: &str = "Entity"; pub const ENTITY_FIELD_ENTITY: &str = "entity"; pub const ENTITY_FIELD_COMPONENTS: &str = "components"; -struct SceneEntityVisitor<'a> { +struct EntityVisitor<'a> { pub registry: &'a TypeRegistry, } -impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> { +impl<'a, 'de> Visitor<'de> for EntityVisitor<'a> { type Value = DynamicEntity; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { From a67fcfe15f0fb741b13e0f8bc89a1cd1feedcca0 Mon Sep 17 00:00:00 2001 From: davier Date: Fri, 7 Jan 2022 21:36:22 +0000 Subject: [PATCH 02/11] Update crates/bevy_scene/src/dynamic_scene.rs Co-authored-by: Alice Cecile --- crates/bevy_scene/src/dynamic_scene.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 3f8d2a2bccf46..f13cb090edca4 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -116,7 +116,7 @@ impl DynamicScene { } })?; - // If the entity already has the given resource attached, + // If the world already contains an instance of the given resource // just apply the (possibly) new value, otherwise insert the resource if world.contains_resource_type_id(registration.type_id()) { reflect_resource.apply_resource(world, &**resource); From f43d667e5b6d92440b396f07f1fedd905fd3b5d4 Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Fri, 7 Jan 2022 22:58:23 +0100 Subject: [PATCH 03/11] Cleanup resources archetype access --- crates/bevy_scene/src/dynamic_scene.rs | 13 ++++++------- crates/bevy_scene/src/scene_spawner.rs | 9 ++------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index f13cb090edca4..302a9dcebe24f 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -36,13 +36,8 @@ impl DynamicScene { let mut scene = DynamicScene::default(); let type_registry = type_registry.read(); - let mut archetypes = world.archetypes().iter(); - - // Empty archetype - let _ = archetypes.next().unwrap(); - // Resources archetype - let resources_archetype = archetypes.next().unwrap(); + let resources_archetype = world.archetypes().resource(); for component_id in resources_archetype.components() { let reflect_resource = world .components() @@ -57,7 +52,11 @@ impl DynamicScene { } // Other archetypes - for archetype in archetypes { + for archetype in world.archetypes().iter() { + if archetype.entities().is_empty() { + continue; + } + let entities_offset = scene.entities.len(); // Create a new dynamic entity for each entity of the given archetype diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index dee51f460fe42..1c8a6963b178e 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -161,13 +161,8 @@ impl SceneSpawner { handle: scene_handle.clone(), })?; - let mut archetypes = scene.world.archetypes().iter(); - - // Empty archetype - let _ = archetypes.next().unwrap(); - // Resources archetype - let resources_archetype = archetypes.next().unwrap(); + let resources_archetype = scene.world.archetypes().resource(); for component_id in resources_archetype.components() { let component_info = scene .world @@ -193,7 +188,7 @@ impl SceneSpawner { } // Other archetypes - for archetype in archetypes { + for archetype in scene.world.archetypes().iter() { for scene_entity in archetype.entities() { let entity = *instance_info .entity_map From 0fd72ae451cfac54feba0aa4de0f53daa49dd4f5 Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Mon, 10 Jan 2022 20:45:46 +0100 Subject: [PATCH 04/11] Remove unwraps --- crates/bevy_scene/src/dynamic_scene.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 302a9dcebe24f..d381a4f09ce4f 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -42,7 +42,8 @@ impl DynamicScene { let reflect_resource = world .components() .get_info(component_id) - .and_then(|info| type_registry.get(info.type_id().unwrap())) + .and_then(|info| info.type_id()) + .and_then(|type_id| type_registry.get(type_id)) .and_then(|registration| registration.data::()); if let Some(reflect_resource) = reflect_resource { if let Some(resource) = reflect_resource.reflect_resource(world) { @@ -73,7 +74,8 @@ impl DynamicScene { let reflect_component = world .components() .get_info(component_id) - .and_then(|info| type_registry.get(info.type_id().unwrap())) + .and_then(|info| info.type_id()) + .and_then(|type_id| type_registry.get(type_id)) .and_then(|registration| registration.data::()); if let Some(reflect_component) = reflect_component { for (i, entity) in archetype.entities().iter().enumerate() { From 8503ab78cc0b48f652b9f3f7864089d7f900576a Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Mon, 10 Jan 2022 21:25:05 +0100 Subject: [PATCH 05/11] Add `contains_resource_with_id` --- crates/bevy_ecs/src/world/mod.rs | 12 +++++++++--- crates/bevy_scene/src/dynamic_scene.rs | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index e8b4783377442..e072800cf358f 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -644,21 +644,27 @@ impl World { Some(unsafe { ptr.cast::().read() }) } + /// Returns `true` if a resource of a given [`ComponentId`] exists. Otherwise returns `false`. + #[inline] + pub fn contains_resource_with_id(&self, component_id: ComponentId) -> bool { + self.get_populated_resource_column(component_id).is_some() + } + /// Returns `true` if a resource of a given [`TypeId`] exists. Otherwise returns `false`. #[inline] - pub fn contains_resource_type_id(&self, type_id: TypeId) -> bool { + pub fn contains_resource_with_type(&self, type_id: TypeId) -> bool { let component_id = if let Some(component_id) = self.components.get_resource_id(type_id) { component_id } else { return false; }; - self.get_populated_resource_column(component_id).is_some() + self.contains_resource_with_id(component_id) } /// Returns `true` if a resource of type `T` exists. Otherwise returns `false`. #[inline] pub fn contains_resource(&self) -> bool { - self.contains_resource_type_id(TypeId::of::()) + self.contains_resource_with_type(TypeId::of::()) } /// Gets a reference to the resource of the given type, if it exists. Otherwise returns [None] diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index d381a4f09ce4f..b3a847a8e87c9 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -119,7 +119,7 @@ impl DynamicScene { // If the world already contains an instance of the given resource // just apply the (possibly) new value, otherwise insert the resource - if world.contains_resource_type_id(registration.type_id()) { + if world.contains_resource_with_type(registration.type_id()) { reflect_resource.apply_resource(world, &**resource); } else { reflect_resource.insert_resource(world, &**resource); From 39a28c25a6aa592902d4a1f5fae47016cb8f646d Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Wed, 12 Jan 2022 13:50:40 +0100 Subject: [PATCH 06/11] Provide safe exclusive access --- crates/bevy_ecs/src/reflect.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/crates/bevy_ecs/src/reflect.rs b/crates/bevy_ecs/src/reflect.rs index cd903e973bc8e..22921784f0829 100644 --- a/crates/bevy_ecs/src/reflect.rs +++ b/crates/bevy_ecs/src/reflect.rs @@ -17,7 +17,7 @@ pub struct ReflectComponent { apply_component: fn(&mut World, Entity, &dyn Reflect), remove_component: fn(&mut World, Entity), reflect_component: fn(&World, Entity) -> Option<&dyn Reflect>, - reflect_component_mut: unsafe fn(&World, Entity) -> Option, + reflect_component_unchecked_mut: unsafe fn(&World, Entity) -> Option, copy_component: fn(&World, &mut World, Entity, Entity), } @@ -48,7 +48,7 @@ impl ReflectComponent { entity: Entity, ) -> Option> { // SAFE: unique world access - unsafe { (self.reflect_component_mut)(world, entity) } + unsafe { (self.reflect_component_unchecked_mut)(world, entity) } } /// # Safety @@ -62,7 +62,7 @@ impl ReflectComponent { world: &'a World, entity: Entity, ) -> Option> { - (self.reflect_component_mut)(world, entity) + (self.reflect_component_unchecked_mut)(world, entity) } pub fn copy_component( @@ -110,7 +110,7 @@ impl FromType for ReflectComponent { .get::() .map(|c| c as &dyn Reflect) }, - reflect_component_mut: |world, entity| unsafe { + reflect_component_unchecked_mut: |world, entity| unsafe { world .get_entity(entity)? .get_unchecked_mut::(world.last_change_tick(), world.read_change_tick()) @@ -129,7 +129,7 @@ pub struct ReflectResource { apply_resource: fn(&mut World, &dyn Reflect), remove_resource: fn(&mut World), reflect_resource: fn(&World) -> Option<&dyn Reflect>, - reflect_resource_mut: unsafe fn(&World) -> Option, + reflect_resource_unchecked_mut: unsafe fn(&World) -> Option, copy_resource: fn(&World, &mut World), } @@ -150,14 +150,22 @@ impl ReflectResource { (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_mut<'a>(&self, world: &'a World) -> Option> { - (self.reflect_resource_mut)(world) + 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) { @@ -181,7 +189,7 @@ impl FromType for ReflectResource { world.remove_resource::(); }, reflect_resource: |world| world.get_resource::().map(|res| res as &dyn Reflect), - reflect_resource_mut: |world| unsafe { + reflect_resource_unchecked_mut: |world| unsafe { world .get_resource_unchecked_mut::() .map(|res| ReflectMut { From 809657b52e0f641ed769b0ac00f18e87362aa3da Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Wed, 12 Jan 2022 13:54:02 +0100 Subject: [PATCH 07/11] Reflect MSAA --- crates/bevy_render/src/view/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 9914c6fa16bc6..24839aef47bc5 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -18,13 +18,16 @@ use crate::{ use bevy_app::{App, Plugin}; use bevy_ecs::prelude::*; use bevy_math::{Mat4, Vec3}; +use bevy_reflect::Reflect; use bevy_transform::components::GlobalTransform; pub struct ViewPlugin; impl Plugin for ViewPlugin { fn build(&self, app: &mut App) { - app.init_resource::().add_plugin(VisibilityPlugin); + app.register_type::() + .init_resource::() + .add_plugin(VisibilityPlugin); app.sub_app_mut(RenderApp) .init_resource::() @@ -37,7 +40,8 @@ impl Plugin for ViewPlugin { } } -#[derive(Clone)] +#[derive(Clone, Reflect)] +#[reflect(Resource)] pub struct Msaa { /// The number of samples to run for Multi-Sample Anti-Aliasing. Higher numbers result in /// smoother edges. Note that WGPU currently only supports 1 or 4 samples. From 48bf06c76b77a5c029a80470d6a0be43e72f61f6 Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Wed, 12 Jan 2022 13:54:16 +0100 Subject: [PATCH 08/11] Reflect WireframeConfig --- crates/bevy_pbr/src/wireframe.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index 02b5d9dbee6eb..25169f1dbbdad 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -29,7 +29,8 @@ impl Plugin for WireframePlugin { Shader::from_wgsl(include_str!("render/wireframe.wgsl")), ); - app.init_resource::(); + app.register_type::() + .init_resource::(); app.sub_app_mut(RenderApp) .add_render_command::() @@ -58,7 +59,8 @@ fn extract_wireframes(mut commands: Commands, query: Query Date: Wed, 12 Jan 2022 13:54:56 +0100 Subject: [PATCH 09/11] Reflect AmbientLight, DirectionalLightShadowMap and PointLightShadowMap --- crates/bevy_pbr/src/lib.rs | 4 +++- crates/bevy_pbr/src/light.rs | 10 +++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 2e4fddbc821bd..17b98a5759f02 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -69,10 +69,12 @@ impl Plugin for PbrPlugin { app.add_plugin(MeshRenderPlugin) .add_plugin(MaterialPlugin::::default()) + .register_type::() + .register_type::() + .register_type::() .init_resource::() .init_resource::() .init_resource::() - .init_resource::() .init_resource::() .add_system_to_stage( CoreStage::PostUpdate, diff --git a/crates/bevy_pbr/src/light.rs b/crates/bevy_pbr/src/light.rs index f77501bb93cfb..fae2624473c24 100644 --- a/crates/bevy_pbr/src/light.rs +++ b/crates/bevy_pbr/src/light.rs @@ -2,6 +2,7 @@ use std::collections::HashSet; use bevy_ecs::prelude::*; use bevy_math::{Mat4, UVec2, UVec3, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles}; +use bevy_reflect::Reflect; use bevy_render::{ camera::{Camera, CameraProjection, OrthographicProjection}, color::Color, @@ -67,7 +68,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, } @@ -144,7 +146,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, } @@ -159,7 +162,8 @@ impl Default for DirectionalLightShadowMap { } /// An ambient light, which lights the entire scene equally. -#[derive(Debug)] +#[derive(Debug, Reflect)] +#[reflect(Resource)] pub struct AmbientLight { pub color: Color, /// A direct scale factor multiplied with `color` before being passed to the shader. From a81cff76f5ee6d37eaab6a84f479c9bc03a623c7 Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Wed, 12 Jan 2022 13:55:17 +0100 Subject: [PATCH 10/11] Reflect ClearColor --- crates/bevy_core_pipeline/Cargo.toml | 2 +- crates/bevy_core_pipeline/src/lib.rs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/bevy_core_pipeline/Cargo.toml b/crates/bevy_core_pipeline/Cargo.toml index e944e2c53cbd5..d7046630bdd0f 100644 --- a/crates/bevy_core_pipeline/Cargo.toml +++ b/crates/bevy_core_pipeline/Cargo.toml @@ -19,4 +19,4 @@ bevy_asset = { path = "../bevy_asset", version = "0.5.0" } bevy_core = { path = "../bevy_core", version = "0.5.0" } bevy_ecs = { path = "../bevy_ecs", version = "0.5.0" } bevy_render = { path = "../bevy_render", version = "0.5.0" } - +bevy_reflect = { path = "../bevy_reflect", version = "0.5.0" } diff --git a/crates/bevy_core_pipeline/src/lib.rs b/crates/bevy_core_pipeline/src/lib.rs index 709c4564879c4..7e9bba6804d44 100644 --- a/crates/bevy_core_pipeline/src/lib.rs +++ b/crates/bevy_core_pipeline/src/lib.rs @@ -18,6 +18,7 @@ pub use main_pass_driver::*; use bevy_app::{App, Plugin}; use bevy_core::FloatOrd; use bevy_ecs::prelude::*; +use bevy_reflect::Reflect; use bevy_render::{ camera::{ActiveCameras, CameraPlugin}, color::Color, @@ -34,7 +35,8 @@ use bevy_render::{ }; /// Resource that configures the clear color -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Reflect)] +#[reflect(Resource)] pub struct ClearColor(pub Color); impl Default for ClearColor { @@ -86,7 +88,8 @@ pub struct CorePipelinePlugin; impl Plugin for CorePipelinePlugin { fn build(&self, app: &mut App) { - app.init_resource::(); + app.register_type::() + .init_resource::(); let render_app = app.sub_app_mut(RenderApp); render_app From 9c0f7f801a3deac3c29183b5eb93141f45017ffe Mon Sep 17 00:00:00 2001 From: Brice DAVIER Date: Wed, 12 Jan 2022 14:32:52 +0100 Subject: [PATCH 11/11] Improve error handling --- crates/bevy_scene/src/scene_spawner.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index 1c8a6963b178e..30a0e49898c03 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -9,10 +9,7 @@ use bevy_ecs::{ }; use bevy_reflect::TypeRegistryArc; use bevy_transform::{hierarchy::AddChild, prelude::Parent}; -use bevy_utils::{ - tracing::{debug, error}, - HashMap, -}; +use bevy_utils::{tracing::error, HashMap}; use thiserror::Error; use uuid::Uuid; @@ -170,8 +167,11 @@ impl SceneSpawner { .get_info(component_id) .expect("component_ids in archetypes should have ComponentInfo"); - // Resources that are not registered are ignored - if let Some(registration) = type_registry.get(component_info.type_id().unwrap()) { + let type_id = component_info + .type_id() + .expect("Reflected resources must have a type_id"); + + if let Some(registration) = type_registry.get(type_id) { let reflect_resource = registration.data::().ok_or_else(|| { SceneSpawnError::UnregisteredResource { @@ -180,10 +180,9 @@ impl SceneSpawner { })?; reflect_resource.copy_resource(&scene.world, world); } else { - debug!( - "Ignored unregistered resource when loading scene: {}", - component_info.name() - ); + return Err(SceneSpawnError::UnregisteredType { + type_name: component_info.name().to_string(), + }); } }