bevy_image/
serialized_image.rs

1use crate::{Image, ImageSampler};
2use bevy_asset::RenderAssetUsages;
3use core::fmt::Debug;
4use serde::{Deserialize, Serialize};
5use wgpu_types::{
6    TextureAspect, TextureDataOrder, TextureDescriptor, TextureFormat, TextureUsages,
7    TextureViewDescriptor, TextureViewDimension,
8};
9
10/// A version of [`Image`] suitable for serializing for short-term transfer.
11///
12/// [`Image`] does not implement [`Serialize`] / [`Deserialize`] because it is made with the renderer in mind.
13/// It is not a general-purpose image implementation, and its internals are subject to frequent change.
14/// As such, storing an [`Image`] on disk is highly discouraged.
15/// Use an existing image asset format such as `.png` instead!
16///
17/// But there are still some valid use cases for serializing an [`Image`], namely transferring images between processes.
18/// To support this, you can create a [`SerializedImage`] from an [`Image`] with [`SerializedImage::from_image`],
19/// and then deserialize it with [`SerializedImage::into_image`].
20///
21/// The caveats are:
22/// - The image representation is not valid across different versions of Bevy.
23/// - This conversion is lossy. The following information is not preserved:
24///   - texture descriptor and texture view descriptor labels
25///   - texture descriptor view formats
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct SerializedImage {
28    data: Option<Vec<u8>>,
29    data_order: SerializedTextureDataOrder,
30    texture_descriptor: TextureDescriptor<(), ()>,
31    sampler: ImageSampler,
32    texture_view_descriptor: Option<SerializedTextureViewDescriptor>,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36struct SerializedTextureViewDescriptor {
37    format: Option<TextureFormat>,
38    dimension: Option<TextureViewDimension>,
39    usage: Option<TextureUsages>,
40    aspect: TextureAspect,
41    base_mip_level: u32,
42    mip_level_count: Option<u32>,
43    base_array_layer: u32,
44    array_layer_count: Option<u32>,
45}
46
47impl SerializedTextureViewDescriptor {
48    fn from_texture_view_descriptor(
49        descriptor: TextureViewDescriptor<Option<&'static str>>,
50    ) -> Self {
51        Self {
52            format: descriptor.format,
53            dimension: descriptor.dimension,
54            usage: descriptor.usage,
55            aspect: descriptor.aspect,
56            base_mip_level: descriptor.base_mip_level,
57            mip_level_count: descriptor.mip_level_count,
58            base_array_layer: descriptor.base_array_layer,
59            array_layer_count: descriptor.array_layer_count,
60        }
61    }
62
63    fn into_texture_view_descriptor(self) -> TextureViewDescriptor<Option<&'static str>> {
64        TextureViewDescriptor {
65            // Not used for asset-based images other than debugging
66            label: None,
67            format: self.format,
68            dimension: self.dimension,
69            usage: self.usage,
70            aspect: self.aspect,
71            base_mip_level: self.base_mip_level,
72            mip_level_count: self.mip_level_count,
73            base_array_layer: self.base_array_layer,
74            array_layer_count: self.array_layer_count,
75        }
76    }
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
80enum SerializedTextureDataOrder {
81    LayerMajor,
82    MipMajor,
83}
84
85impl SerializedTextureDataOrder {
86    fn from_texture_data_order(order: TextureDataOrder) -> Self {
87        match order {
88            TextureDataOrder::LayerMajor => SerializedTextureDataOrder::LayerMajor,
89            TextureDataOrder::MipMajor => SerializedTextureDataOrder::MipMajor,
90        }
91    }
92
93    fn into_texture_data_order(self) -> TextureDataOrder {
94        match self {
95            SerializedTextureDataOrder::LayerMajor => TextureDataOrder::LayerMajor,
96            SerializedTextureDataOrder::MipMajor => TextureDataOrder::MipMajor,
97        }
98    }
99}
100
101impl SerializedImage {
102    /// Creates a new [`SerializedImage`] from an [`Image`].
103    pub fn from_image(image: Image) -> Self {
104        Self {
105            data: image.data,
106            data_order: SerializedTextureDataOrder::from_texture_data_order(image.data_order),
107            texture_descriptor: TextureDescriptor {
108                label: (),
109                size: image.texture_descriptor.size,
110                mip_level_count: image.texture_descriptor.mip_level_count,
111                sample_count: image.texture_descriptor.sample_count,
112                dimension: image.texture_descriptor.dimension,
113                format: image.texture_descriptor.format,
114                usage: image.texture_descriptor.usage,
115                view_formats: (),
116            },
117            sampler: image.sampler,
118            texture_view_descriptor: image.texture_view_descriptor.map(|descriptor| {
119                SerializedTextureViewDescriptor::from_texture_view_descriptor(descriptor)
120            }),
121        }
122    }
123
124    /// Create an [`Image`] from a [`SerializedImage`].
125    pub fn into_image(self) -> Image {
126        Image {
127            data: self.data,
128            data_order: self.data_order.into_texture_data_order(),
129            texture_descriptor: TextureDescriptor {
130                // Not used for asset-based images other than debugging
131                label: None,
132                size: self.texture_descriptor.size,
133                mip_level_count: self.texture_descriptor.mip_level_count,
134                sample_count: self.texture_descriptor.sample_count,
135                dimension: self.texture_descriptor.dimension,
136                format: self.texture_descriptor.format,
137                usage: self.texture_descriptor.usage,
138                // Not used for asset-based images
139                view_formats: &[],
140            },
141            sampler: self.sampler,
142            texture_view_descriptor: self
143                .texture_view_descriptor
144                .map(SerializedTextureViewDescriptor::into_texture_view_descriptor),
145            asset_usage: RenderAssetUsages::RENDER_WORLD,
146            copy_on_resize: false,
147        }
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use wgpu_types::{Extent3d, TextureDimension};
154
155    use super::*;
156
157    #[test]
158    fn serialize_deserialize_image() {
159        let image = Image::new(
160            Extent3d {
161                width: 3,
162                height: 1,
163                depth_or_array_layers: 1,
164            },
165            TextureDimension::D2,
166            vec![255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255],
167            TextureFormat::Rgba8UnormSrgb,
168            RenderAssetUsages::RENDER_WORLD,
169        );
170
171        let serialized_image = SerializedImage::from_image(image.clone());
172        let serialized_string = serde_json::to_string(&serialized_image).unwrap();
173        let serialized_image_from_string: SerializedImage =
174            serde_json::from_str(&serialized_string).unwrap();
175        let deserialized_image = serialized_image_from_string.into_image();
176        assert_eq!(image, deserialized_image);
177    }
178}