bevy_render/texture/
gpu_image.rs1use crate::{
2 render_asset::{AssetExtractionError, PrepareAssetError, RenderAsset},
3 render_resource::{DefaultImageSampler, Sampler, Texture, TextureView},
4 renderer::{RenderDevice, RenderQueue},
5};
6use bevy_asset::{AssetId, RenderAssetUsages};
7use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem};
8use bevy_image::{Image, ImageSampler};
9use bevy_math::{AspectRatio, UVec2};
10use tracing::warn;
11use wgpu::{Extent3d, TextureFormat, TextureViewDescriptor};
12
13#[derive(Debug, Clone)]
16pub struct GpuImage {
17 pub texture: Texture,
18 pub texture_view: TextureView,
19 pub texture_format: TextureFormat,
20 pub texture_view_format: Option<TextureFormat>,
21 pub sampler: Sampler,
22 pub size: Extent3d,
23 pub mip_level_count: u32,
24 pub had_data: bool,
25}
26
27impl RenderAsset for GpuImage {
28 type SourceAsset = Image;
29 type Param = (
30 SRes<RenderDevice>,
31 SRes<RenderQueue>,
32 SRes<DefaultImageSampler>,
33 );
34
35 #[inline]
36 fn asset_usage(image: &Self::SourceAsset) -> RenderAssetUsages {
37 image.asset_usage
38 }
39
40 fn take_gpu_data(
41 source: &mut Self::SourceAsset,
42 previous_gpu_asset: Option<&Self>,
43 ) -> Result<Self::SourceAsset, AssetExtractionError> {
44 let data = source.data.take();
45
46 let valid_upload = data.is_some() || previous_gpu_asset.is_none_or(|prev| !prev.had_data);
49
50 valid_upload
51 .then(|| Self::SourceAsset {
52 data,
53 ..source.clone()
54 })
55 .ok_or(AssetExtractionError::AlreadyExtracted)
56 }
57
58 #[inline]
59 fn byte_len(image: &Self::SourceAsset) -> Option<usize> {
60 image.data.as_ref().map(Vec::len)
61 }
62
63 fn prepare_asset(
65 image: Self::SourceAsset,
66 _: AssetId<Self::SourceAsset>,
67 (render_device, render_queue, default_sampler): &mut SystemParamItem<Self::Param>,
68 previous_asset: Option<&Self>,
69 ) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
70 let had_data = image.data.is_some();
71 let texture = if let Some(ref data) = image.data {
72 render_device.create_texture_with_data(
73 render_queue,
74 &image.texture_descriptor,
75 image.data_order,
76 data,
77 )
78 } else {
79 let new_texture = render_device.create_texture(&image.texture_descriptor);
80 if image.copy_on_resize {
81 if let Some(previous) = previous_asset {
82 let mut command_encoder =
83 render_device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
84 label: Some("copy_image_on_resize"),
85 });
86 let copy_size = Extent3d {
87 width: image.texture_descriptor.size.width.min(previous.size.width),
88 height: image
89 .texture_descriptor
90 .size
91 .height
92 .min(previous.size.height),
93 depth_or_array_layers: image
94 .texture_descriptor
95 .size
96 .depth_or_array_layers
97 .min(previous.size.depth_or_array_layers),
98 };
99
100 command_encoder.copy_texture_to_texture(
101 previous.texture.as_image_copy(),
102 new_texture.as_image_copy(),
103 copy_size,
104 );
105 render_queue.submit([command_encoder.finish()]);
106 } else {
107 warn!("No previous asset to copy from for image: {:?}", image);
108 }
109 }
110 new_texture
111 };
112
113 let texture_view = texture.create_view(
114 image
115 .texture_view_descriptor
116 .as_ref()
117 .unwrap_or(&TextureViewDescriptor::default()),
118 );
119 let sampler = match image.sampler {
120 ImageSampler::Default => (***default_sampler).clone(),
121 ImageSampler::Descriptor(descriptor) => {
122 render_device.create_sampler(&descriptor.as_wgpu())
123 }
124 };
125
126 Ok(GpuImage {
127 texture,
128 texture_view,
129 texture_format: image.texture_descriptor.format,
130 texture_view_format: image.texture_view_descriptor.and_then(|v| v.format),
131 sampler,
132 size: image.texture_descriptor.size,
133 mip_level_count: image.texture_descriptor.mip_level_count,
134 had_data,
135 })
136 }
137}
138
139impl GpuImage {
140 #[inline]
142 pub fn aspect_ratio(&self) -> AspectRatio {
143 AspectRatio::try_from_pixels(self.size.width, self.size.height).expect(
144 "Failed to calculate aspect ratio: Image dimensions must be positive, non-zero values",
145 )
146 }
147
148 #[inline]
150 pub fn size_2d(&self) -> UVec2 {
151 UVec2::new(self.size.width, self.size.height)
152 }
153}