bevy/crates/bevy_render/src/texture/gpu_image.rs
Lege19 3978ba9783
Allowed creating uninitialized images (for use as storage textures) (#17760)
# Objective
https://github.com/bevyengine/bevy/issues/17746
## Solution
- Change `Image.data` from being a `Vec<u8>` to a `Option<Vec<u8>>`
- Added functions to help with creating images
## Testing

- Did you test these changes? If so, how?
All current tests pass
Tested a variety of existing examples to make sure they don't crash
(they don't)
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
Linux x86 64-bit NixOS 
---
## Migration Guide
Code that directly access `Image` data will now need to use unwrap or
handle the case where no data is provided.
Behaviour of new_fill slightly changed, but not in a way that is likely
to affect anything. It no longer panics and will fill the whole texture
instead of leaving black pixels if the data provided is not a nice
factor of the size of the image.

---------

Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
2025-02-10 22:22:07 +00:00

100 lines
3.2 KiB
Rust

use crate::{
render_asset::{PrepareAssetError, RenderAsset, RenderAssetUsages},
render_resource::{DefaultImageSampler, Sampler, Texture, TextureView},
renderer::{RenderDevice, RenderQueue},
};
use bevy_asset::AssetId;
use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem};
use bevy_image::{Image, ImageSampler};
use bevy_math::{AspectRatio, UVec2};
use wgpu::{Extent3d, TextureFormat, TextureViewDescriptor};
/// The GPU-representation of an [`Image`].
/// Consists of the [`Texture`], its [`TextureView`] and the corresponding [`Sampler`], and the texture's size.
#[derive(Debug, Clone)]
pub struct GpuImage {
pub texture: Texture,
pub texture_view: TextureView,
pub texture_format: TextureFormat,
pub sampler: Sampler,
pub size: Extent3d,
pub mip_level_count: u32,
}
impl RenderAsset for GpuImage {
type SourceAsset = Image;
type Param = (
SRes<RenderDevice>,
SRes<RenderQueue>,
SRes<DefaultImageSampler>,
);
#[inline]
fn asset_usage(image: &Self::SourceAsset) -> RenderAssetUsages {
image.asset_usage
}
#[inline]
fn byte_len(image: &Self::SourceAsset) -> Option<usize> {
image.data.as_ref().map(Vec::len)
}
/// Converts the extracted image into a [`GpuImage`].
fn prepare_asset(
image: Self::SourceAsset,
_: AssetId<Self::SourceAsset>,
(render_device, render_queue, default_sampler): &mut SystemParamItem<Self::Param>,
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
let texture = if let Some(ref data) = image.data {
render_device.create_texture_with_data(
render_queue,
&image.texture_descriptor,
// TODO: Is this correct? Do we need to use `MipMajor` if it's a ktx2 file?
wgpu::util::TextureDataOrder::default(),
data,
)
} else {
render_device.create_texture(&image.texture_descriptor)
};
let texture_view = texture.create_view(
image
.texture_view_descriptor
.or_else(|| Some(TextureViewDescriptor::default()))
.as_ref()
.unwrap(),
);
let sampler = match image.sampler {
ImageSampler::Default => (***default_sampler).clone(),
ImageSampler::Descriptor(descriptor) => {
render_device.create_sampler(&descriptor.as_wgpu())
}
};
Ok(GpuImage {
texture,
texture_view,
texture_format: image.texture_descriptor.format,
sampler,
size: image.texture_descriptor.size,
mip_level_count: image.texture_descriptor.mip_level_count,
})
}
}
impl GpuImage {
/// Returns the aspect ratio (width / height) of a 2D image.
#[inline]
pub fn aspect_ratio(&self) -> AspectRatio {
AspectRatio::try_from_pixels(self.size.width, self.size.height).expect(
"Failed to calculate aspect ratio: Image dimensions must be positive, non-zero values",
)
}
/// Returns the size of a 2D image.
#[inline]
pub fn size_2d(&self) -> UVec2 {
UVec2::new(self.size.width, self.size.height)
}
}