From f562869f992a15af61b51072460b90c7c31e338f Mon Sep 17 00:00:00 2001 From: robtfm <50659922+robtfm@users.noreply.github.com> Date: Tue, 17 May 2022 12:26:53 +0100 Subject: [PATCH 01/26] fmt --- crates/bevy_pbr/src/render/light.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 3b6857af36c44..7e8d8ae46423f 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -9,9 +9,7 @@ use bevy_ecs::{ prelude::*, system::{lifetimeless::*, SystemParamItem}, }; -use bevy_math::{ - const_vec3, Mat4, UVec2, UVec3, UVec4, Vec2, Vec3, Vec4, Vec4Swizzles, -}; +use bevy_math::{const_vec3, Mat4, UVec2, UVec3, UVec4, Vec2, Vec3, Vec4, Vec4Swizzles}; use bevy_render::{ camera::{Camera, CameraProjection}, color::Color, From 3233ecce38869054657c4f1493579c54bd609b82 Mon Sep 17 00:00:00 2001 From: Jakob Hellermann Date: Tue, 10 May 2022 20:01:55 +0000 Subject: [PATCH 02/26] fix "unused" warnings when compiling with `render` feature but without `animation` (#4714) # Objective When running `cargo check --no-default-features --features render` I get ```rust warning: unused import: `Quat` --> crates/bevy_gltf/src/loader.rs:11:23 | 11 | use bevy_math::{Mat4, Quat, Vec3}; | ^^^^ | = note: `#[warn(unused_imports)]` on by default warning: function is never used: `paths_recur` --> crates/bevy_gltf/src/loader.rs:542:4 | 542 | fn paths_recur( | ^^^^^^^^^^^ | = note: `#[warn(dead_code)]` on by default ``` ## Solution Put these items behind `#[cfg(feature = "animation")]`. --- crates/bevy_gltf/src/loader.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index 9233d619a8dda..04c391907e0a3 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -1,6 +1,4 @@ use anyhow::Result; -#[cfg(feature = "bevy_animation")] -use bevy_animation::{AnimationClip, AnimationPlayer, EntityPath, Keyframes, VariableCurve}; use bevy_asset::{ AssetIoError, AssetLoader, AssetPath, BoxedFuture, Handle, LoadContext, LoadedAsset, }; @@ -8,7 +6,7 @@ use bevy_core::Name; use bevy_ecs::{entity::Entity, prelude::FromWorld, world::World}; use bevy_hierarchy::{BuildWorldChildren, WorldChildBuilder}; use bevy_log::warn; -use bevy_math::{Mat4, Quat, Vec3}; +use bevy_math::{Mat4, Vec3}; use bevy_pbr::{ AlphaMode, DirectionalLight, DirectionalLightBundle, PbrBundle, PointLight, PointLightBundle, StandardMaterial, @@ -150,7 +148,7 @@ async fn load_gltf<'a, 'b>( let mut named_animations = HashMap::default(); let mut animation_roots = HashSet::default(); for animation in gltf.animations() { - let mut animation_clip = AnimationClip::default(); + let mut animation_clip = bevy_animation::AnimationClip::default(); for channel in animation.channels() { match channel.sampler().interpolation() { gltf::animation::Interpolation::Linear => (), @@ -177,13 +175,15 @@ async fn load_gltf<'a, 'b>( let keyframes = if let Some(outputs) = reader.read_outputs() { match outputs { gltf::animation::util::ReadOutputs::Translations(tr) => { - Keyframes::Translation(tr.map(Vec3::from).collect()) + bevy_animation::Keyframes::Translation(tr.map(Vec3::from).collect()) } gltf::animation::util::ReadOutputs::Rotations(rots) => { - Keyframes::Rotation(rots.into_f32().map(Quat::from_array).collect()) + bevy_animation::Keyframes::Rotation( + rots.into_f32().map(bevy_math::Quat::from_array).collect(), + ) } gltf::animation::util::ReadOutputs::Scales(scale) => { - Keyframes::Scale(scale.map(Vec3::from).collect()) + bevy_animation::Keyframes::Scale(scale.map(Vec3::from).collect()) } gltf::animation::util::ReadOutputs::MorphTargetWeights(_) => { warn!("Morph animation property not yet supported"); @@ -198,10 +198,10 @@ async fn load_gltf<'a, 'b>( if let Some((root_index, path)) = paths.get(&node.index()) { animation_roots.insert(root_index); animation_clip.add_curve_to_path( - EntityPath { + bevy_animation::EntityPath { parts: path.clone(), }, - VariableCurve { + bevy_animation::VariableCurve { keyframe_timestamps, keyframes, }, @@ -481,7 +481,7 @@ async fn load_gltf<'a, 'b>( if animation_roots.contains(&node.index()) { world .entity_mut(*node_index_to_entity_map.get(&node.index()).unwrap()) - .insert(AnimationPlayer::default()); + .insert(bevy_animation::AnimationPlayer::default()); } } } @@ -539,6 +539,7 @@ fn node_name(node: &Node) -> Name { Name::new(name) } +#[cfg(feature = "bevy_animation")] fn paths_recur( node: Node, current_path: &[Name], From 8c4f2072c052d4657252536f2ac33f59cb32938f Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 10 May 2022 20:18:58 +0000 Subject: [PATCH 03/26] Add a tracing span for run criteria. (#4709) # Objective Adds a tracing span for run critieria. This change will be invalidated by stageless, but it was a simple change. Fixes #4681. ## Changelog Shows how long a run criteria takes to run when tracing is enabled. ![image](https://user-images.githubusercontent.com/2180432/167517447-93dba7db-8c85-4686-90e0-30e9636f120f.png) --- crates/bevy_ecs/src/schedule/stage.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/bevy_ecs/src/schedule/stage.rs b/crates/bevy_ecs/src/schedule/stage.rs index 2cf14f0d0097b..210ac514c2085 100644 --- a/crates/bevy_ecs/src/schedule/stage.rs +++ b/crates/bevy_ecs/src/schedule/stage.rs @@ -802,6 +802,12 @@ impl Stage for SystemStage { for index in 0..self.run_criteria.len() { let (run_criteria, tail) = self.run_criteria.split_at_mut(index); let mut criteria = &mut tail[0]; + + #[cfg(feature = "trace")] + let _span = + bevy_utils::tracing::info_span!("run criteria", name = &*criteria.name()) + .entered(); + match &mut criteria.inner { RunCriteriaInner::Single(system) => criteria.should_run = system.run((), world), RunCriteriaInner::Piped { From 0527b595b9a6c79a69a3fadd80c77fb1e4e1fc8c Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Tue, 10 May 2022 20:18:59 +0000 Subject: [PATCH 04/26] Tidy up the code of events (#4713) # Objective - The code in `events.rs` was a bit messy. There was lots of duplication between `EventReader` and `ManualEventReader`, and the state management code is not needed. ## Solution - Clean it up. ## Future work Should we remove the type parameter from `ManualEventReader`? It doesn't have any meaning outside of its source `Events`. But there's no real reason why it needs to have a type parameter - it's just plain data. I didn't remove it yet to keep the type safety in some of the users of it (primarily related to `&mut World` usage) --- benches/benches/bevy_ecs/run_criteria.rs | 2 +- crates/bevy_ecs/src/event.rs | 256 +++++++++++------------ 2 files changed, 122 insertions(+), 136 deletions(-) diff --git a/benches/benches/bevy_ecs/run_criteria.rs b/benches/benches/bevy_ecs/run_criteria.rs index 7d842d01ea959..26a9ef1984e72 100644 --- a/benches/benches/bevy_ecs/run_criteria.rs +++ b/benches/benches/bevy_ecs/run_criteria.rs @@ -2,7 +2,7 @@ use bevy_ecs::{ component::Component, prelude::{ParallelSystemDescriptorCoercion, Res, RunCriteriaDescriptorCoercion}, schedule::{ShouldRun, Stage, SystemStage}, - system::{IntoSystem, Query}, + system::Query, world::World, }; use criterion::{criterion_group, criterion_main, Criterion}; diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 0a4a459073a5d..00e0c6b74607e 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -3,6 +3,7 @@ use crate as bevy_ecs; use crate::system::{Local, Res, ResMut, SystemParam}; use bevy_utils::tracing::trace; +use std::ops::{Deref, DerefMut}; use std::{ fmt::{self}, hash::Hash, @@ -56,12 +57,6 @@ struct EventInstance { pub event: E, } -#[derive(Debug)] -enum State { - A, - B, -} - /// An event collection that represents the events that occurred within the last two /// [`Events::update`] calls. /// Events can be written to using an [`EventWriter`] @@ -135,42 +130,92 @@ enum State { /// #[derive(Debug)] pub struct Events { - events_a: Vec>, - events_b: Vec>, - a_start_event_count: usize, - b_start_event_count: usize, + /// Holds the oldest still active events. + /// Note that a.start_event_count + a.len() should always === events_b.start_event_count. + events_a: EventSequence, + /// Holds the newer events. + events_b: EventSequence, event_count: usize, - state: State, } +// Derived Default impl would incorrectly require E: Default impl Default for Events { fn default() -> Self { - Events { - a_start_event_count: 0, - b_start_event_count: 0, - event_count: 0, - events_a: Vec::new(), - events_b: Vec::new(), - state: State::A, + Self { + events_a: Default::default(), + events_b: Default::default(), + event_count: Default::default(), } } } -fn map_instance_event_with_id(event_instance: &EventInstance) -> (&E, EventId) { - (&event_instance.event, event_instance.event_id) +#[derive(Debug)] +struct EventSequence { + events: Vec>, + start_event_count: usize, +} + +// Derived Default impl would incorrectly require E: Default +impl Default for EventSequence { + fn default() -> Self { + Self { + events: Default::default(), + start_event_count: Default::default(), + } + } } -fn map_instance_event(event_instance: &EventInstance) -> &E { - &event_instance.event +impl Deref for EventSequence { + type Target = Vec>; + + fn deref(&self) -> &Self::Target { + &self.events + } +} + +impl DerefMut for EventSequence { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.events + } } /// Reads events of type `T` in order and tracks which events have already been read. #[derive(SystemParam)] pub struct EventReader<'w, 's, E: Event> { - last_event_count: Local<'s, (usize, PhantomData)>, + reader: Local<'s, ManualEventReader>, events: Res<'w, Events>, } +impl<'w, 's, E: Event> EventReader<'w, 's, E> { + /// Iterates over the events this [`EventReader`] has not seen yet. This updates the + /// [`EventReader`]'s event counter, which means subsequent event reads will not include events + /// that happened before now. + pub fn iter(&mut self) -> impl DoubleEndedIterator + ExactSizeIterator { + self.iter_with_id().map(|(event, _id)| event) + } + + /// Like [`iter`](Self::iter), except also returning the [`EventId`] of the events. + pub fn iter_with_id( + &mut self, + ) -> impl DoubleEndedIterator)> + ExactSizeIterator)> + { + self.reader.iter_with_id(&self.events).map(|r @ (_, id)| { + trace!("EventReader::iter() -> {}", id); + r + }) + } + + /// Determines the number of events available to be read from this [`EventReader`] without consuming any. + pub fn len(&self) -> usize { + self.reader.len(&self.events) + } + + /// Determines if are any events available to be read without consuming any. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + /// Sends events of type `T`. #[derive(SystemParam)] pub struct EventWriter<'w, 's, E: Event> { @@ -199,6 +244,7 @@ impl<'w, 's, E: Event> EventWriter<'w, 's, E> { } } +#[derive(Debug)] pub struct ManualEventReader { last_event_count: usize, _marker: PhantomData, @@ -220,7 +266,7 @@ impl ManualEventReader { &'a mut self, events: &'a Events, ) -> impl DoubleEndedIterator + ExactSizeIterator { - internal_event_reader(&mut self.last_event_count, events).map(|(e, _)| e) + self.iter_with_id(events).map(|(e, _)| e) } /// See [`EventReader::iter_with_id`] @@ -229,12 +275,34 @@ impl ManualEventReader { events: &'a Events, ) -> impl DoubleEndedIterator)> + ExactSizeIterator)> { - internal_event_reader(&mut self.last_event_count, events) + // if the reader has seen some of the events in a buffer, find the proper index offset. + // otherwise read all events in the buffer + let a_index = (self.last_event_count).saturating_sub(events.events_a.start_event_count); + let b_index = (self.last_event_count).saturating_sub(events.events_b.start_event_count); + let a = events.events_a.get(a_index..).unwrap_or_default(); + let b = events.events_b.get(b_index..).unwrap_or_default(); + let unread_count = a.len() + b.len(); + // Ensure `len` is implemented correctly + debug_assert_eq!(unread_count, self.len(events)); + self.last_event_count = events.event_count - unread_count; + // Iterate the oldest first, then the newer events + let iterator = a.iter().chain(b.iter()); + iterator + .map(|e| (&e.event, e.event_id)) + .with_exact_size(unread_count) + .inspect(move |(_, id)| self.last_event_count = (id.id + 1).max(self.last_event_count)) } /// See [`EventReader::len`] pub fn len(&self, events: &Events) -> usize { - internal_event_reader(&mut self.last_event_count.clone(), events).len() + // The number of events in this reader is the difference between the most recent event + // and the last event seen by it. This will be at most the number of events contained + // with the events (any others have already been dropped) + // TODO: Warn when there are dropped events, or return e.g. a `Result` + events + .event_count + .saturating_sub(self.last_event_count) + .min(events.len()) } /// See [`EventReader::is_empty`] @@ -243,39 +311,6 @@ impl ManualEventReader { } } -/// Like [`iter_with_id`](EventReader::iter_with_id) except not emitting any traces for read -/// messages. -fn internal_event_reader<'a, E: Event>( - last_event_count: &'a mut usize, - events: &'a Events, -) -> impl DoubleEndedIterator)> + ExactSizeIterator)> -{ - // if the reader has seen some of the events in a buffer, find the proper index offset. - // otherwise read all events in the buffer - let a_index = if *last_event_count > events.a_start_event_count { - *last_event_count - events.a_start_event_count - } else { - 0 - }; - let b_index = if *last_event_count > events.b_start_event_count { - *last_event_count - events.b_start_event_count - } else { - 0 - }; - let a = events.events_a.get(a_index..).unwrap_or_default(); - let b = events.events_b.get(b_index..).unwrap_or_default(); - let unread_count = a.len() + b.len(); - *last_event_count = events.event_count - unread_count; - let iterator = match events.state { - State::A => b.iter().chain(a.iter()), - State::B => a.iter().chain(b.iter()), - }; - iterator - .map(map_instance_event_with_id) - .with_exact_size(unread_count) - .inspect(move |(_, id)| *last_event_count = (id.id + 1).max(*last_event_count)) -} - trait IteratorExt { fn with_exact_size(self, len: usize) -> ExactSize where @@ -330,36 +365,6 @@ impl ExactSizeIterator for ExactSize { } } -impl<'w, 's, E: Event> EventReader<'w, 's, E> { - /// Iterates over the events this [`EventReader`] has not seen yet. This updates the - /// [`EventReader`]'s event counter, which means subsequent event reads will not include events - /// that happened before now. - pub fn iter(&mut self) -> impl DoubleEndedIterator + ExactSizeIterator { - self.iter_with_id().map(|(event, _id)| event) - } - - /// Like [`iter`](Self::iter), except also returning the [`EventId`] of the events. - pub fn iter_with_id( - &mut self, - ) -> impl DoubleEndedIterator)> + ExactSizeIterator)> - { - internal_event_reader(&mut self.last_event_count.0, &self.events).map(|(event, id)| { - trace!("EventReader::iter() -> {}", id); - (event, id) - }) - } - - /// Determines the number of events available to be read from this [`EventReader`] without consuming any. - pub fn len(&self) -> usize { - internal_event_reader(&mut self.last_event_count.0.clone(), &self.events).len() - } - - /// Determines if are any events available to be read without consuming any. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } -} - impl Events { /// "Sends" an `event` by writing it to the current event buffer. [`EventReader`]s can then read /// the event. @@ -372,11 +377,7 @@ impl Events { let event_instance = EventInstance { event_id, event }; - match self.state { - State::A => self.events_a.push(event_instance), - State::B => self.events_b.push(event_instance), - } - + self.events_b.push(event_instance); self.event_count += 1; } @@ -390,10 +391,7 @@ impl Events { /// Gets a new [`ManualEventReader`]. This will include all events already in the event buffers. pub fn get_reader(&self) -> ManualEventReader { - ManualEventReader { - last_event_count: 0, - _marker: PhantomData, - } + ManualEventReader::default() } /// Gets a new [`ManualEventReader`]. This will ignore all events already in the event buffers. @@ -401,25 +399,20 @@ impl Events { pub fn get_reader_current(&self) -> ManualEventReader { ManualEventReader { last_event_count: self.event_count, - _marker: PhantomData, + ..Default::default() } } /// Swaps the event buffers and clears the oldest event buffer. In general, this should be /// called once per frame/update. pub fn update(&mut self) { - match self.state { - State::A => { - self.events_b.clear(); - self.state = State::B; - self.b_start_event_count = self.event_count; - } - State::B => { - self.events_a.clear(); - self.state = State::A; - self.a_start_event_count = self.event_count; - } - } + std::mem::swap(&mut self.events_a, &mut self.events_b); + self.events_b.clear(); + self.events_b.start_event_count = self.event_count; + debug_assert_eq!( + self.events_a.start_event_count + self.events_a.len(), + self.events_b.start_event_count + ); } /// A system that calls [`Events::update`] once per frame. @@ -429,8 +422,8 @@ impl Events { #[inline] fn reset_start_event_count(&mut self) { - self.a_start_event_count = self.event_count; - self.b_start_event_count = self.event_count; + self.events_a.start_event_count = self.event_count; + self.events_b.start_event_count = self.event_count; } /// Removes all events. @@ -441,29 +434,26 @@ impl Events { self.events_b.clear(); } + #[inline] + pub fn len(&self) -> usize { + self.events_a.len() + self.events_b.len() + } + /// Returns true if there are no events in this collection. #[inline] pub fn is_empty(&self) -> bool { - self.events_a.is_empty() && self.events_b.is_empty() + self.len() == 0 } /// Creates a draining iterator that removes all events. pub fn drain(&mut self) -> impl Iterator + '_ { self.reset_start_event_count(); - let map = |i: EventInstance| i.event; - match self.state { - State::A => self - .events_b - .drain(..) - .map(map) - .chain(self.events_a.drain(..).map(map)), - State::B => self - .events_a - .drain(..) - .map(map) - .chain(self.events_b.drain(..).map(map)), - } + // Drain the oldest events first, then the newest + self.events_a + .drain(..) + .chain(self.events_b.drain(..)) + .map(|i| i.event) } /// Iterates over events that happened since the last "update" call. @@ -475,10 +465,7 @@ impl Events { pub fn iter_current_update_events( &self, ) -> impl DoubleEndedIterator + ExactSizeIterator { - match self.state { - State::A => self.events_a.iter().map(map_instance_event), - State::B => self.events_b.iter().map(map_instance_event), - } + self.events_b.iter().map(|i| &i.event) } } @@ -497,10 +484,7 @@ impl std::iter::Extend for Events { EventInstance { event_id, event } }); - match self.state { - State::A => self.events_a.extend(events), - State::B => self.events_b.extend(events), - } + self.events_b.extend(events); trace!( "Events::extend() -> ids: ({}..{})", @@ -719,6 +703,8 @@ mod tests { let mut events = Events::::default(); events.send(TestEvent { i: 0 }); let reader = events.get_reader_current(); + dbg!(&reader); + dbg!(&events); assert!(reader.is_empty(&events)); events.send(TestEvent { i: 0 }); assert_eq!(reader.len(&events), 1); From 2f7f0b0b8c14d6fdc768bc4a70b1a0882e690abc Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Wed, 11 May 2022 09:31:39 +0000 Subject: [PATCH 05/26] Added keyboard key to mouse control for scene viewer example (#4411) # Objective - Added keyboard control for scene_viewer example. Fixes #4407 Co-authored-by: Troels Jessen --- examples/tools/scene_viewer.rs | 42 +++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/examples/tools/scene_viewer.rs b/examples/tools/scene_viewer.rs index 65dbd807b6718..eceade01f4631 100644 --- a/examples/tools/scene_viewer.rs +++ b/examples/tools/scene_viewer.rs @@ -18,21 +18,21 @@ fn main() { println!( " Controls: - MOUSE - Move camera orientation - LClick - Enable mouse movement - WSAD - forward/back/strafe left/right - LShift - 'run' - E - up - Q - down - L - animate light direction - U - toggle shadows - C - cycle through cameras - 5/6 - decrease/increase shadow projection width - 7/8 - decrease/increase shadow projection height - 9/0 - decrease/increase shadow projection near/far - - Space - Play/Pause animation - Enter - Cycle through animations + MOUSE - Move camera orientation + LClick/M - Enable mouse movement + WSAD - forward/back/strafe left/right + LShift - 'run' + E - up + Q - down + L - animate light direction + U - toggle shadows + C - cycle through cameras + 5/6 - decrease/increase shadow projection width + 7/8 - decrease/increase shadow projection height + 9/0 - decrease/increase shadow projection near/far + + Space - Play/Pause animation + Enter - Cycle through animations " ); App::new() @@ -404,7 +404,8 @@ struct CameraController { pub key_up: KeyCode, pub key_down: KeyCode, pub key_run: KeyCode, - pub key_enable_mouse: MouseButton, + pub mouse_key_enable_mouse: MouseButton, + pub keyboard_key_enable_mouse: KeyCode, pub walk_speed: f32, pub run_speed: f32, pub friction: f32, @@ -426,7 +427,8 @@ impl Default for CameraController { key_up: KeyCode::E, key_down: KeyCode::Q, key_run: KeyCode::LShift, - key_enable_mouse: MouseButton::Left, + mouse_key_enable_mouse: MouseButton::Left, + keyboard_key_enable_mouse: KeyCode::M, walk_speed: 5.0, run_speed: 15.0, friction: 0.5, @@ -442,6 +444,7 @@ fn camera_controller( mut mouse_events: EventReader, mouse_button_input: Res>, key_input: Res>, + mut move_toggled: Local, mut query: Query<(&mut Transform, &mut CameraController), With>, ) { let dt = time.delta_seconds(); @@ -477,6 +480,9 @@ fn camera_controller( if key_input.pressed(options.key_down) { axis_input.y -= 1.0; } + if key_input.just_pressed(options.keyboard_key_enable_mouse) { + *move_toggled = !*move_toggled; + } // Apply movement update if axis_input != Vec3::ZERO { @@ -501,7 +507,7 @@ fn camera_controller( // Handle mouse input let mut mouse_delta = Vec2::ZERO; - if mouse_button_input.pressed(options.key_enable_mouse) { + if mouse_button_input.pressed(options.mouse_key_enable_mouse) || *move_toggled { for mouse_event in mouse_events.iter() { mouse_delta += mouse_event.delta; } From f873a29a40368358486bd1ea25afbeb50b015ec6 Mon Sep 17 00:00:00 2001 From: Jakob Hellermann Date: Thu, 12 May 2022 18:39:20 +0000 Subject: [PATCH 06/26] fix tracy frame marker placement (after preseting *all* windows) (#4731) # Objective The frame marker event was emitted in the loop of presenting all the windows. This would mark the frame as finished multiple times if more than one window is used. ## Solution Move the frame marker to after the `for`-loop, so that it gets executed only once. --- crates/bevy_render/src/renderer/mod.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index 8faa558be7eab..07667096ac30b 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -63,15 +63,15 @@ pub fn render_system(world: &mut World) { if let Some(surface_texture) = texture_view.take_surface_texture() { surface_texture.present(); } - - #[cfg(feature = "tracing-tracy")] - bevy_utils::tracing::event!( - bevy_utils::tracing::Level::INFO, - message = "finished frame", - tracy.frame_mark = true - ); } } + + #[cfg(feature = "tracing-tracy")] + bevy_utils::tracing::event!( + bevy_utils::tracing::Level::INFO, + message = "finished frame", + tracy.frame_mark = true + ); } } From abf108f0772c2b3515a5f2f560143c98f32078b8 Mon Sep 17 00:00:00 2001 From: MrGVSV Date: Thu, 12 May 2022 19:43:23 +0000 Subject: [PATCH 07/26] bevy_reflect_derive: Tidying up the code (#4712) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com> --- crates/bevy_macro_utils/src/lib.rs | 16 + .../src/container_attributes.rs | 234 ++++ .../bevy_reflect_derive/src/derive_data.rs | 197 +++ .../src/field_attributes.rs | 76 ++ .../bevy_reflect_derive/src/from_reflect.rs | 251 ++-- .../bevy_reflect_derive/src/impls.rs | 410 +++++++ .../bevy_reflect_derive/src/lib.rs | 1075 ++--------------- .../bevy_reflect_derive/src/reflect_value.rs | 54 + .../bevy_reflect_derive/src/registration.rs | 25 + .../{reflect_trait.rs => trait_reflection.rs} | 10 +- .../bevy_reflect_derive/src/type_uuid.rs | 2 +- .../bevy_reflect_derive/src/utility.rs | 23 + crates/bevy_reflect/src/lib.rs | 2 +- 13 files changed, 1257 insertions(+), 1118 deletions(-) create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/registration.rs rename crates/bevy_reflect/bevy_reflect_derive/src/{reflect_trait.rs => trait_reflection.rs} (87%) create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/utility.rs diff --git a/crates/bevy_macro_utils/src/lib.rs b/crates/bevy_macro_utils/src/lib.rs index 4883999039dd8..d1ab5cc40f60f 100644 --- a/crates/bevy_macro_utils/src/lib.rs +++ b/crates/bevy_macro_utils/src/lib.rs @@ -78,6 +78,22 @@ impl BevyManifest { deps.and_then(find_in_deps) .or_else(|| deps_dev.and_then(find_in_deps)) } + + /// Returns the path for the crate with the given name. + /// + /// This is a convenience method for constructing a [manifest] and + /// calling the [`get_path`] method. + /// + /// This method should only be used where you just need the path and can't + /// cache the [manifest]. If caching is possible, it's recommended to create + /// the [manifest] yourself and use the [`get_path`] method. + /// + /// [`get_path`]: Self::get_path + /// [manifest]: Self + pub fn get_path_direct(name: &str) -> syn::Path { + Self::default().get_path(name) + } + pub fn get_path(&self, name: &str) -> syn::Path { self.maybe_get_path(name) .unwrap_or_else(|| Self::parse_str(name)) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs new file mode 100644 index 0000000000000..8e16e77626c5a --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -0,0 +1,234 @@ +//! Contains code related to container attributes for reflected types. +//! +//! A container attribute is an attribute which applies to an entire struct or enum +//! as opposed to a particular field or variant. An example of such an attribute is +//! the derive helper attribute for `Reflect`, which looks like: +//! `#[reflect(PartialEq, Default, ...)]` and `#[reflect_value(PartialEq, Default, ...)]`. + +use crate::utility; +use proc_macro2::Ident; +use quote::quote; +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::token::Comma; +use syn::{Meta, NestedMeta, Path}; + +// The "special" trait idents that are used internally for reflection. +// Received via attributes like `#[reflect(PartialEq, Hash, ...)]` +const PARTIAL_EQ_ATTR: &str = "PartialEq"; +const HASH_ATTR: &str = "Hash"; +const SERIALIZE_ATTR: &str = "Serialize"; + +/// A marker for trait implementations registered via the `Reflect` derive macro. +#[derive(Clone)] +pub(crate) enum TraitImpl { + /// The trait is not registered as implemented. + NotImplemented, + /// The trait is registered as implemented. + Implemented, + + // TODO: This can be made to use `ExprPath` instead of `Ident`, allowing for fully qualified paths to be used + /// The trait is registered with a custom function rather than an actual implementation. + Custom(Ident), +} + +impl Default for TraitImpl { + fn default() -> Self { + Self::NotImplemented + } +} + +/// A collection of traits that have been registered for a reflected type. +/// +/// This keeps track of a few traits that are utilized internally for reflection +/// (we'll call these traits _special traits_ within this context), but it +/// will also keep track of all registered traits. Traits are registered as part of the +/// `Reflect` derive macro using the helper attribute: `#[reflect(...)]`. +/// +/// The list of special traits are as follows: +/// * `Hash` +/// * `PartialEq` +/// * `Serialize` +/// +/// When registering a trait, there are a few things to keep in mind: +/// * Traits must have a valid `Reflect{}` struct in scope. For example, `Default` +/// needs `bevy_reflect::prelude::ReflectDefault` in scope. +/// * Traits must be single path identifiers. This means you _must_ use `Default` +/// instead of `std::default::Default` (otherwise it will try to register `Reflectstd`!) +/// * A custom function may be supplied in place of an actual implementation +/// for the special traits (but still follows the same single-path identifier +/// rules as normal). +/// +/// # Example +/// +/// Registering the `Default` implementation: +/// +/// ```ignore +/// // Import ReflectDefault so it's accessible by the derive macro +/// use bevy_reflect::prelude::ReflectDefault. +/// +/// #[derive(Reflect, Default)] +/// #[reflect(Default)] +/// struct Foo; +/// ``` +/// +/// Registering the `Hash` implementation: +/// +/// ```ignore +/// // `Hash` is a "special trait" and does not need (nor have) a ReflectHash struct +/// +/// #[derive(Reflect, Hash)] +/// #[reflect(Hash)] +/// struct Foo; +/// ``` +/// +/// Registering the `Hash` implementation using a custom function: +/// +/// ```ignore +/// // This function acts as our `Hash` implementation and +/// // corresponds to the `Reflect::reflect_hash` method. +/// fn get_hash(foo: &Foo) -> Option { +/// Some(123) +/// } +/// +/// #[derive(Reflect)] +/// // Register the custom `Hash` function +/// #[reflect(Hash(get_hash))] +/// struct Foo; +/// ``` +/// +/// > __Note:__ Registering a custom function only works for special traits. +/// +#[derive(Default)] +pub(crate) struct ReflectTraits { + hash: TraitImpl, + partial_eq: TraitImpl, + serialize: TraitImpl, + idents: Vec, +} + +impl ReflectTraits { + /// Create a new [`ReflectTraits`] instance from a set of nested metas. + pub fn from_nested_metas(nested_metas: &Punctuated) -> Self { + let mut traits = ReflectTraits::default(); + for nested_meta in nested_metas.iter() { + match nested_meta { + // Handles `#[reflect( Hash, Default, ... )]` + NestedMeta::Meta(Meta::Path(path)) => { + // Get the first ident in the path (hopefully the path only contains one and not `std::hash::Hash`) + let ident = if let Some(segment) = path.segments.iter().next() { + segment.ident.to_string() + } else { + continue; + }; + + match ident.as_str() { + PARTIAL_EQ_ATTR => traits.partial_eq = TraitImpl::Implemented, + HASH_ATTR => traits.hash = TraitImpl::Implemented, + SERIALIZE_ATTR => traits.serialize = TraitImpl::Implemented, + // We only track reflected idents for traits not considered special + _ => traits.idents.push(utility::get_reflect_ident(&ident)), + } + } + // Handles `#[reflect( Hash(custom_hash_fn) )]` + NestedMeta::Meta(Meta::List(list)) => { + // Get the first ident in the path (hopefully the path only contains one and not `std::hash::Hash`) + let ident = if let Some(segment) = list.path.segments.iter().next() { + segment.ident.to_string() + } else { + continue; + }; + + let list_meta = list.nested.iter().next(); + if let Some(NestedMeta::Meta(Meta::Path(path))) = list_meta { + if let Some(segment) = path.segments.iter().next() { + // This should be the ident of the custom function + let trait_func_ident = TraitImpl::Custom(segment.ident.clone()); + match ident.as_str() { + PARTIAL_EQ_ATTR => traits.partial_eq = trait_func_ident, + HASH_ATTR => traits.hash = trait_func_ident, + SERIALIZE_ATTR => traits.serialize = trait_func_ident, + _ => {} + } + } + } + } + _ => {} + } + } + + traits + } + + /// Returns true if the given reflected trait name (i.e. `ReflectDefault` for `Default`) + /// is registered for this type. + pub fn contains(&self, name: &str) -> bool { + self.idents.iter().any(|ident| ident == name) + } + + /// The list of reflected traits by their reflected ident (i.e. `ReflectDefault` for `Default`). + pub fn idents(&self) -> &[Ident] { + &self.idents + } + + /// Returns the logic for `Reflect::reflect_hash` as a `TokenStream`. + /// + /// If `Hash` was not registered, returns `None`. + pub fn get_hash_impl(&self, path: &Path) -> Option { + match &self.hash { + TraitImpl::Implemented => Some(quote! { + use std::hash::{Hash, Hasher}; + let mut hasher = #path::ReflectHasher::default(); + Hash::hash(&std::any::Any::type_id(self), &mut hasher); + Hash::hash(self, &mut hasher); + Some(hasher.finish()) + }), + TraitImpl::Custom(impl_fn) => Some(quote! { + Some(#impl_fn(self)) + }), + TraitImpl::NotImplemented => None, + } + } + + /// Returns the logic for `Reflect::reflect_partial_eq` as a `TokenStream`. + /// + /// If `PartialEq` was not registered, returns `None`. + pub fn get_partial_eq_impl(&self) -> Option { + match &self.partial_eq { + TraitImpl::Implemented => Some(quote! { + let value = value.any(); + if let Some(value) = value.downcast_ref::() { + Some(std::cmp::PartialEq::eq(self, value)) + } else { + Some(false) + } + }), + TraitImpl::Custom(impl_fn) => Some(quote! { + Some(#impl_fn(self, value)) + }), + TraitImpl::NotImplemented => None, + } + } + + /// Returns the logic for `Reflect::serializable` as a `TokenStream`. + /// + /// If `Serialize` was not registered, returns `None`. + pub fn get_serialize_impl(&self, path: &Path) -> Option { + match &self.serialize { + TraitImpl::Implemented => Some(quote! { + Some(#path::serde::Serializable::Borrowed(self)) + }), + TraitImpl::Custom(impl_fn) => Some(quote! { + Some(#impl_fn(self)) + }), + TraitImpl::NotImplemented => None, + } + } +} + +impl Parse for ReflectTraits { + fn parse(input: ParseStream) -> syn::Result { + let result = Punctuated::::parse_terminated(input)?; + Ok(ReflectTraits::from_nested_metas(&result)) + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs new file mode 100644 index 0000000000000..219fe8158bcac --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -0,0 +1,197 @@ +use crate::container_attributes::ReflectTraits; +use crate::field_attributes::{parse_field_attrs, ReflectFieldAttr}; +use crate::utility::get_bevy_reflect_path; +use crate::{REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME}; +use syn::{Data, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Meta, Path}; + +pub(crate) enum DeriveType { + Struct, + TupleStruct, + UnitStruct, + Value, +} + +/// Represents a field on a struct or tuple struct. +pub(crate) struct StructField<'a> { + /// The raw field. + pub data: &'a Field, + /// The reflection-based attributes on the field. + pub attrs: ReflectFieldAttr, + /// The index of this field within the struct. + pub index: usize, +} + +/// Data used by derive macros for `Reflect` and `FromReflect` +/// +/// # Example +/// ```ignore +/// // attrs +/// // |----------------------------------------| +/// #[reflect(PartialEq, Serialize, Deserialize, Default)] +/// // type_name generics +/// // |-------------------||----------| +/// struct ThingThatImReflecting { +/// x: T1, // | +/// y: T2, // |- fields +/// z: T3 // | +/// } +/// ``` +pub(crate) struct ReflectDeriveData<'a> { + derive_type: DeriveType, + traits: ReflectTraits, + type_name: &'a Ident, + generics: &'a Generics, + fields: Vec>, + bevy_reflect_path: Path, +} + +impl<'a> ReflectDeriveData<'a> { + pub fn from_input(input: &'a DeriveInput) -> Result { + let mut output = Self { + type_name: &input.ident, + derive_type: DeriveType::Value, + generics: &input.generics, + fields: Vec::new(), + traits: ReflectTraits::default(), + bevy_reflect_path: get_bevy_reflect_path(), + }; + + // Should indicate whether `#[reflect_value]` was used + let mut force_reflect_value = false; + + for attribute in input.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { + let meta_list = if let Meta::List(meta_list) = attribute { + meta_list + } else { + continue; + }; + + if let Some(ident) = meta_list.path.get_ident() { + if ident == REFLECT_ATTRIBUTE_NAME { + output.traits = ReflectTraits::from_nested_metas(&meta_list.nested); + } else if ident == REFLECT_VALUE_ATTRIBUTE_NAME { + force_reflect_value = true; + output.traits = ReflectTraits::from_nested_metas(&meta_list.nested); + } + } + } + + let fields = match &input.data { + Data::Struct(DataStruct { + fields: Fields::Named(fields), + .. + }) => { + if !force_reflect_value { + output.derive_type = DeriveType::Struct; + } + &fields.named + } + Data::Struct(DataStruct { + fields: Fields::Unnamed(fields), + .. + }) => { + if !force_reflect_value { + output.derive_type = DeriveType::TupleStruct; + } + &fields.unnamed + } + Data::Struct(DataStruct { + fields: Fields::Unit, + .. + }) => { + if !force_reflect_value { + output.derive_type = DeriveType::UnitStruct; + } + return Ok(output); + } + _ => { + return Ok(output); + } + }; + + let mut errors: Option = None; + output.fields = fields + .iter() + .enumerate() + .map(|(index, field)| { + let attrs = parse_field_attrs(&field.attrs).unwrap_or_else(|err| { + if let Some(ref mut errors) = errors { + errors.combine(err); + } else { + errors = Some(err); + } + ReflectFieldAttr::default() + }); + + StructField { + index, + attrs, + data: field, + } + }) + .collect::>(); + if let Some(errs) = errors { + return Err(errs); + } + + Ok(output) + } + + /// Get an iterator over the active fields + pub fn active_fields(&self) -> impl Iterator> { + self.fields.iter().filter(|field| !field.attrs.ignore) + } + + /// Get an iterator over the ignored fields + pub fn ignored_fields(&self) -> impl Iterator> { + self.fields.iter().filter(|field| field.attrs.ignore) + } + + /// Get a collection of all active types + pub fn active_types(&self) -> Vec { + self.active_fields() + .map(|field| field.data.ty.clone()) + .collect::>() + } + + /// The [`DeriveType`] of this struct. + pub fn derive_type(&self) -> &DeriveType { + &self.derive_type + } + + /// The registered reflect traits on this struct. + pub fn traits(&self) -> &ReflectTraits { + &self.traits + } + + /// The name of this struct. + pub fn type_name(&self) -> &'a Ident { + self.type_name + } + + /// The generics associated with this struct. + pub fn generics(&self) -> &'a Generics { + self.generics + } + + /// The complete set of fields in this struct. + #[allow(dead_code)] + pub fn fields(&self) -> &[StructField<'a>] { + &self.fields + } + + /// The cached `bevy_reflect` path. + pub fn bevy_reflect_path(&self) -> &Path { + &self.bevy_reflect_path + } + + /// Returns the `GetTypeRegistration` impl as a `TokenStream`. + pub fn get_type_registration(&self) -> proc_macro2::TokenStream { + crate::registration::impl_get_type_registration( + self.type_name, + &self.bevy_reflect_path, + self.traits.idents(), + self.generics, + ) + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs new file mode 100644 index 0000000000000..a0f3d2b6bc3a0 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs @@ -0,0 +1,76 @@ +//! Contains code related to field attributes for reflected types. +//! +//! A field attribute is an attribute which applies to particular field or variant +//! as opposed to an entire struct or enum. An example of such an attribute is +//! the derive helper attribute for `Reflect`, which looks like: `#[reflect(ignore)]`. + +use crate::REFLECT_ATTRIBUTE_NAME; +use quote::ToTokens; +use syn::spanned::Spanned; +use syn::{Attribute, Meta, NestedMeta}; + +pub(crate) static IGNORE_ATTR: &str = "ignore"; + +/// A container for attributes defined on a field reflected type's field. +#[derive(Default)] +pub(crate) struct ReflectFieldAttr { + /// Determines if this field should be ignored. + pub ignore: bool, +} + +/// Parse all field attributes marked "reflect" (such as `#[reflect(ignore)]`). +pub(crate) fn parse_field_attrs(attrs: &[Attribute]) -> Result { + let mut args = ReflectFieldAttr::default(); + let mut errors: Option = None; + + let attrs = attrs + .iter() + .filter(|a| a.path.is_ident(REFLECT_ATTRIBUTE_NAME)); + for attr in attrs { + let meta = attr.parse_meta()?; + if let Err(err) = parse_meta(&mut args, &meta) { + if let Some(ref mut error) = errors { + error.combine(err); + } else { + errors = Some(err); + } + } + } + + if let Some(error) = errors { + Err(error) + } else { + Ok(args) + } +} + +fn parse_meta(args: &mut ReflectFieldAttr, meta: &Meta) -> Result<(), syn::Error> { + match meta { + Meta::Path(path) if path.is_ident(IGNORE_ATTR) => { + args.ignore = true; + Ok(()) + } + Meta::Path(path) => Err(syn::Error::new( + path.span(), + format!("unknown attribute parameter: {}", path.to_token_stream()), + )), + Meta::NameValue(pair) => { + let path = &pair.path; + Err(syn::Error::new( + path.span(), + format!("unknown attribute parameter: {}", path.to_token_stream()), + )) + } + Meta::List(list) if !list.path.is_ident(REFLECT_ATTRIBUTE_NAME) => { + Err(syn::Error::new(list.path.span(), "unexpected property")) + } + Meta::List(list) => { + for nested in list.nested.iter() { + if let NestedMeta::Meta(meta) = nested { + parse_meta(args, meta)?; + } + } + Ok(()) + } + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs index aabf75d904479..8e9e9c679295b 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -1,130 +1,88 @@ +use crate::ReflectDeriveData; use proc_macro::TokenStream; +use proc_macro2::Span; use quote::quote; -use syn::{Field, Generics, Ident, Index, Member, Path}; +use syn::{Field, Generics, Ident, Index, Lit, LitInt, LitStr, Member, Path}; -pub fn impl_struct( - struct_name: &Ident, +/// Implements `FromReflect` for the given struct +pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { + impl_struct_internal(derive_data, false) +} + +/// Implements `FromReflect` for the given tuple struct +pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream { + impl_struct_internal(derive_data, true) +} + +/// Implements `FromReflect` for the given value type +pub(crate) fn impl_value( + type_name: &Ident, generics: &Generics, bevy_reflect_path: &Path, - active_fields: &[(&Field, usize)], - ignored_fields: &[(&Field, usize)], - custom_constructor: Option, ) -> TokenStream { - let field_names = active_fields - .iter() - .map(|(field, index)| { - field - .ident - .as_ref() - .map(|i| i.to_string()) - .unwrap_or_else(|| index.to_string()) - }) - .collect::>(); - let field_idents = active_fields - .iter() - .map(|(field, index)| { - field - .ident - .as_ref() - .map(|ident| Member::Named(ident.clone())) - .unwrap_or_else(|| Member::Unnamed(Index::from(*index))) - }) - .collect::>(); - - let field_types = active_fields - .iter() - .map(|(field, _index)| field.ty.clone()) - .collect::>(); - let field_count = active_fields.len(); - let ignored_field_idents = ignored_fields - .iter() - .map(|(field, index)| { - field - .ident - .as_ref() - .map(|ident| Member::Named(ident.clone())) - .unwrap_or_else(|| Member::Unnamed(Index::from(*index))) - }) - .collect::>(); - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + TokenStream::from(quote! { + impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause { + fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option { + Some(reflect.any().downcast_ref::<#type_name #ty_generics>()?.clone()) + } + } + }) +} - // Add FromReflect bound for each active field - let mut where_from_reflect_clause = if where_clause.is_some() { - quote! {#where_clause} - } else if field_count > 0 { - quote! {where} +/// Container for a struct's members (field name or index) and their +/// corresponding values. +struct MemberValuePair(Vec, Vec); + +impl MemberValuePair { + pub fn new(items: (Vec, Vec)) -> Self { + Self(items.0, items.1) + } +} + +fn impl_struct_internal(derive_data: &ReflectDeriveData, is_tuple: bool) -> TokenStream { + let struct_name = derive_data.type_name(); + let generics = derive_data.generics(); + let bevy_reflect_path = derive_data.bevy_reflect_path(); + + let ref_struct = Ident::new("__ref_struct", Span::call_site()); + let ref_struct_type = if is_tuple { + Ident::new("TupleStruct", Span::call_site()) } else { - quote! {} + Ident::new("Struct", Span::call_site()) }; - where_from_reflect_clause.extend(quote! { - #(#field_types: #bevy_reflect_path::FromReflect,)* - }); - let constructor = if let Some(constructor) = custom_constructor { + let field_types = derive_data.active_types(); + let MemberValuePair(ignored_members, ignored_values) = + get_ignored_fields(derive_data, is_tuple); + let MemberValuePair(active_members, active_values) = + get_active_fields(derive_data, &ref_struct, &ref_struct_type, is_tuple); + + let constructor = if derive_data.traits().contains("ReflectDefault") { quote!( - let mut value: Self = #constructor; + let mut __this = Self::default(); #( - value.#field_idents = { - <#field_types as #bevy_reflect_path::FromReflect>::from_reflect(#bevy_reflect_path::Struct::field(ref_struct, #field_names)?)? - }; + __this.#active_members = #active_values; )* - Some(value) + Some(__this) ) } else { quote!( Some( Self { - #(#field_idents: { - <#field_types as #bevy_reflect_path::FromReflect>::from_reflect(#bevy_reflect_path::Struct::field(ref_struct, #field_names)?)? - },)* - #(#ignored_field_idents: Default::default(),)* + #(#active_members: #active_values,)* + #(#ignored_members: #ignored_values,)* } ) ) }; - TokenStream::from(quote! { - impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause - { - fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option { - if let #bevy_reflect_path::ReflectRef::Struct(ref_struct) = reflect.reflect_ref() { - #constructor - } else { - None - } - } - } - }) -} - -pub fn impl_tuple_struct( - struct_name: &Ident, - generics: &Generics, - bevy_reflect_path: &Path, - active_fields: &[(&Field, usize)], - ignored_fields: &[(&Field, usize)], -) -> TokenStream { - let field_idents = active_fields - .iter() - .map(|(_field, index)| Member::Unnamed(Index::from(*index))) - .collect::>(); - let field_types = active_fields - .iter() - .map(|(field, _index)| field.ty.clone()) - .collect::>(); - let field_count = active_fields.len(); - let field_indices = (0..field_count).collect::>(); - let ignored_field_idents = ignored_fields - .iter() - .map(|(_field, index)| Member::Unnamed(Index::from(*index))) - .collect::>(); - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + // Add FromReflect bound for each active field let mut where_from_reflect_clause = if where_clause.is_some() { quote! {#where_clause} - } else if field_count > 0 { + } else if !active_members.is_empty() { quote! {where} } else { quote! {} @@ -137,16 +95,8 @@ pub fn impl_tuple_struct( impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause { fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option { - use #bevy_reflect_path::TupleStruct; - if let #bevy_reflect_path::ReflectRef::TupleStruct(ref_tuple_struct) = reflect.reflect_ref() { - Some( - Self{ - #(#field_idents: - <#field_types as #bevy_reflect_path::FromReflect>::from_reflect(ref_tuple_struct.field(#field_indices)?)? - ,)* - #(#ignored_field_idents: Default::default(),)* - } - ) + if let #bevy_reflect_path::ReflectRef::#ref_struct_type(#ref_struct) = reflect.reflect_ref() { + #constructor } else { None } @@ -155,13 +105,78 @@ pub fn impl_tuple_struct( }) } -pub fn impl_value(type_name: &Ident, generics: &Generics, bevy_reflect_path: &Path) -> TokenStream { - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - TokenStream::from(quote! { - impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause { - fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option { - Some(reflect.any().downcast_ref::<#type_name #ty_generics>()?.clone()) - } - } - }) +/// Get the collection of ignored field definitions +fn get_ignored_fields(derive_data: &ReflectDeriveData, is_tuple: bool) -> MemberValuePair { + MemberValuePair::new( + derive_data + .ignored_fields() + .map(|field| { + let member = get_ident(field.data, field.index, is_tuple); + let value = quote! { + Default::default() + }; + + (member, value) + }) + .unzip(), + ) +} + +/// Get the collection of active field definitions +fn get_active_fields( + derive_data: &ReflectDeriveData, + dyn_struct_name: &Ident, + struct_type: &Ident, + is_tuple: bool, +) -> MemberValuePair { + let bevy_reflect_path = derive_data.bevy_reflect_path(); + + MemberValuePair::new( + derive_data + .active_fields() + .map(|field| { + let member = get_ident(field.data, field.index, is_tuple); + let accessor = get_field_accessor(field.data, field.index, is_tuple); + let ty = field.data.ty.clone(); + + let value = quote! { { + <#ty as #bevy_reflect_path::FromReflect>::from_reflect( + // Accesses the field on the given dynamic struct or tuple struct + #bevy_reflect_path::#struct_type::field(#dyn_struct_name, #accessor)? + )? + }}; + + (member, value) + }) + .unzip(), + ) +} + +/// Returns the member for a given field of a struct or tuple struct. +fn get_ident(field: &Field, index: usize, is_tuple: bool) -> Member { + if is_tuple { + Member::Unnamed(Index::from(index)) + } else { + field + .ident + .as_ref() + .map(|ident| Member::Named(ident.clone())) + .unwrap_or_else(|| Member::Unnamed(Index::from(index))) + } +} + +/// Returns the accessor for a given field of a struct or tuple struct. +/// +/// This differs from a member in that it needs to be a number for tuple structs +/// and a string for standard structs. +fn get_field_accessor(field: &Field, index: usize, is_tuple: bool) -> Lit { + if is_tuple { + Lit::Int(LitInt::new(&index.to_string(), Span::call_site())) + } else { + field + .ident + .as_ref() + .map(|ident| Lit::Str(LitStr::new(&ident.to_string(), Span::call_site()))) + .unwrap_or_else(|| Lit::Str(LitStr::new(&index.to_string(), Span::call_site()))) + } } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs new file mode 100644 index 0000000000000..18b4c0ab96e4b --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs @@ -0,0 +1,410 @@ +use crate::container_attributes::ReflectTraits; +use crate::ReflectDeriveData; +use proc_macro::TokenStream; +use proc_macro2::Ident; +use quote::quote; +use syn::{Generics, Index, Member, Path}; + +/// Implements `Struct`, `GetTypeRegistration`, and `Reflect` for the given derive data. +pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { + let bevy_reflect_path = derive_data.bevy_reflect_path(); + let struct_name = derive_data.type_name(); + + let field_names = derive_data + .active_fields() + .map(|field| { + field + .data + .ident + .as_ref() + .map(|i| i.to_string()) + .unwrap_or_else(|| field.index.to_string()) + }) + .collect::>(); + let field_idents = derive_data + .active_fields() + .map(|field| { + field + .data + .ident + .as_ref() + .map(|ident| Member::Named(ident.clone())) + .unwrap_or_else(|| Member::Unnamed(Index::from(field.index))) + }) + .collect::>(); + let field_count = field_idents.len(); + let field_indices = (0..field_count).collect::>(); + + let hash_fn = derive_data + .traits() + .get_hash_impl(bevy_reflect_path) + .unwrap_or_else(|| quote!(None)); + let serialize_fn = derive_data + .traits() + .get_serialize_impl(bevy_reflect_path) + .unwrap_or_else(|| quote!(None)); + let partial_eq_fn = derive_data + .traits() + .get_partial_eq_impl() + .unwrap_or_else(|| { + quote! { + #bevy_reflect_path::struct_partial_eq(self, value) + } + }); + + let get_type_registration_impl = derive_data.get_type_registration(); + let (impl_generics, ty_generics, where_clause) = derive_data.generics().split_for_impl(); + + TokenStream::from(quote! { + #get_type_registration_impl + + impl #impl_generics #bevy_reflect_path::Struct for #struct_name #ty_generics #where_clause { + fn field(&self, name: &str) -> Option<&dyn #bevy_reflect_path::Reflect> { + match name { + #(#field_names => Some(&self.#field_idents),)* + _ => None, + } + } + + fn field_mut(&mut self, name: &str) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + match name { + #(#field_names => Some(&mut self.#field_idents),)* + _ => None, + } + } + + fn field_at(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { + match index { + #(#field_indices => Some(&self.#field_idents),)* + _ => None, + } + } + + fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + match index { + #(#field_indices => Some(&mut self.#field_idents),)* + _ => None, + } + } + + fn name_at(&self, index: usize) -> Option<&str> { + match index { + #(#field_indices => Some(#field_names),)* + _ => None, + } + } + + fn field_len(&self) -> usize { + #field_count + } + + fn iter_fields(&self) -> #bevy_reflect_path::FieldIter { + #bevy_reflect_path::FieldIter::new(self) + } + + fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicStruct { + let mut dynamic = #bevy_reflect_path::DynamicStruct::default(); + dynamic.set_name(self.type_name().to_string()); + #(dynamic.insert_boxed(#field_names, self.#field_idents.clone_value());)* + dynamic + } + } + + // SAFE: any and any_mut both return self + unsafe impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn any(&self) -> &dyn std::any::Any { + self + } + #[inline] + fn any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + #[inline] + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn clone_value(&self) -> Box { + Box::new(#bevy_reflect_path::Struct::clone_dynamic(self)) + } + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + #[inline] + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + if let #bevy_reflect_path::ReflectRef::Struct(struct_value) = value.reflect_ref() { + for (i, value) in struct_value.iter_fields().enumerate() { + let name = struct_value.name_at(i).unwrap(); + #bevy_reflect_path::Struct::field_mut(self, name).map(|v| v.apply(value)); + } + } else { + panic!("Attempted to apply non-struct type to struct type."); + } + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::Struct(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::Struct(self) + } + + fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { + #serialize_fn + } + + fn reflect_hash(&self) -> Option { + #hash_fn + } + + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + #partial_eq_fn + } + } + }) +} + +/// Implements `TupleStruct`, `GetTypeRegistration`, and `Reflect` for the given derive data. +pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream { + let bevy_reflect_path = derive_data.bevy_reflect_path(); + let struct_name = derive_data.type_name(); + let get_type_registration_impl = derive_data.get_type_registration(); + + let field_idents = derive_data + .active_fields() + .map(|field| Member::Unnamed(Index::from(field.index))) + .collect::>(); + let field_count = field_idents.len(); + let field_indices = (0..field_count).collect::>(); + + let hash_fn = derive_data + .traits() + .get_hash_impl(bevy_reflect_path) + .unwrap_or_else(|| quote!(None)); + let serialize_fn = derive_data + .traits() + .get_serialize_impl(bevy_reflect_path) + .unwrap_or_else(|| quote!(None)); + let partial_eq_fn = derive_data + .traits() + .get_partial_eq_impl() + .unwrap_or_else(|| { + quote! { + #bevy_reflect_path::tuple_struct_partial_eq(self, value) + } + }); + + let (impl_generics, ty_generics, where_clause) = derive_data.generics().split_for_impl(); + TokenStream::from(quote! { + #get_type_registration_impl + + impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_name #ty_generics #where_clause { + fn field(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { + match index { + #(#field_indices => Some(&self.#field_idents),)* + _ => None, + } + } + + fn field_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + match index { + #(#field_indices => Some(&mut self.#field_idents),)* + _ => None, + } + } + + fn field_len(&self) -> usize { + #field_count + } + + fn iter_fields(&self) -> #bevy_reflect_path::TupleStructFieldIter { + #bevy_reflect_path::TupleStructFieldIter::new(self) + } + + fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicTupleStruct { + let mut dynamic = #bevy_reflect_path::DynamicTupleStruct::default(); + dynamic.set_name(self.type_name().to_string()); + #(dynamic.insert_boxed(self.#field_idents.clone_value());)* + dynamic + } + } + + // SAFE: any and any_mut both return self + unsafe impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn any(&self) -> &dyn std::any::Any { + self + } + #[inline] + fn any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + #[inline] + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn clone_value(&self) -> Box { + Box::new(#bevy_reflect_path::TupleStruct::clone_dynamic(self)) + } + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + #[inline] + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + if let #bevy_reflect_path::ReflectRef::TupleStruct(struct_value) = value.reflect_ref() { + for (i, value) in struct_value.iter_fields().enumerate() { + #bevy_reflect_path::TupleStruct::field_mut(self, i).map(|v| v.apply(value)); + } + } else { + panic!("Attempted to apply non-TupleStruct type to TupleStruct type."); + } + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::TupleStruct(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::TupleStruct(self) + } + + fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { + #serialize_fn + } + + fn reflect_hash(&self) -> Option { + #hash_fn + } + + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + #partial_eq_fn + } + } + }) +} + +/// Implements `GetTypeRegistration` and `Reflect` for the given type data. +pub(crate) fn impl_value( + type_name: &Ident, + generics: &Generics, + get_type_registration_impl: proc_macro2::TokenStream, + bevy_reflect_path: &Path, + reflect_attrs: &ReflectTraits, +) -> TokenStream { + let hash_fn = reflect_attrs + .get_hash_impl(bevy_reflect_path) + .unwrap_or_else(|| quote!(None)); + let partial_eq_fn = reflect_attrs + .get_partial_eq_impl() + .unwrap_or_else(|| quote!(None)); + let serialize_fn = reflect_attrs + .get_serialize_impl(bevy_reflect_path) + .unwrap_or_else(|| quote!(None)); + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + TokenStream::from(quote! { + #get_type_registration_impl + + // SAFE: any and any_mut both return self + unsafe impl #impl_generics #bevy_reflect_path::Reflect for #type_name #ty_generics #where_clause { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn any(&self) -> &dyn std::any::Any { + self + } + + #[inline] + fn any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + #[inline] + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn clone_value(&self) -> Box { + Box::new(self.clone()) + } + + #[inline] + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + let value = value.any(); + if let Some(value) = value.downcast_ref::() { + *self = value.clone(); + } else { + panic!("Value is not {}.", std::any::type_name::()); + } + } + + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::Value(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::Value(self) + } + + fn reflect_hash(&self) -> Option { + #hash_fn + } + + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + #partial_eq_fn + } + + fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { + #serialize_fn + } + } + }) +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 2b5f93a85edd4..8d59def2c0f9b 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -1,680 +1,121 @@ +//! This crate contains macros used by Bevy's `Reflect` API. +//! +//! The main export of this crate is the derive macro for [`Reflect`]. This allows +//! types to easily implement `Reflect` along with other `bevy_reflect` traits, +//! such as `Struct`, `GetTypeRegistration`, and more— all with a single derive! +//! +//! Some other noteworthy exports include the derive macros for [`FromReflect`] and +//! [`TypeUuid`], as well as the [`reflect_trait`] attribute macro. +//! +//! [`Reflect`]: crate::derive_reflect +//! [`FromReflect`]: crate::derive_from_reflect +//! [`TypeUuid`]: crate::derive_type_uuid +//! [`reflect_trait`]: macro@reflect_trait + extern crate proc_macro; +mod container_attributes; +mod derive_data; +mod field_attributes; mod from_reflect; -mod reflect_trait; +mod impls; +mod reflect_value; +mod registration; +mod trait_reflection; mod type_uuid; +mod utility; -use bevy_macro_utils::BevyManifest; +use crate::derive_data::ReflectDeriveData; +use derive_data::DeriveType; use proc_macro::TokenStream; -use proc_macro2::Span; use quote::quote; -use syn::{ - parenthesized, - parse::{Parse, ParseStream}, - parse_macro_input, - punctuated::Punctuated, - token::{Comma, Paren, Where}, - Data, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Index, Member, Meta, NestedMeta, - Path, -}; - -#[derive(Default)] -struct PropAttributeArgs { - pub ignore: Option, -} +use reflect_value::ReflectValueDef; +use syn::{parse_macro_input, DeriveInput}; -#[derive(Clone)] -enum TraitImpl { - NotImplemented, - Implemented, - Custom(Ident), -} - -impl Default for TraitImpl { - fn default() -> Self { - Self::NotImplemented - } -} - -enum DeriveType { - Struct, - TupleStruct, - UnitStruct, - Value, -} - -static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; -static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; +pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; +pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; #[proc_macro_derive(Reflect, attributes(reflect, reflect_value, module))] pub fn derive_reflect(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); - let unit_struct_punctuated = Punctuated::new(); - let (fields, mut derive_type) = match &ast.data { - Data::Struct(DataStruct { - fields: Fields::Named(fields), - .. - }) => (&fields.named, DeriveType::Struct), - Data::Struct(DataStruct { - fields: Fields::Unnamed(fields), - .. - }) => (&fields.unnamed, DeriveType::TupleStruct), - Data::Struct(DataStruct { - fields: Fields::Unit, - .. - }) => (&unit_struct_punctuated, DeriveType::UnitStruct), - _ => (&unit_struct_punctuated, DeriveType::Value), - }; - let fields_and_args = fields - .iter() - .enumerate() - .map(|(i, f)| { - ( - f, - f.attrs - .iter() - .find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME) - .map(|a| { - syn::custom_keyword!(ignore); - let mut attribute_args = PropAttributeArgs { ignore: None }; - a.parse_args_with(|input: ParseStream| { - if input.parse::>()?.is_some() { - attribute_args.ignore = Some(true); - return Ok(()); - } - Ok(()) - }) - .expect("Invalid 'property' attribute format."); - - attribute_args - }), - i, - ) - }) - .collect::, usize)>>(); - let active_fields = fields_and_args - .iter() - .filter(|(_field, attrs, _i)| { - attrs.is_none() - || match attrs.as_ref().unwrap().ignore { - Some(ignore) => !ignore, - None => true, - } - }) - .map(|(f, _attr, i)| (*f, *i)) - .collect::>(); - - let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); - let type_name = &ast.ident; - - let mut reflect_attrs = ReflectAttrs::default(); - for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { - let meta_list = if let Meta::List(meta_list) = attribute { - meta_list - } else { - continue; - }; - - if let Some(ident) = meta_list.path.get_ident() { - if ident == REFLECT_ATTRIBUTE_NAME { - reflect_attrs = ReflectAttrs::from_nested_metas(&meta_list.nested); - } else if ident == REFLECT_VALUE_ATTRIBUTE_NAME { - derive_type = DeriveType::Value; - reflect_attrs = ReflectAttrs::from_nested_metas(&meta_list.nested); - } - } - } - - let registration_data = &reflect_attrs.data; - let get_type_registration_impl = impl_get_type_registration( - type_name, - &bevy_reflect_path, - registration_data, - &ast.generics, - ); + let derive_data = match ReflectDeriveData::from_input(&ast) { + Ok(data) => data, + Err(err) => return err.into_compile_error().into(), + }; - match derive_type { - DeriveType::Struct | DeriveType::UnitStruct => impl_struct( - type_name, - &ast.generics, - &get_type_registration_impl, - &bevy_reflect_path, - &reflect_attrs, - &active_fields, - ), - DeriveType::TupleStruct => impl_tuple_struct( - type_name, - &ast.generics, - get_type_registration_impl, - &bevy_reflect_path, - &reflect_attrs, - &active_fields, - ), - DeriveType::Value => impl_value( - type_name, - &ast.generics, - get_type_registration_impl, - &bevy_reflect_path, - &reflect_attrs, + match derive_data.derive_type() { + DeriveType::Struct | DeriveType::UnitStruct => impls::impl_struct(&derive_data), + DeriveType::TupleStruct => impls::impl_tuple_struct(&derive_data), + DeriveType::Value => impls::impl_value( + derive_data.type_name(), + derive_data.generics(), + derive_data.get_type_registration(), + derive_data.bevy_reflect_path(), + derive_data.traits(), ), } } -fn impl_struct( - struct_name: &Ident, - generics: &Generics, - get_type_registration_impl: &proc_macro2::TokenStream, - bevy_reflect_path: &Path, - reflect_attrs: &ReflectAttrs, - active_fields: &[(&Field, usize)], -) -> TokenStream { - let field_names = active_fields - .iter() - .map(|(field, index)| { - field - .ident - .as_ref() - .map(|i| i.to_string()) - .unwrap_or_else(|| index.to_string()) - }) - .collect::>(); - let field_idents = active_fields - .iter() - .map(|(field, index)| { - field - .ident - .as_ref() - .map(|ident| Member::Named(ident.clone())) - .unwrap_or_else(|| Member::Unnamed(Index::from(*index))) - }) - .collect::>(); - let field_count = active_fields.len(); - let field_indices = (0..field_count).collect::>(); - - let hash_fn = reflect_attrs.get_hash_impl(bevy_reflect_path); - let serialize_fn = reflect_attrs.get_serialize_impl(bevy_reflect_path); - let partial_eq_fn = match reflect_attrs.reflect_partial_eq { - TraitImpl::NotImplemented => quote! { - use #bevy_reflect_path::Struct; - #bevy_reflect_path::struct_partial_eq(self, value) - }, - TraitImpl::Implemented | TraitImpl::Custom(_) => reflect_attrs.get_partial_eq_impl(), - }; - - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - - TokenStream::from(quote! { - #get_type_registration_impl - - impl #impl_generics #bevy_reflect_path::Struct for #struct_name #ty_generics #where_clause { - fn field(&self, name: &str) -> Option<&dyn #bevy_reflect_path::Reflect> { - match name { - #(#field_names => Some(&self.#field_idents),)* - _ => None, - } - } - - fn field_mut(&mut self, name: &str) -> Option<&mut dyn #bevy_reflect_path::Reflect> { - match name { - #(#field_names => Some(&mut self.#field_idents),)* - _ => None, - } - } - - fn field_at(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { - match index { - #(#field_indices => Some(&self.#field_idents),)* - _ => None, - } - } - - fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { - match index { - #(#field_indices => Some(&mut self.#field_idents),)* - _ => None, - } - } - - fn name_at(&self, index: usize) -> Option<&str> { - match index { - #(#field_indices => Some(#field_names),)* - _ => None, - } - } - - fn field_len(&self) -> usize { - #field_count - } - - fn iter_fields(&self) -> #bevy_reflect_path::FieldIter { - #bevy_reflect_path::FieldIter::new(self) - } - - fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicStruct { - let mut dynamic = #bevy_reflect_path::DynamicStruct::default(); - dynamic.set_name(self.type_name().to_string()); - #(dynamic.insert_boxed(#field_names, self.#field_idents.clone_value());)* - dynamic - } - } - - // SAFE: any and any_mut both return self - unsafe impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause { - #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() - } - - #[inline] - fn any(&self) -> &dyn std::any::Any { - self - } - #[inline] - fn any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - - #[inline] - fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn clone_value(&self) -> Box { - Box::new(#bevy_reflect_path::Struct::clone_dynamic(self)) - } - #[inline] - fn set(&mut self, value: Box) -> Result<(), Box> { - *self = value.take()?; - Ok(()) - } - - #[inline] - fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { - if let #bevy_reflect_path::ReflectRef::Struct(struct_value) = value.reflect_ref() { - for (i, value) in struct_value.iter_fields().enumerate() { - let name = struct_value.name_at(i).unwrap(); - #bevy_reflect_path::Struct::field_mut(self, name).map(|v| v.apply(value)); - } - } else { - panic!("Attempted to apply non-struct type to struct type."); - } - } - - fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { - #bevy_reflect_path::ReflectRef::Struct(self) - } - - fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { - #bevy_reflect_path::ReflectMut::Struct(self) - } - - fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { - #serialize_fn - } - - fn reflect_hash(&self) -> Option { - #hash_fn - } - - fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { - #partial_eq_fn - } - } - }) -} - -fn impl_tuple_struct( - struct_name: &Ident, - generics: &Generics, - get_type_registration_impl: proc_macro2::TokenStream, - bevy_reflect_path: &Path, - reflect_attrs: &ReflectAttrs, - active_fields: &[(&Field, usize)], -) -> TokenStream { - let field_idents = active_fields - .iter() - .map(|(_field, index)| Member::Unnamed(Index::from(*index))) - .collect::>(); - let field_count = active_fields.len(); - let field_indices = (0..field_count).collect::>(); +/// Derives the `FromReflect` trait. +/// +/// This macro supports the following field attributes: +/// * `#[reflect(ignore)]`: Ignores the field. This requires the field to implement [`Default`]. +/// +#[proc_macro_derive(FromReflect, attributes(reflect))] +pub fn derive_from_reflect(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); - let hash_fn = reflect_attrs.get_hash_impl(bevy_reflect_path); - let serialize_fn = reflect_attrs.get_serialize_impl(bevy_reflect_path); - let partial_eq_fn = match reflect_attrs.reflect_partial_eq { - TraitImpl::NotImplemented => quote! { - use #bevy_reflect_path::TupleStruct; - #bevy_reflect_path::tuple_struct_partial_eq(self, value) - }, - TraitImpl::Implemented | TraitImpl::Custom(_) => reflect_attrs.get_partial_eq_impl(), + let derive_data = match ReflectDeriveData::from_input(&ast) { + Ok(data) => data, + Err(err) => return err.into_compile_error().into(), }; - let (impl_generics, ty_generics, _where_clause) = generics.split_for_impl(); - TokenStream::from(quote! { - #get_type_registration_impl - - impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_name #ty_generics { - fn field(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { - match index { - #(#field_indices => Some(&self.#field_idents),)* - _ => None, - } - } - - fn field_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { - match index { - #(#field_indices => Some(&mut self.#field_idents),)* - _ => None, - } - } - - fn field_len(&self) -> usize { - #field_count - } - - fn iter_fields(&self) -> #bevy_reflect_path::TupleStructFieldIter { - #bevy_reflect_path::TupleStructFieldIter::new(self) - } - - fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicTupleStruct { - let mut dynamic = #bevy_reflect_path::DynamicTupleStruct::default(); - dynamic.set_name(self.type_name().to_string()); - #(dynamic.insert_boxed(self.#field_idents.clone_value());)* - dynamic - } - } - - // SAFE: any and any_mut both return self - unsafe impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics { - #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() - } - - #[inline] - fn any(&self) -> &dyn std::any::Any { - self - } - #[inline] - fn any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - - #[inline] - fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn clone_value(&self) -> Box { - use #bevy_reflect_path::TupleStruct; - Box::new(self.clone_dynamic()) - } - #[inline] - fn set(&mut self, value: Box) -> Result<(), Box> { - *self = value.take()?; - Ok(()) - } - - #[inline] - fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { - use #bevy_reflect_path::TupleStruct; - if let #bevy_reflect_path::ReflectRef::TupleStruct(struct_value) = value.reflect_ref() { - for (i, value) in struct_value.iter_fields().enumerate() { - self.field_mut(i).map(|v| v.apply(value)); - } - } else { - panic!("Attempted to apply non-TupleStruct type to TupleStruct type."); - } - } - - fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { - #bevy_reflect_path::ReflectRef::TupleStruct(self) - } - - fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { - #bevy_reflect_path::ReflectMut::TupleStruct(self) - } - - fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { - #serialize_fn - } - - fn reflect_hash(&self) -> Option { - #hash_fn - } - - fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { - #partial_eq_fn - } - } - }) + match derive_data.derive_type() { + DeriveType::Struct | DeriveType::UnitStruct => from_reflect::impl_struct(&derive_data), + DeriveType::TupleStruct => from_reflect::impl_tuple_struct(&derive_data), + DeriveType::Value => from_reflect::impl_value( + derive_data.type_name(), + &ast.generics, + derive_data.bevy_reflect_path(), + ), + } } -fn impl_value( - type_name: &Ident, - generics: &Generics, - get_type_registration_impl: proc_macro2::TokenStream, - bevy_reflect_path: &Path, - reflect_attrs: &ReflectAttrs, -) -> TokenStream { - let hash_fn = reflect_attrs.get_hash_impl(bevy_reflect_path); - let partial_eq_fn = reflect_attrs.get_partial_eq_impl(); - let serialize_fn = reflect_attrs.get_serialize_impl(bevy_reflect_path); - - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - TokenStream::from(quote! { - #get_type_registration_impl - - // SAFE: any and any_mut both return self - unsafe impl #impl_generics #bevy_reflect_path::Reflect for #type_name #ty_generics #where_clause { - #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() - } - - #[inline] - fn any(&self) -> &dyn std::any::Any { - self - } - - #[inline] - fn any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - - #[inline] - fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn clone_value(&self) -> Box { - Box::new(self.clone()) - } - - #[inline] - fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { - let value = value.any(); - if let Some(value) = value.downcast_ref::() { - *self = value.clone(); - } else { - panic!("Value is not {}.", std::any::type_name::()); - } - } - - #[inline] - fn set(&mut self, value: Box) -> Result<(), Box> { - *self = value.take()?; - Ok(()) - } - - fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { - #bevy_reflect_path::ReflectRef::Value(self) - } - - fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { - #bevy_reflect_path::ReflectMut::Value(self) - } - - fn reflect_hash(&self) -> Option { - #hash_fn - } - - fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { - #partial_eq_fn - } - - fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { - #serialize_fn - } - } - }) -} -struct ReflectDef { - type_name: Ident, - generics: Generics, - attrs: Option, +// From https://github.com/randomPoison/type-uuid +#[proc_macro_derive(TypeUuid, attributes(uuid))] +pub fn derive_type_uuid(input: TokenStream) -> TokenStream { + type_uuid::type_uuid_derive(input) } -impl Parse for ReflectDef { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let type_ident = input.parse::()?; - let generics = input.parse::()?; - let mut lookahead = input.lookahead1(); - let mut where_clause = None; - if lookahead.peek(Where) { - where_clause = Some(input.parse()?); - lookahead = input.lookahead1(); - } - - let mut attrs = None; - if lookahead.peek(Paren) { - let content; - parenthesized!(content in input); - attrs = Some(content.parse::()?); - } - - Ok(ReflectDef { - type_name: type_ident, - generics: Generics { - where_clause, - ..generics - }, - attrs, - }) - } +#[proc_macro_attribute] +pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream { + trait_reflection::reflect_trait(&args, input) } #[proc_macro] pub fn impl_reflect_value(input: TokenStream) -> TokenStream { - let reflect_value_def = parse_macro_input!(input as ReflectDef); + let reflect_value_def = parse_macro_input!(input as ReflectValueDef); - let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); + let bevy_reflect_path = utility::get_bevy_reflect_path(); let ty = &reflect_value_def.type_name; - let reflect_attrs = reflect_value_def.attrs.unwrap_or_default(); - let registration_data = &reflect_attrs.data; - let get_type_registration_impl = impl_get_type_registration( + let reflect_traits = reflect_value_def.traits.unwrap_or_default(); + let registration_data = &reflect_traits.idents(); + let get_type_registration_impl = registration::impl_get_type_registration( ty, &bevy_reflect_path, registration_data, &reflect_value_def.generics, ); - impl_value( + impls::impl_value( ty, &reflect_value_def.generics, get_type_registration_impl, &bevy_reflect_path, - &reflect_attrs, + &reflect_traits, ) } -/// Represents the information needed to implement a type as a Reflect Struct. -/// -/// # Example -/// ```ignore -/// impl_reflect_struct!( -/// // attrs -/// // |----------------------------------------| -/// #[reflect(PartialEq, Serialize, Deserialize, Default)] -/// // type_name generics -/// // |-------------------||----------| -/// struct ThingThatImReflecting { -/// x: T1, // | -/// y: T2, // |- fields -/// z: T3 // | -/// } -/// ); -/// ``` -struct ReflectStructDef { - type_name: Ident, - generics: Generics, - attrs: ReflectAttrs, - fields: Fields, -} - -impl Parse for ReflectStructDef { - fn parse(input: ParseStream) -> syn::Result { - let ast = input.parse::()?; - - let type_name = ast.ident; - let generics = ast.generics; - let fields = match ast.data { - Data::Struct(data) => data.fields, - Data::Enum(data) => { - return Err(syn::Error::new_spanned( - data.enum_token, - "Enums are not currently supported for reflection", - )) - } - Data::Union(data) => { - return Err(syn::Error::new_spanned( - data.union_token, - "Unions are not supported for reflection", - )) - } - }; - - let mut attrs = ReflectAttrs::default(); - for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { - let meta_list = if let Meta::List(meta_list) = attribute { - meta_list - } else { - continue; - }; - - if let Some(ident) = meta_list.path.get_ident() { - if ident == REFLECT_ATTRIBUTE_NAME || ident == REFLECT_VALUE_ATTRIBUTE_NAME { - attrs = ReflectAttrs::from_nested_metas(&meta_list.nested); - } - } - } - - Ok(Self { - type_name, - generics, - attrs, - fields, - }) - } -} - /// A replacement for `#[derive(Reflect)]` to be used with foreign types which /// the definitions of cannot be altered. /// @@ -705,96 +146,14 @@ impl Parse for ReflectStructDef { /// ``` #[proc_macro] pub fn impl_reflect_struct(input: TokenStream) -> TokenStream { - let ReflectStructDef { - type_name, - generics, - attrs, - fields, - } = parse_macro_input!(input as ReflectStructDef); - - let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); - - let fields_and_args = fields - .iter() - .enumerate() - .map(|(i, f)| { - ( - f, - f.attrs - .iter() - .find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME) - .map(|a| { - syn::custom_keyword!(ignore); - let mut attribute_args = PropAttributeArgs { ignore: None }; - a.parse_args_with(|input: ParseStream| { - if input.parse::>()?.is_some() { - attribute_args.ignore = Some(true); - return Ok(()); - } - Ok(()) - }) - .expect("Invalid 'property' attribute format."); - - attribute_args - }), - i, - ) - }) - .collect::, usize)>>(); - let active_fields = fields_and_args - .iter() - .filter(|(_field, attrs, _i)| { - attrs.is_none() - || match attrs.as_ref().unwrap().ignore { - Some(ignore) => !ignore, - None => true, - } - }) - .map(|(f, _attr, i)| (*f, *i)) - .collect::>(); - let ignored_fields = fields_and_args - .iter() - .filter(|(_field, attrs, _i)| { - attrs - .as_ref() - .map(|attrs| attrs.ignore.unwrap_or(false)) - .unwrap_or(false) - }) - .map(|(f, _attr, i)| (*f, *i)) - .collect::>(); - - let constructor = if attrs - .data - .contains(&Ident::new("ReflectDefault", Span::call_site())) - { - Some(quote! { Default::default() }) - } else { - None + let ast = parse_macro_input!(input as DeriveInput); + let derive_data = match ReflectDeriveData::from_input(&ast) { + Ok(data) => data, + Err(err) => return err.into_compile_error().into(), }; - let registration_data = &attrs.data; - let get_type_registration_impl = - impl_get_type_registration(&type_name, &bevy_reflect_path, registration_data, &generics); - - let impl_struct: proc_macro2::TokenStream = impl_struct( - &type_name, - &generics, - &get_type_registration_impl, - &bevy_reflect_path, - &attrs, - &active_fields, - ) - .into(); - - let impl_from_struct: proc_macro2::TokenStream = from_reflect::impl_struct( - &type_name, - &generics, - &bevy_reflect_path, - &active_fields, - &ignored_fields, - constructor, - ) - .into(); + let impl_struct: proc_macro2::TokenStream = impls::impl_struct(&derive_data).into(); + let impl_from_struct: proc_macro2::TokenStream = from_reflect::impl_struct(&derive_data).into(); TokenStream::from(quote! { #impl_struct @@ -803,279 +162,11 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream { }) } -#[derive(Default)] -struct ReflectAttrs { - reflect_hash: TraitImpl, - reflect_partial_eq: TraitImpl, - serialize: TraitImpl, - data: Vec, -} - -impl ReflectAttrs { - fn from_nested_metas(nested_metas: &Punctuated) -> Self { - let mut attrs = ReflectAttrs::default(); - for nested_meta in nested_metas.iter() { - match nested_meta { - NestedMeta::Lit(_) => {} - NestedMeta::Meta(meta) => match meta { - Meta::Path(path) => { - if let Some(segment) = path.segments.iter().next() { - let ident = segment.ident.to_string(); - match ident.as_str() { - "PartialEq" => attrs.reflect_partial_eq = TraitImpl::Implemented, - "Hash" => attrs.reflect_hash = TraitImpl::Implemented, - "Serialize" => attrs.serialize = TraitImpl::Implemented, - _ => attrs.data.push(Ident::new( - &format!("Reflect{}", segment.ident), - Span::call_site(), - )), - } - } - } - Meta::List(list) => { - let ident = if let Some(segment) = list.path.segments.iter().next() { - segment.ident.to_string() - } else { - continue; - }; - - if let Some(list_nested) = list.nested.iter().next() { - match list_nested { - NestedMeta::Meta(list_nested_meta) => match list_nested_meta { - Meta::Path(path) => { - if let Some(segment) = path.segments.iter().next() { - match ident.as_str() { - "PartialEq" => { - attrs.reflect_partial_eq = - TraitImpl::Custom(segment.ident.clone()); - } - "Hash" => { - attrs.reflect_hash = - TraitImpl::Custom(segment.ident.clone()); - } - "Serialize" => { - attrs.serialize = - TraitImpl::Custom(segment.ident.clone()); - } - _ => {} - } - } - } - Meta::List(_) => {} - Meta::NameValue(_) => {} - }, - NestedMeta::Lit(_) => {} - } - } - } - Meta::NameValue(_) => {} - }, - } - } - - attrs - } - - fn get_hash_impl(&self, path: &Path) -> proc_macro2::TokenStream { - match &self.reflect_hash { - TraitImpl::Implemented => quote! { - use std::hash::{Hash, Hasher}; - let mut hasher = #path::ReflectHasher::default(); - Hash::hash(&std::any::Any::type_id(self), &mut hasher); - Hash::hash(self, &mut hasher); - Some(hasher.finish()) - }, - TraitImpl::Custom(impl_fn) => quote! { - Some(#impl_fn(self)) - }, - TraitImpl::NotImplemented => quote! { - None - }, - } - } - - fn get_partial_eq_impl(&self) -> proc_macro2::TokenStream { - match &self.reflect_partial_eq { - TraitImpl::Implemented => quote! { - let value = value.any(); - if let Some(value) = value.downcast_ref::() { - Some(std::cmp::PartialEq::eq(self, value)) - } else { - Some(false) - } - }, - TraitImpl::Custom(impl_fn) => quote! { - Some(#impl_fn(self, value)) - }, - TraitImpl::NotImplemented => quote! { - None - }, - } - } - - fn get_serialize_impl(&self, path: &Path) -> proc_macro2::TokenStream { - match &self.serialize { - TraitImpl::Implemented => quote! { - Some(#path::serde::Serializable::Borrowed(self)) - }, - TraitImpl::Custom(impl_fn) => quote! { - Some(#impl_fn(self)) - }, - TraitImpl::NotImplemented => quote! { - None - }, - } - } -} - -impl Parse for ReflectAttrs { - fn parse(input: ParseStream) -> syn::Result { - let result = Punctuated::::parse_terminated(input)?; - Ok(ReflectAttrs::from_nested_metas(&result)) - } -} - -fn impl_get_type_registration( - type_name: &Ident, - bevy_reflect_path: &Path, - registration_data: &[Ident], - generics: &Generics, -) -> proc_macro2::TokenStream { - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - quote! { - #[allow(unused_mut)] - impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_clause { - fn get_type_registration() -> #bevy_reflect_path::TypeRegistration { - let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>(); - #(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)* - registration - } - } - } -} - -// From https://github.com/randomPoison/type-uuid -#[proc_macro_derive(TypeUuid, attributes(uuid))] -pub fn type_uuid_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - type_uuid::type_uuid_derive(input) -} - -#[proc_macro_attribute] -pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream { - reflect_trait::reflect_trait(&args, input) -} - -#[proc_macro_derive(FromReflect)] -pub fn derive_from_reflect(input: TokenStream) -> TokenStream { - let ast = parse_macro_input!(input as DeriveInput); - let unit_struct_punctuated = Punctuated::new(); - let (fields, mut derive_type) = match &ast.data { - Data::Struct(DataStruct { - fields: Fields::Named(fields), - .. - }) => (&fields.named, DeriveType::Struct), - Data::Struct(DataStruct { - fields: Fields::Unnamed(fields), - .. - }) => (&fields.unnamed, DeriveType::TupleStruct), - Data::Struct(DataStruct { - fields: Fields::Unit, - .. - }) => (&unit_struct_punctuated, DeriveType::UnitStruct), - _ => (&unit_struct_punctuated, DeriveType::Value), - }; - - let fields_and_args = fields - .iter() - .enumerate() - .map(|(i, f)| { - ( - f, - f.attrs - .iter() - .find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME) - .map(|a| { - syn::custom_keyword!(ignore); - let mut attribute_args = PropAttributeArgs { ignore: None }; - a.parse_args_with(|input: ParseStream| { - if input.parse::>()?.is_some() { - attribute_args.ignore = Some(true); - return Ok(()); - } - Ok(()) - }) - .expect("Invalid 'property' attribute format."); - - attribute_args - }), - i, - ) - }) - .collect::, usize)>>(); - let active_fields = fields_and_args - .iter() - .filter(|(_field, attrs, _i)| { - attrs.is_none() - || match attrs.as_ref().unwrap().ignore { - Some(ignore) => !ignore, - None => true, - } - }) - .map(|(f, _attr, i)| (*f, *i)) - .collect::>(); - let ignored_fields = fields_and_args - .iter() - .filter(|(_field, attrs, _i)| { - attrs - .as_ref() - .map(|attrs| attrs.ignore.unwrap_or(false)) - .unwrap_or(false) - }) - .map(|(f, _attr, i)| (*f, *i)) - .collect::>(); - - let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); - let type_name = &ast.ident; - - for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { - let meta_list = if let Meta::List(meta_list) = attribute { - meta_list - } else { - continue; - }; - - if let Some(ident) = meta_list.path.get_ident() { - if ident == REFLECT_VALUE_ATTRIBUTE_NAME { - derive_type = DeriveType::Value; - } - } - } - - match derive_type { - DeriveType::Struct | DeriveType::UnitStruct => from_reflect::impl_struct( - type_name, - &ast.generics, - &bevy_reflect_path, - &active_fields, - &ignored_fields, - None, - ), - DeriveType::TupleStruct => from_reflect::impl_tuple_struct( - type_name, - &ast.generics, - &bevy_reflect_path, - &active_fields, - &ignored_fields, - ), - DeriveType::Value => from_reflect::impl_value(type_name, &ast.generics, &bevy_reflect_path), - } -} - #[proc_macro] pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream { - let reflect_value_def = parse_macro_input!(input as ReflectDef); + let reflect_value_def = parse_macro_input!(input as ReflectValueDef); - let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); + let bevy_reflect_path = utility::get_bevy_reflect_path(); let ty = &reflect_value_def.type_name; from_reflect::impl_value(ty, &reflect_value_def.generics, &bevy_reflect_path) } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs new file mode 100644 index 0000000000000..ec54b99a6f404 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs @@ -0,0 +1,54 @@ +use crate::container_attributes::ReflectTraits; +use proc_macro2::Ident; +use syn::parse::{Parse, ParseStream}; +use syn::token::{Paren, Where}; +use syn::{parenthesized, Generics}; + +/// A struct used to define a simple reflected value type (such as primitives). +/// +/// This takes the form: +/// +/// ```ignore +/// // Standard +/// foo(TraitA, TraitB) +/// +/// // With generics +/// foo(TraitA, TraitB) +/// +/// // With generics and where clause +/// foo where T1: Bar (TraitA, TraitB) +/// ``` +pub(crate) struct ReflectValueDef { + pub type_name: Ident, + pub generics: Generics, + pub traits: Option, +} + +impl Parse for ReflectValueDef { + fn parse(input: ParseStream) -> syn::Result { + let type_ident = input.parse::()?; + let generics = input.parse::()?; + let mut lookahead = input.lookahead1(); + let mut where_clause = None; + if lookahead.peek(Where) { + where_clause = Some(input.parse()?); + lookahead = input.lookahead1(); + } + + let mut traits = None; + if lookahead.peek(Paren) { + let content; + parenthesized!(content in input); + traits = Some(content.parse::()?); + } + + Ok(ReflectValueDef { + type_name: type_ident, + generics: Generics { + where_clause, + ..generics + }, + traits, + }) + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs new file mode 100644 index 0000000000000..ea3157242211e --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs @@ -0,0 +1,25 @@ +//! Contains code related specifically to Bevy's type registration. + +use proc_macro2::Ident; +use quote::quote; +use syn::{Generics, Path}; + +/// Creates the `GetTypeRegistration` impl for the given type data. +pub(crate) fn impl_get_type_registration( + type_name: &Ident, + bevy_reflect_path: &Path, + registration_data: &[Ident], + generics: &Generics, +) -> proc_macro2::TokenStream { + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + quote! { + #[allow(unused_mut)] + impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_clause { + fn get_type_registration() -> #bevy_reflect_path::TypeRegistration { + let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>(); + #(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)* + registration + } + } + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs b/crates/bevy_reflect/bevy_reflect_derive/src/trait_reflection.rs similarity index 87% rename from crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs rename to crates/bevy_reflect/bevy_reflect_derive/src/trait_reflection.rs index c3632c2d3ef49..fe4d4a6bb257e 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_trait.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/trait_reflection.rs @@ -1,10 +1,9 @@ use bevy_macro_utils::BevyManifest; use proc_macro::TokenStream; -use proc_macro2::Span; use quote::quote; -use syn::{parse::Parse, parse_macro_input, Attribute, Ident, ItemTrait, Token}; +use syn::{parse::Parse, parse_macro_input, Attribute, ItemTrait, Token}; -pub struct TraitInfo { +pub(crate) struct TraitInfo { item_trait: ItemTrait, } @@ -22,13 +21,12 @@ impl Parse for TraitInfo { } } -pub fn reflect_trait(_args: &TokenStream, input: TokenStream) -> TokenStream { +pub(crate) fn reflect_trait(_args: &TokenStream, input: TokenStream) -> TokenStream { let trait_info = parse_macro_input!(input as TraitInfo); let item_trait = &trait_info.item_trait; let trait_ident = &item_trait.ident; let trait_vis = &item_trait.vis; - let reflect_trait_ident = - Ident::new(&format!("Reflect{}", item_trait.ident), Span::call_site()); + let reflect_trait_ident = crate::utility::get_reflect_ident(&item_trait.ident.to_string()); let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect"); TokenStream::from(quote! { #item_trait diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/type_uuid.rs b/crates/bevy_reflect/bevy_reflect_derive/src/type_uuid.rs index 382036a9f3713..8adb2dbcdad7e 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/type_uuid.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/type_uuid.rs @@ -5,7 +5,7 @@ use quote::quote; use syn::*; use uuid::Uuid; -pub fn type_uuid_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { +pub(crate) fn type_uuid_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { // Construct a representation of Rust code as a syntax tree // that we can manipulate let mut ast: DeriveInput = syn::parse(input).unwrap(); diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs new file mode 100644 index 0000000000000..34fbdf186cc94 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -0,0 +1,23 @@ +//! General-purpose utility functions for internal usage within this crate. + +use bevy_macro_utils::BevyManifest; +use proc_macro2::{Ident, Span}; +use syn::Path; + +/// Returns the correct path for `bevy_reflect`. +pub(crate) fn get_bevy_reflect_path() -> Path { + BevyManifest::get_path_direct("bevy_reflect") +} + +/// Returns the "reflected" ident for a given string. +/// +/// # Example +/// +/// ```ignore +/// let reflected: Ident = get_reflect_ident("Hash"); +/// assert_eq!("ReflectHash", reflected.to_string()); +/// ``` +pub(crate) fn get_reflect_ident(name: &str) -> Ident { + let reflected = format!("Reflect{}", name); + Ident::new(&reflected, Span::call_site()) +} diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index d8f7b42d8e80a..90def413de98b 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -85,7 +85,6 @@ mod tests { #[cfg(feature = "glam")] use ::glam::{vec3, Vec3}; use ::serde::de::DeserializeSeed; - use ::serde::Serialize; use bevy_utils::HashMap; use ron::{ ser::{to_string_pretty, PrettyConfig}, @@ -478,6 +477,7 @@ mod tests { #[cfg(feature = "glam")] mod glam { use super::*; + use ::serde::Serialize; #[test] fn vec3_serialization() { From e4af3b273b4b566cc4ef004c2f43df3487ebfe30 Mon Sep 17 00:00:00 2001 From: James Liu Date: Fri, 13 May 2022 00:33:13 +0000 Subject: [PATCH 08/26] Profile par_for_each(_mut) tasks (#4711) # Objective `Query::par_for_each` and it's variants do not show up when profiling using `tracy` or other profilers. Failing to show the impact of changing batch size, the overhead of scheduling tasks, overall thread utilization, etc. other than the effect on the surrounding system. ## Solution Add a child span that is entered on every spawned task. Example view of the results in `tracy` using a modified `parallel_query`: ![image](https://user-images.githubusercontent.com/3137680/167560036-626bd091-344b-4664-b323-b692f4f16084.png) --- ## Changelog Added: `tracing` spans for `Query::par_for_each` and its variants. Spans should now be visible for all --- crates/bevy_ecs/src/query/state.rs | 36 +++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index e01d8df5f2b35..ae23c1c512e8e 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -11,6 +11,8 @@ use crate::{ world::{World, WorldId}, }; use bevy_tasks::TaskPool; +#[cfg(feature = "trace")] +use bevy_utils::tracing::Instrument; use fixedbitset::FixedBitSet; use std::fmt; @@ -856,7 +858,8 @@ impl QueryState { let mut offset = 0; while offset < table.len() { let func = func.clone(); - scope.spawn(async move { + let len = batch_size.min(table.len() - offset); + let task = async move { let mut fetch = QF::init(world, &self.fetch_state, last_change_tick, change_tick); let mut filter = as Fetch>::init( @@ -869,7 +872,6 @@ impl QueryState { let table = &tables[*table_id]; fetch.set_table(&self.fetch_state, table); filter.set_table(&self.filter_state, table); - let len = batch_size.min(table.len() - offset); for table_index in offset..offset + len { if !filter.table_filter_fetch(table_index) { continue; @@ -877,7 +879,17 @@ impl QueryState { let item = fetch.table_fetch(table_index); func(item); } - }); + }; + #[cfg(feature = "trace")] + let span = bevy_utils::tracing::info_span!( + "par_for_each", + query = std::any::type_name::(), + filter = std::any::type_name::(), + count = len, + ); + #[cfg(feature = "trace")] + let task = task.instrument(span); + scope.spawn(task); offset += batch_size; } } @@ -888,7 +900,8 @@ impl QueryState { let archetype = &archetypes[*archetype_id]; while offset < archetype.len() { let func = func.clone(); - scope.spawn(async move { + let len = batch_size.min(archetype.len() - offset); + let task = async move { let mut fetch = QF::init(world, &self.fetch_state, last_change_tick, change_tick); let mut filter = as Fetch>::init( @@ -902,14 +915,25 @@ impl QueryState { fetch.set_archetype(&self.fetch_state, archetype, tables); filter.set_archetype(&self.filter_state, archetype, tables); - let len = batch_size.min(archetype.len() - offset); for archetype_index in offset..offset + len { if !filter.archetype_filter_fetch(archetype_index) { continue; } func(fetch.archetype_fetch(archetype_index)); } - }); + }; + + #[cfg(feature = "trace")] + let span = bevy_utils::tracing::info_span!( + "par_for_each", + query = std::any::type_name::(), + filter = std::any::type_name::(), + count = len, + ); + #[cfg(feature = "trace")] + let task = task.instrument(span); + + scope.spawn(task); offset += batch_size; } } From 3e493d3f93dfbaab47f8dabc39e07db8f807955a Mon Sep 17 00:00:00 2001 From: Charles Date: Fri, 13 May 2022 00:57:04 +0000 Subject: [PATCH 09/26] Add a clear() method to the EventReader that consumes the iterator (#4693) # Objective - It's pretty common to want to check if an EventReader has received one or multiple events while also needing to consume the iterator to "clear" the EventReader. - The current approach is to do something like `events.iter().count() > 0` or `events.iter().last().is_some()`. It's not immediately obvious that the purpose of that is to consume the events and check if there were any events. My solution doesn't really solve that part, but it encapsulates the pattern. ## Solution - Add a `.clear()` method that consumes the iterator. - It takes the EventReader by value to make sure it isn't used again after it has been called. --- ## Migration Guide Not a breaking change, but if you ever found yourself in a situation where you needed to consume the EventReader and check if there was any events you can now use ```rust fn system(events: EventReader) { if !events.is_empty { events.clear(); // Process the fact that one or more event was received } } ``` Co-authored-by: Charles --- crates/bevy_ecs/src/event.rs | 59 +++++++++++++++++++++++++++++++++++- examples/games/breakout.rs | 9 +++--- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 00e0c6b74607e..22af1b3ed2dff 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -210,10 +210,42 @@ impl<'w, 's, E: Event> EventReader<'w, 's, E> { self.reader.len(&self.events) } - /// Determines if are any events available to be read without consuming any. + /// Determines if no events are available to be read without consuming any. + /// If you need to consume the iterator you can use [`EventReader::clear`]. + /// + /// # Example + /// + /// The following example shows a common pattern of this function in conjunction with `clear` + /// to avoid leaking events to the next schedule iteration while also checking if it was emitted. + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// # + /// struct CollisionEvent; + /// + /// fn play_collision_sound(events: EventReader) { + /// if !events.is_empty() { + /// events.clear(); + /// // Play a sound + /// } + /// } + /// # bevy_ecs::system::assert_is_system(play_collision_sound); + /// ``` pub fn is_empty(&self) -> bool { self.len() == 0 } + + /// Consumes the iterator. + /// + /// This means all currently available events will be removed before the next frame. + /// This is useful when multiple events are sent in a single frame and you want + /// to react to one or more events without needing to know how many were sent. + /// In those situations you generally want to consume those events to make sure they don't appear in the next frame. + /// + /// For more information see [`EventReader::is_empty()`]. + pub fn clear(mut self) { + self.iter().last(); + } } /// Sends events of type `T`. @@ -727,6 +759,31 @@ mod tests { assert!(reader.is_empty(&events)); } + #[test] + fn test_event_reader_clear() { + use bevy_ecs::prelude::*; + + let mut world = World::new(); + let mut events = Events::::default(); + events.send(TestEvent { i: 0 }); + world.insert_resource(events); + + let mut reader = IntoSystem::into_system(|events: EventReader| -> bool { + if !events.is_empty() { + events.clear(); + false + } else { + true + } + }); + reader.initialize(&mut world); + + let is_empty = reader.run((), &mut world); + assert!(!is_empty, "EventReader should not be empty"); + let is_empty = reader.run((), &mut world); + assert!(is_empty, "EventReader should be empty"); + } + #[derive(Clone, PartialEq, Debug, Default)] struct EmptyTestEvent; diff --git a/examples/games/breakout.rs b/examples/games/breakout.rs index 06d091f76d93c..4c777fe325abd 100644 --- a/examples/games/breakout.rs +++ b/examples/games/breakout.rs @@ -411,13 +411,14 @@ fn check_for_collisions( } fn play_collision_sound( - mut collision_events: EventReader, + collision_events: EventReader, audio: Res