bevy_render/texture/
gpu_image.rs1use crate::{
2 render_asset::{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 sampler: Sampler,
21 pub size: Extent3d,
22 pub mip_level_count: u32,
23}
24
25impl RenderAsset for GpuImage {
26 type SourceAsset = Image;
27 type Param = (
28 SRes<RenderDevice>,
29 SRes<RenderQueue>,
30 SRes<DefaultImageSampler>,
31 );
32
33 #[inline]
34 fn asset_usage(image: &Self::SourceAsset) -> RenderAssetUsages {
35 image.asset_usage
36 }
37
38 #[inline]
39 fn byte_len(image: &Self::SourceAsset) -> Option<usize> {
40 image.data.as_ref().map(Vec::len)
41 }
42
43 fn prepare_asset(
45 image: Self::SourceAsset,
46 _: AssetId<Self::SourceAsset>,
47 (render_device, render_queue, default_sampler): &mut SystemParamItem<Self::Param>,
48 previous_asset: Option<&Self>,
49 ) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
50 let texture = if let Some(ref data) = image.data {
51 render_device.create_texture_with_data(
52 render_queue,
53 &image.texture_descriptor,
54 image.data_order,
55 data,
56 )
57 } else {
58 let new_texture = render_device.create_texture(&image.texture_descriptor);
59 if image.copy_on_resize {
60 if let Some(previous) = previous_asset {
61 let mut command_encoder =
62 render_device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
63 label: Some("copy_image_on_resize"),
64 });
65 let copy_size = Extent3d {
66 width: image.texture_descriptor.size.width.min(previous.size.width),
67 height: image
68 .texture_descriptor
69 .size
70 .height
71 .min(previous.size.height),
72 depth_or_array_layers: image
73 .texture_descriptor
74 .size
75 .depth_or_array_layers
76 .min(previous.size.depth_or_array_layers),
77 };
78
79 command_encoder.copy_texture_to_texture(
80 previous.texture.as_image_copy(),
81 new_texture.as_image_copy(),
82 copy_size,
83 );
84 render_queue.submit([command_encoder.finish()]);
85 } else {
86 warn!("No previous asset to copy from for image: {:?}", image);
87 }
88 }
89 new_texture
90 };
91
92 let texture_view = texture.create_view(
93 image
94 .texture_view_descriptor
95 .or_else(|| Some(TextureViewDescriptor::default()))
96 .as_ref()
97 .unwrap(),
98 );
99 let sampler = match image.sampler {
100 ImageSampler::Default => (***default_sampler).clone(),
101 ImageSampler::Descriptor(descriptor) => {
102 render_device.create_sampler(&descriptor.as_wgpu())
103 }
104 };
105
106 Ok(GpuImage {
107 texture,
108 texture_view,
109 texture_format: image.texture_descriptor.format,
110 sampler,
111 size: image.texture_descriptor.size,
112 mip_level_count: image.texture_descriptor.mip_level_count,
113 })
114 }
115}
116
117impl GpuImage {
118 #[inline]
120 pub fn aspect_ratio(&self) -> AspectRatio {
121 AspectRatio::try_from_pixels(self.size.width, self.size.height).expect(
122 "Failed to calculate aspect ratio: Image dimensions must be positive, non-zero values",
123 )
124 }
125
126 #[inline]
128 pub fn size_2d(&self) -> UVec2 {
129 UVec2::new(self.size.width, self.size.height)
130 }
131}