bevy_render/texture/
gpu_image.rs

1use crate::{
2    render_asset::{PrepareAssetError, RenderAsset, RenderAssetUsages},
3    render_resource::{DefaultImageSampler, Sampler, Texture, TextureView},
4    renderer::{RenderDevice, RenderQueue},
5};
6use bevy_asset::AssetId;
7use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem};
8use bevy_image::{Image, ImageSampler};
9use bevy_math::{AspectRatio, UVec2};
10use wgpu::{Extent3d, TextureFormat, TextureViewDescriptor};
11
12/// The GPU-representation of an [`Image`].
13/// Consists of the [`Texture`], its [`TextureView`] and the corresponding [`Sampler`], and the texture's size.
14#[derive(Debug, Clone)]
15pub struct GpuImage {
16    pub texture: Texture,
17    pub texture_view: TextureView,
18    pub texture_format: TextureFormat,
19    pub sampler: Sampler,
20    pub size: Extent3d,
21    pub mip_level_count: u32,
22}
23
24impl RenderAsset for GpuImage {
25    type SourceAsset = Image;
26    type Param = (
27        SRes<RenderDevice>,
28        SRes<RenderQueue>,
29        SRes<DefaultImageSampler>,
30    );
31
32    #[inline]
33    fn asset_usage(image: &Self::SourceAsset) -> RenderAssetUsages {
34        image.asset_usage
35    }
36
37    #[inline]
38    fn byte_len(image: &Self::SourceAsset) -> Option<usize> {
39        image.data.as_ref().map(Vec::len)
40    }
41
42    /// Converts the extracted image into a [`GpuImage`].
43    fn prepare_asset(
44        image: Self::SourceAsset,
45        _: AssetId<Self::SourceAsset>,
46        (render_device, render_queue, default_sampler): &mut SystemParamItem<Self::Param>,
47    ) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
48        let texture = if let Some(ref data) = image.data {
49            render_device.create_texture_with_data(
50                render_queue,
51                &image.texture_descriptor,
52                // TODO: Is this correct? Do we need to use `MipMajor` if it's a ktx2 file?
53                wgpu::util::TextureDataOrder::default(),
54                data,
55            )
56        } else {
57            render_device.create_texture(&image.texture_descriptor)
58        };
59
60        let texture_view = texture.create_view(
61            image
62                .texture_view_descriptor
63                .or_else(|| Some(TextureViewDescriptor::default()))
64                .as_ref()
65                .unwrap(),
66        );
67        let sampler = match image.sampler {
68            ImageSampler::Default => (***default_sampler).clone(),
69            ImageSampler::Descriptor(descriptor) => {
70                render_device.create_sampler(&descriptor.as_wgpu())
71            }
72        };
73
74        Ok(GpuImage {
75            texture,
76            texture_view,
77            texture_format: image.texture_descriptor.format,
78            sampler,
79            size: image.texture_descriptor.size,
80            mip_level_count: image.texture_descriptor.mip_level_count,
81        })
82    }
83}
84
85impl GpuImage {
86    /// Returns the aspect ratio (width / height) of a 2D image.
87    #[inline]
88    pub fn aspect_ratio(&self) -> AspectRatio {
89        AspectRatio::try_from_pixels(self.size.width, self.size.height).expect(
90            "Failed to calculate aspect ratio: Image dimensions must be positive, non-zero values",
91        )
92    }
93
94    /// Returns the size of a 2D image.
95    #[inline]
96    pub fn size_2d(&self) -> UVec2 {
97        UVec2::new(self.size.width, self.size.height)
98    }
99}