From c53792626d4225e07c876c45e6e081737d11e592 Mon Sep 17 00:00:00 2001 From: aecsocket Date: Mon, 18 Nov 2024 16:40:31 +0000 Subject: [PATCH 1/3] 0.15: `ReflectDe/SerializerProcessor` --- .../15482_reflect_serde_processors.md | 102 ++++++++++++++++++ .../0.15/release-notes/_release-notes.toml | 59 +++++----- 2 files changed, 135 insertions(+), 26 deletions(-) create mode 100644 release-content/0.15/release-notes/15482_reflect_serde_processors.md diff --git a/release-content/0.15/release-notes/15482_reflect_serde_processors.md b/release-content/0.15/release-notes/15482_reflect_serde_processors.md new file mode 100644 index 0000000000..31afbd0b9c --- /dev/null +++ b/release-content/0.15/release-notes/15482_reflect_serde_processors.md @@ -0,0 +1,102 @@ + + + +Alongside `SerializeWithRegistry` and `DeserializeWithRegistry`, a new tool has been added for users +who use the reflect machinery for de/serialization. When using the `ReflectSerializer` or +`ReflectDeserializer`, you can now use `with_processor` and pass in a *de/serializer processor*. +This processor allows you to override the de/serialization logic for specific values and specific +types, while also capturing any context you might need inside the processor itself. + +The motivating example for this is being able to deserialize `Handle`s properly inside an asset +loader when reflect-deserializing. Let's imagine that we have an asset that looks like this: + +```rust +#[derive(Debug, Clone, Reflect)] +struct AnimationGraph { + nodes: Vec>, +} + +trait AnimationNode: Send + Sync + Reflect { /* .. */ } + +#[derive(Debug, Clone, Reflect)] +struct ClipNode { + clip: Handle +} + +impl AnimationNode for ClipNode { /* .. */ } + +#[derive(Debug, Clone, Reflect)] +struct AdjustSpeedNode { + speed_multiplier: f32, +} + +impl AnimationNode for AdjustSpeedNode { /* .. */ } +``` + +```ron +( + animation_graph: ( + nodes: [ + { + "my_app::animation::node::ClipNode": ( + clip: "animations/run.anim.ron", + ) + }, + { + "my_app::animation::node::AdjustSpeedNode": ( + speed_multiplier: 1.5, + ) + } + ] + ) +) +``` + +When we write an `AssetLoader` for this `AnimationGraph`, we have access to a `&mut LoadContext` +which we can use to start new asset load operations, and get a `Handle` to that asset. We can also +use the existing `ReflectDeserializer` to deserialize `Box`s. However, when the +deserializer encounters a `Handle`, this will be deserialized as `Handle::default` +and no asset load will be kicked off, making the handle useless. + +With a `ReflectDeserializerProcessor`, we can pass in a processor which captures the +`&mut LoadContext` and, if it encounters a `Handle`, it will kick off an asset load for `T`, +and assigns the result of that load to the field it's deserializing. + +```rust +struct HandleProcessor<'a> { + load_context: &'a mut LoadContext, +} + +impl ReflectDeserializerProcessor for HandleProcessor<'_> { + fn try_deserialize<'de, D>( + &mut self, + registration: &TypeRegistration, + _registry: &TypeRegistry, + deserializer: D, + ) -> Result, D>, D::Error> + where + D: Deserializer<'de>, + { + let Some(reflect_handle) = registration.data::() else { + // we don't want to deserialize this - give the deserializer back + // and do default deserialization logic + return Ok(Err(deserializer)); + }; + + let asset_type_id = reflect_handle.asset_type_id(); + let asset_path = deserializer.deserialize_str(AssetPathVisitor)?; + + let handle: Handle = self.load_context + .loader() + .with_dynamic_type(asset_type_id) + .load(asset_path); + Ok(Box::new(handle)) + } +} +``` + +Combined with `ReflectSerializerProcessor`, this can be used to round-trip `Handle`s to/from string +asset paths. + +The processors take priority over all other serde logic, including `De/SerializeWithRegistry`, so it +can be used to override any reflect serialization logic. diff --git a/release-content/0.15/release-notes/_release-notes.toml b/release-content/0.15/release-notes/_release-notes.toml index 69a5e664f6..eefffe1bc4 100644 --- a/release-content/0.15/release-notes/_release-notes.toml +++ b/release-content/0.15/release-notes/_release-notes.toml @@ -2,12 +2,12 @@ title = "Cosmic text" authors = ["@tigregalis", "@TotalKrill"] contributors = [ - "@tigregalis", - "@alice-i-cecile", - "@nicoburns", - "@rparrett", - "@Dimchikkk", - "@bytemunch", + "@tigregalis", + "@alice-i-cecile", + "@nicoburns", + "@rparrett", + "@Dimchikkk", + "@bytemunch", ] prs = [10193] file_name = "10193_Cosmic_text.md" @@ -233,10 +233,10 @@ file_name = "15184_Support_systems_that_take_references_as_input.md" title = "Initial implementation of the Bevy Remote Protocol (Adopted)" authors = ["@mweatherley"] contributors = [ - "@DragonGamesStudios", - "@MrGVSV", - "@ChristopherBiscardi", - "@pcwalton", + "@DragonGamesStudios", + "@MrGVSV", + "@ChristopherBiscardi", + "@pcwalton", ] prs = [14880] file_name = "14880_Initial_implementation_of_the_Bevy_Remote_Protocol_Adopted.md" @@ -288,22 +288,22 @@ title = "Function reflection" authors = ["@MrGVSV", "@nixpulvis", "@hooded-shrimp"] contributors = [] prs = [ - 13152, - 14098, - 14141, - 14174, - 14201, - 14641, - 14647, - 14666, - 14704, - 14813, - 15086, - 15145, - 15147, - 15148, - 15205, - 15484, + 13152, + 14098, + 14141, + 14174, + 14201, + 14641, + 14647, + 14666, + 14704, + 14813, + 15086, + 15145, + 15147, + 15148, + 15205, + 15484, ] file_name = "function_reflection.md" @@ -404,3 +404,10 @@ authors = ["@tychedelia"] contributors = ["@IceSentry"] prs = [14663] file_name = "14663_shader_storage_buffer.md" + +[[release_notes]] +title = "Reflect de/serializer processors" +authors = ["@aecsocket"] +contributors = ["@aecsocket"] +prs = [15482, 15548] +file_name = "15482_reflect_serde_processors.md" From f6a6cc20219ec5a3fdf4879c4511d0f93703c4ed Mon Sep 17 00:00:00 2001 From: aecsocket Date: Mon, 18 Nov 2024 16:42:37 +0000 Subject: [PATCH 2/3] fix diff --- .../0.15/release-notes/_release-notes.toml | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/release-content/0.15/release-notes/_release-notes.toml b/release-content/0.15/release-notes/_release-notes.toml index eefffe1bc4..e672eb98e5 100644 --- a/release-content/0.15/release-notes/_release-notes.toml +++ b/release-content/0.15/release-notes/_release-notes.toml @@ -2,12 +2,12 @@ title = "Cosmic text" authors = ["@tigregalis", "@TotalKrill"] contributors = [ - "@tigregalis", - "@alice-i-cecile", - "@nicoburns", - "@rparrett", - "@Dimchikkk", - "@bytemunch", + "@tigregalis", + "@alice-i-cecile", + "@nicoburns", + "@rparrett", + "@Dimchikkk", + "@bytemunch", ] prs = [10193] file_name = "10193_Cosmic_text.md" @@ -233,10 +233,10 @@ file_name = "15184_Support_systems_that_take_references_as_input.md" title = "Initial implementation of the Bevy Remote Protocol (Adopted)" authors = ["@mweatherley"] contributors = [ - "@DragonGamesStudios", - "@MrGVSV", - "@ChristopherBiscardi", - "@pcwalton", + "@DragonGamesStudios", + "@MrGVSV", + "@ChristopherBiscardi", + "@pcwalton", ] prs = [14880] file_name = "14880_Initial_implementation_of_the_Bevy_Remote_Protocol_Adopted.md" @@ -288,22 +288,22 @@ title = "Function reflection" authors = ["@MrGVSV", "@nixpulvis", "@hooded-shrimp"] contributors = [] prs = [ - 13152, - 14098, - 14141, - 14174, - 14201, - 14641, - 14647, - 14666, - 14704, - 14813, - 15086, - 15145, - 15147, - 15148, - 15205, - 15484, + 13152, + 14098, + 14141, + 14174, + 14201, + 14641, + 14647, + 14666, + 14704, + 14813, + 15086, + 15145, + 15147, + 15148, + 15205, + 15484, ] file_name = "function_reflection.md" From d25b728149047214ca8e84eec1fcfe76411b274d Mon Sep 17 00:00:00 2001 From: aecsocket Date: Tue, 19 Nov 2024 14:52:29 +0000 Subject: [PATCH 3/3] merge processor notes into De/SerializeWithRegistry --- .../15482_reflect_serde_processors.md | 102 ----------------- ...erializeWithRegistry_and_SerializeWithR.md | 104 ++++++++++++++++++ .../0.15/release-notes/_release-notes.toml | 11 +- 3 files changed, 106 insertions(+), 111 deletions(-) delete mode 100644 release-content/0.15/release-notes/15482_reflect_serde_processors.md diff --git a/release-content/0.15/release-notes/15482_reflect_serde_processors.md b/release-content/0.15/release-notes/15482_reflect_serde_processors.md deleted file mode 100644 index 31afbd0b9c..0000000000 --- a/release-content/0.15/release-notes/15482_reflect_serde_processors.md +++ /dev/null @@ -1,102 +0,0 @@ - - - -Alongside `SerializeWithRegistry` and `DeserializeWithRegistry`, a new tool has been added for users -who use the reflect machinery for de/serialization. When using the `ReflectSerializer` or -`ReflectDeserializer`, you can now use `with_processor` and pass in a *de/serializer processor*. -This processor allows you to override the de/serialization logic for specific values and specific -types, while also capturing any context you might need inside the processor itself. - -The motivating example for this is being able to deserialize `Handle`s properly inside an asset -loader when reflect-deserializing. Let's imagine that we have an asset that looks like this: - -```rust -#[derive(Debug, Clone, Reflect)] -struct AnimationGraph { - nodes: Vec>, -} - -trait AnimationNode: Send + Sync + Reflect { /* .. */ } - -#[derive(Debug, Clone, Reflect)] -struct ClipNode { - clip: Handle -} - -impl AnimationNode for ClipNode { /* .. */ } - -#[derive(Debug, Clone, Reflect)] -struct AdjustSpeedNode { - speed_multiplier: f32, -} - -impl AnimationNode for AdjustSpeedNode { /* .. */ } -``` - -```ron -( - animation_graph: ( - nodes: [ - { - "my_app::animation::node::ClipNode": ( - clip: "animations/run.anim.ron", - ) - }, - { - "my_app::animation::node::AdjustSpeedNode": ( - speed_multiplier: 1.5, - ) - } - ] - ) -) -``` - -When we write an `AssetLoader` for this `AnimationGraph`, we have access to a `&mut LoadContext` -which we can use to start new asset load operations, and get a `Handle` to that asset. We can also -use the existing `ReflectDeserializer` to deserialize `Box`s. However, when the -deserializer encounters a `Handle`, this will be deserialized as `Handle::default` -and no asset load will be kicked off, making the handle useless. - -With a `ReflectDeserializerProcessor`, we can pass in a processor which captures the -`&mut LoadContext` and, if it encounters a `Handle`, it will kick off an asset load for `T`, -and assigns the result of that load to the field it's deserializing. - -```rust -struct HandleProcessor<'a> { - load_context: &'a mut LoadContext, -} - -impl ReflectDeserializerProcessor for HandleProcessor<'_> { - fn try_deserialize<'de, D>( - &mut self, - registration: &TypeRegistration, - _registry: &TypeRegistry, - deserializer: D, - ) -> Result, D>, D::Error> - where - D: Deserializer<'de>, - { - let Some(reflect_handle) = registration.data::() else { - // we don't want to deserialize this - give the deserializer back - // and do default deserialization logic - return Ok(Err(deserializer)); - }; - - let asset_type_id = reflect_handle.asset_type_id(); - let asset_path = deserializer.deserialize_str(AssetPathVisitor)?; - - let handle: Handle = self.load_context - .loader() - .with_dynamic_type(asset_type_id) - .load(asset_path); - Ok(Box::new(handle)) - } -} -``` - -Combined with `ReflectSerializerProcessor`, this can be used to round-trip `Handle`s to/from string -asset paths. - -The processors take priority over all other serde logic, including `De/SerializeWithRegistry`, so it -can be used to override any reflect serialization logic. diff --git a/release-content/0.15/release-notes/8611_bevy_reflect_Add_DeserializeWithRegistry_and_SerializeWithR.md b/release-content/0.15/release-notes/8611_bevy_reflect_Add_DeserializeWithRegistry_and_SerializeWithR.md index f919d56ee5..7a0ebe85f0 100644 --- a/release-content/0.15/release-notes/8611_bevy_reflect_Add_DeserializeWithRegistry_and_SerializeWithR.md +++ b/release-content/0.15/release-notes/8611_bevy_reflect_Add_DeserializeWithRegistry_and_SerializeWithR.md @@ -1,4 +1,108 @@ +#### Serialization with registry context + + +#### Reflect de/serializer processors + +Alongside `SerializeWithRegistry` and `DeserializeWithRegistry`, a new tool has been added for users +who use the reflect machinery for de/serialization. When using the `ReflectSerializer` or +`ReflectDeserializer`, you can now use `with_processor` and pass in a *de/serializer processor*. +This processor allows you to override the de/serialization logic for specific values and specific +types, while also capturing any context you might need inside the processor itself. + +The motivating example for this is being able to deserialize `Handle`s properly inside an asset +loader when reflect-deserializing. Let's imagine that we have an asset that looks like this: + +```rust +#[derive(Debug, Clone, Reflect)] +struct AnimationGraph { + nodes: Vec>, +} + +trait AnimationNode: Send + Sync + Reflect { /* .. */ } + +#[derive(Debug, Clone, Reflect)] +struct ClipNode { + clip: Handle +} + +impl AnimationNode for ClipNode { /* .. */ } + +#[derive(Debug, Clone, Reflect)] +struct AdjustSpeedNode { + speed_multiplier: f32, +} + +impl AnimationNode for AdjustSpeedNode { /* .. */ } +``` + +```ron +( + animation_graph: ( + nodes: [ + { + "my_app::animation::node::ClipNode": ( + clip: "animations/run.anim.ron", + ) + }, + { + "my_app::animation::node::AdjustSpeedNode": ( + speed_multiplier: 1.5, + ) + } + ] + ) +) +``` + +When we write an `AssetLoader` for this `AnimationGraph`, we have access to a `&mut LoadContext` +which we can use to start new asset load operations, and get a `Handle` to that asset. We can also +use the existing `ReflectDeserializer` to deserialize `Box`s. However, when the +deserializer encounters a `Handle`, this will be deserialized as `Handle::default` +and no asset load will be kicked off, making the handle useless. + +With a `ReflectDeserializerProcessor`, we can pass in a processor which captures the +`&mut LoadContext` and, if it encounters a `Handle`, it will kick off an asset load for `T`, +and assigns the result of that load to the field it's deserializing. + +```rust +struct HandleProcessor<'a> { + load_context: &'a mut LoadContext, +} + +impl ReflectDeserializerProcessor for HandleProcessor<'_> { + fn try_deserialize<'de, D>( + &mut self, + registration: &TypeRegistration, + _registry: &TypeRegistry, + deserializer: D, + ) -> Result, D>, D::Error> + where + D: Deserializer<'de>, + { + let Some(reflect_handle) = registration.data::() else { + // we don't want to deserialize this - give the deserializer back + // and do default deserialization logic + return Ok(Err(deserializer)); + }; + + let asset_type_id = reflect_handle.asset_type_id(); + let asset_path = deserializer.deserialize_str(AssetPathVisitor)?; + + let handle: Handle = self.load_context + .loader() + .with_dynamic_type(asset_type_id) + .load(asset_path); + Ok(Box::new(handle)) + } +} +``` + +Combined with `ReflectSerializerProcessor`, this can be used to round-trip `Handle`s to/from string +asset paths. + +The processors take priority over all other serde logic, including `De/SerializeWithRegistry`, so it +can be used to override any reflect serialization logic. diff --git a/release-content/0.15/release-notes/_release-notes.toml b/release-content/0.15/release-notes/_release-notes.toml index e672eb98e5..2b7579cbad 100644 --- a/release-content/0.15/release-notes/_release-notes.toml +++ b/release-content/0.15/release-notes/_release-notes.toml @@ -330,9 +330,9 @@ file_name = "12095_Add_features_to_switch_NativeActivity_and_GameActivity_usa.md [[release_notes]] title = "bevy_reflect: Add `DeserializeWithRegistry` and `SerializeWithRegistry`" -authors = ["@MrGVSV"] +authors = ["@MrGVSV", "@aecsocket"] contributors = ["@aecsocket", "@alice-i-cecile"] -prs = [8611] +prs = [8611, 15482, 15548] file_name = "8611_bevy_reflect_Add_DeserializeWithRegistry_and_SerializeWithR.md" [[release_notes]] @@ -404,10 +404,3 @@ authors = ["@tychedelia"] contributors = ["@IceSentry"] prs = [14663] file_name = "14663_shader_storage_buffer.md" - -[[release_notes]] -title = "Reflect de/serializer processors" -authors = ["@aecsocket"] -contributors = ["@aecsocket"] -prs = [15482, 15548] -file_name = "15482_reflect_serde_processors.md"