Path: blob/main/crates/bevy_render/src/texture/gpu_image.rs
9343 views
use crate::{1render_asset::{AssetExtractionError, PrepareAssetError, RenderAsset},2render_resource::{DefaultImageSampler, Sampler, Texture, TextureView},3renderer::{RenderDevice, RenderQueue},4};5use bevy_asset::{AssetId, RenderAssetUsages};6use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem};7use bevy_image::{Image, ImageSampler};8use bevy_log::warn;9use bevy_math::{AspectRatio, UVec2};10use wgpu::{Extent3d, TexelCopyBufferLayout, TextureFormat, TextureUsages};11use wgpu_types::{TextureDescriptor, TextureViewDescriptor};1213/// The GPU-representation of an [`Image`].14/// Consists of the [`Texture`], its [`TextureView`] and the corresponding [`Sampler`], and the texture's size.15#[derive(Debug, Clone)]16pub struct GpuImage {17pub texture: Texture,18pub texture_view: TextureView,19pub sampler: Sampler,20pub texture_descriptor: TextureDescriptor<Option<&'static str>, &'static [TextureFormat]>,21pub texture_view_descriptor: Option<TextureViewDescriptor<Option<&'static str>>>,22pub had_data: bool,23}2425impl RenderAsset for GpuImage {26type SourceAsset = Image;27type Param = (28SRes<RenderDevice>,29SRes<RenderQueue>,30SRes<DefaultImageSampler>,31);3233#[inline]34fn asset_usage(image: &Self::SourceAsset) -> RenderAssetUsages {35image.asset_usage36}3738fn take_gpu_data(39source: &mut Self::SourceAsset,40previous_gpu_asset: Option<&Self>,41) -> Result<Self::SourceAsset, AssetExtractionError> {42let data = source.data.take();4344// check if this image originally had data and no longer does, that implies it45// has already been extracted46let valid_upload = data.is_some() || previous_gpu_asset.is_none_or(|prev| !prev.had_data);4748valid_upload49.then(|| Self::SourceAsset {50data,51..source.clone()52})53.ok_or(AssetExtractionError::AlreadyExtracted)54}5556#[inline]57fn byte_len(image: &Self::SourceAsset) -> Option<usize> {58image.data.as_ref().map(Vec::len)59}6061/// Converts the extracted image into a [`GpuImage`].62fn prepare_asset(63image: Self::SourceAsset,64_: AssetId<Self::SourceAsset>,65(render_device, render_queue, default_sampler): &mut SystemParamItem<Self::Param>,66previous_asset: Option<&Self>,67) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {68let had_data = image.data.is_some();69let texture = if let Some(prev) = previous_asset70&& prev.texture_descriptor == image.texture_descriptor71&& prev72.texture_descriptor73.usage74.contains(TextureUsages::COPY_DST)75&& let Some(block_bytes) = image.texture_descriptor.format.block_copy_size(None)76{77if let Some(ref data) = image.data {78let (block_width, block_height) =79image.texture_descriptor.format.block_dimensions();8081// queue copy82render_queue.write_texture(83prev.texture.as_image_copy(),84data,85TexelCopyBufferLayout {86offset: 0,87bytes_per_row: Some(image.width() / block_width * block_bytes),88rows_per_image: Some(image.height() / block_height),89},90image.texture_descriptor.size,91);92}9394if !image.copy_on_resize {95// TODO else could clear here? probably not necessary as textures without data are only useful as render96// targets and will normally be overwritten immediately anyway97}9899// reuse previous texture100prev.texture.clone()101} else if let Some(ref data) = image.data {102render_device.create_texture_with_data(103render_queue,104&image.texture_descriptor,105image.data_order,106data,107)108} else {109let new_texture = render_device.create_texture(&image.texture_descriptor);110if image.copy_on_resize {111if let Some(previous) = previous_asset {112let mut command_encoder =113render_device.create_command_encoder(&wgpu::CommandEncoderDescriptor {114label: Some("copy_image_on_resize"),115});116let copy_size = Extent3d {117width: image118.texture_descriptor119.size120.width121.min(previous.texture_descriptor.size.width),122height: image123.texture_descriptor124.size125.height126.min(previous.texture_descriptor.size.height),127depth_or_array_layers: image128.texture_descriptor129.size130.depth_or_array_layers131.min(previous.texture_descriptor.size.depth_or_array_layers),132};133134command_encoder.copy_texture_to_texture(135previous.texture.as_image_copy(),136new_texture.as_image_copy(),137copy_size,138);139render_queue.submit([command_encoder.finish()]);140} else {141warn!("No previous asset to copy from for image: {:?}", image);142}143}144new_texture145};146147let texture_view = if let Some(prev) = previous_asset.as_ref()148&& prev.texture_descriptor == image.texture_descriptor149&& prev150.texture_descriptor151.usage152.contains(TextureUsages::COPY_DST)153&& prev.texture_view_descriptor == image.texture_view_descriptor154{155prev.texture_view.clone()156} else {157image158.texture_view_descriptor159.as_ref()160.map(|desc| texture.create_view(desc))161.unwrap_or_else(|| texture.create_view(&TextureViewDescriptor::default()))162};163let sampler = match image.sampler {164ImageSampler::Default => (***default_sampler).clone(),165ImageSampler::Descriptor(descriptor) => {166render_device.create_sampler(&descriptor.as_wgpu())167}168};169170Ok(GpuImage {171texture,172texture_view,173sampler,174texture_descriptor: image.texture_descriptor,175texture_view_descriptor: image.texture_view_descriptor,176had_data,177})178}179}180181impl GpuImage {182/// Returns the aspect ratio (width / height) of a 2D image.183#[inline]184pub fn aspect_ratio(&self) -> AspectRatio {185AspectRatio::try_from_pixels(186self.texture_descriptor.size.width,187self.texture_descriptor.size.height,188)189.expect(190"Failed to calculate aspect ratio: Image dimensions must be positive, non-zero values",191)192}193194/// Returns the size of a 2D image.195#[inline]196pub fn size_2d(&self) -> UVec2 {197UVec2::new(198self.texture_descriptor.size.width,199self.texture_descriptor.size.height,200)201}202203/// Gets the view format of this image.204/// If the view format is not explicitly provided, falls back to the base image format205#[inline]206pub fn view_format(&self) -> TextureFormat {207self.texture_view_descriptor208.as_ref()209.and_then(|view_desc| view_desc.format)210.unwrap_or(self.texture_descriptor.format)211}212}213214215