Change GpuImage::size from UVec2 to Extent3d (#16815)

# Objective

When preparing `GpuImage`s, we currently discard the
`depth_or_array_layers` of the `Image`'s size by converting it into a
`UVec2`.

Fixes #16715.

## Solution

Change `GpuImage::size` to `Extent3d`, and just pass that through when
creating `GpuImage`s.
Also copy the `aspect_ratio`, and `size` (now `size_2d` for
disambiguation from the field) functions from `Image` to `GpuImage` for
ease of use with 2D textures.
I originally copied all size-related functions (like `width`, and
`height`), but i think they are unnecessary considering how visible the
`size` field on `GpuImage` is compared to `Image`.

## Testing

Tested via `cargo r -p ci` for everything except docs, when generating
docs it keeps spitting out a ton of
```
error[E0554]: `#![feature]` may not be used on the stable release channel
 --> crates/bevy_dylib/src/lib.rs:1:21
  |
1 | #![cfg_attr(docsrs, feature(doc_auto_cfg))]
  | 
```
Not sure why this is happening, but it also happens without my changes,
so it's almost certainly some strange issue specific to my machine.

## Migration Guide

- `GpuImage::size` is now an `Extent3d`. To easily get 2D size, use
`size_2d()`.
This commit is contained in:
noxmore 2024-12-17 11:08:09 -08:00 committed by GitHub
parent bfa6553f9c
commit 73d68d60bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 43 additions and 40 deletions

View File

@ -1523,7 +1523,7 @@ impl FromWorld for MeshPipeline {
texture_view, texture_view,
texture_format: image.texture_descriptor.format, texture_format: image.texture_descriptor.format,
sampler, sampler,
size: image.size(), size: image.texture_descriptor.size,
mip_level_count: image.texture_descriptor.mip_level_count, mip_level_count: image.texture_descriptor.mip_level_count,
} }
}; };

View File

@ -23,7 +23,7 @@ use bevy_ecs::{
use bevy_image::{Image, TextureFormatPixelInfo}; use bevy_image::{Image, TextureFormatPixelInfo};
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_render_macros::ExtractComponent; use bevy_render_macros::ExtractComponent;
use bevy_utils::{default, tracing::warn, HashMap}; use bevy_utils::{tracing::warn, HashMap};
use encase::internal::ReadFrom; use encase::internal::ReadFrom;
use encase::private::Reader; use encase::private::Reader;
use encase::ShaderType; use encase::ShaderType;
@ -239,17 +239,16 @@ fn prepare_buffers(
match readback { match readback {
Readback::Texture(image) => { Readback::Texture(image) => {
if let Some(gpu_image) = gpu_images.get(image) { if let Some(gpu_image) = gpu_images.get(image) {
let size = Extent3d { let layout = layout_data(
width: gpu_image.size.x, gpu_image.size.width,
height: gpu_image.size.y, gpu_image.size.height,
..default() gpu_image.texture_format,
}; );
let layout = layout_data(size.width, size.height, gpu_image.texture_format);
let buffer = buffer_pool.get( let buffer = buffer_pool.get(
&render_device, &render_device,
get_aligned_size( get_aligned_size(
size.width, gpu_image.size.width,
size.height, gpu_image.size.height,
gpu_image.texture_format.pixel_size() as u32, gpu_image.texture_format.pixel_size() as u32,
) as u64, ) as u64,
); );
@ -259,7 +258,7 @@ fn prepare_buffers(
src: ReadbackSource::Texture { src: ReadbackSource::Texture {
texture: gpu_image.texture.clone(), texture: gpu_image.texture.clone(),
layout, layout,
size, size: gpu_image.size,
}, },
buffer, buffer,
rx, rx,

View File

@ -135,7 +135,7 @@ fn fallback_image_new(
texture_view, texture_view,
texture_format: image.texture_descriptor.format, texture_format: image.texture_descriptor.format,
sampler, sampler,
size: image.size(), size: image.texture_descriptor.size,
mip_level_count: image.texture_descriptor.mip_level_count, mip_level_count: image.texture_descriptor.mip_level_count,
} }
} }

View File

@ -6,8 +6,8 @@ use crate::{
use bevy_asset::AssetId; use bevy_asset::AssetId;
use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem}; use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem};
use bevy_image::{Image, ImageSampler}; use bevy_image::{Image, ImageSampler};
use bevy_math::UVec2; use bevy_math::{AspectRatio, UVec2};
use wgpu::{TextureFormat, TextureViewDescriptor}; use wgpu::{Extent3d, TextureFormat, TextureViewDescriptor};
/// The GPU-representation of an [`Image`]. /// The GPU-representation of an [`Image`].
/// Consists of the [`Texture`], its [`TextureView`] and the corresponding [`Sampler`], and the texture's size. /// Consists of the [`Texture`], its [`TextureView`] and the corresponding [`Sampler`], and the texture's size.
@ -17,7 +17,7 @@ pub struct GpuImage {
pub texture_view: TextureView, pub texture_view: TextureView,
pub texture_format: TextureFormat, pub texture_format: TextureFormat,
pub sampler: Sampler, pub sampler: Sampler,
pub size: UVec2, pub size: Extent3d,
pub mip_level_count: u32, pub mip_level_count: u32,
} }
@ -53,7 +53,6 @@ impl RenderAsset for GpuImage {
&image.data, &image.data,
); );
let size = image.size();
let texture_view = texture.create_view( let texture_view = texture.create_view(
image image
.texture_view_descriptor .texture_view_descriptor
@ -73,8 +72,24 @@ impl RenderAsset for GpuImage {
texture_view, texture_view,
texture_format: image.texture_descriptor.format, texture_format: image.texture_descriptor.format,
sampler, sampler,
size, size: image.texture_descriptor.size,
mip_level_count: image.texture_descriptor.mip_level_count, 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)
}
}

View File

@ -302,13 +302,8 @@ fn prepare_screenshots(
continue; continue;
}; };
let format = gpu_image.texture_format; let format = gpu_image.texture_format;
let size = Extent3d {
width: gpu_image.size.x,
height: gpu_image.size.y,
..default()
};
let (texture_view, state) = prepare_screenshot_state( let (texture_view, state) = prepare_screenshot_state(
size, gpu_image.size,
format, format,
&render_device, &render_device,
&screenshot_pipeline, &screenshot_pipeline,
@ -542,8 +537,8 @@ pub(crate) fn submit_screenshot_commands(world: &World, encoder: &mut CommandEnc
warn!("Unknown image for screenshot, skipping: {:?}", image); warn!("Unknown image for screenshot, skipping: {:?}", image);
continue; continue;
}; };
let width = gpu_image.size.x; let width = gpu_image.size.width;
let height = gpu_image.size.y; let height = gpu_image.size.height;
let texture_format = gpu_image.texture_format; let texture_format = gpu_image.texture_format;
let texture_view = gpu_image.texture_view.deref(); let texture_view = gpu_image.texture_view.deref();
render_screenshot( render_screenshot(

View File

@ -313,7 +313,7 @@ impl FromWorld for Mesh2dPipeline {
texture_view, texture_view,
texture_format: image.texture_descriptor.format, texture_format: image.texture_descriptor.format,
sampler, sampler,
size: image.size(), size: image.texture_descriptor.size,
mip_level_count: image.texture_descriptor.mip_level_count, mip_level_count: image.texture_descriptor.mip_level_count,
} }
}; };

View File

@ -117,7 +117,7 @@ impl FromWorld for SpritePipeline {
texture_view, texture_view,
texture_format: image.texture_descriptor.format, texture_format: image.texture_descriptor.format,
sampler, sampler,
size: image.size(), size: image.texture_descriptor.size,
mip_level_count: image.texture_descriptor.mip_level_count, mip_level_count: image.texture_descriptor.mip_level_count,
} }
}; };
@ -676,7 +676,7 @@ pub fn prepare_sprite_image_bind_groups(
continue; continue;
}; };
batch_image_size = gpu_image.size.as_vec2(); batch_image_size = gpu_image.size_2d().as_vec2();
batch_image_handle = extracted_sprite.image_handle_id; batch_image_handle = extracted_sprite.image_handle_id;
image_bind_groups image_bind_groups
.values .values

View File

@ -1052,7 +1052,7 @@ pub fn prepare_uinodes(
); );
// Rescale atlases. This is done here because we need texture data that might not be available in Extract. // Rescale atlases. This is done here because we need texture data that might not be available in Extract.
let atlas_extent = atlas_scaling let atlas_extent = atlas_scaling
.map(|scaling| image.size.as_vec2() * scaling) .map(|scaling| image.size_2d().as_vec2() * scaling)
.unwrap_or(uinode_rect.max); .unwrap_or(uinode_rect.max);
if *flip_x { if *flip_x {
core::mem::swap(&mut uinode_rect.max.x, &mut uinode_rect.min.x); core::mem::swap(&mut uinode_rect.max.x, &mut uinode_rect.min.x);
@ -1127,7 +1127,7 @@ pub fn prepare_uinodes(
.get(extracted_uinode.image) .get(extracted_uinode.image)
.expect("Image was checked during batching and should still exist"); .expect("Image was checked during batching and should still exist");
let atlas_extent = image.size.as_vec2() * *atlas_scaling; let atlas_extent = image.size_2d().as_vec2() * *atlas_scaling;
let color = extracted_uinode.color.to_f32_array(); let color = extracted_uinode.color.to_f32_array();
for glyph in &extracted_uinodes.glyphs[range.clone()] { for glyph in &extracted_uinodes.glyphs[range.clone()] {

View File

@ -442,7 +442,7 @@ pub fn prepare_ui_slices(
if let Some(gpu_image) = gpu_images.get(texture_slices.image) { if let Some(gpu_image) = gpu_images.get(texture_slices.image) {
batch_item_index = item_index; batch_item_index = item_index;
batch_image_handle = texture_slices.image; batch_image_handle = texture_slices.image;
batch_image_size = gpu_image.size.as_vec2(); batch_image_size = gpu_image.size_2d().as_vec2();
let new_batch = UiTextureSlicerBatch { let new_batch = UiTextureSlicerBatch {
range: vertices_index..vertices_index, range: vertices_index..vertices_index,
@ -475,7 +475,7 @@ pub fn prepare_ui_slices(
{ {
if let Some(gpu_image) = gpu_images.get(texture_slices.image) { if let Some(gpu_image) = gpu_images.get(texture_slices.image) {
batch_image_handle = texture_slices.image; batch_image_handle = texture_slices.image;
batch_image_size = gpu_image.size.as_vec2(); batch_image_size = gpu_image.size_2d().as_vec2();
existing_batch.as_mut().unwrap().1.image = texture_slices.image; existing_batch.as_mut().unwrap().1.image = texture_slices.image;
image_bind_groups image_bind_groups

View File

@ -367,15 +367,9 @@ impl render_graph::Node for ImageCopyDriver {
// That's why image in buffer can be little bit wider // That's why image in buffer can be little bit wider
// This should be taken into account at copy from buffer stage // This should be taken into account at copy from buffer stage
let padded_bytes_per_row = RenderDevice::align_copy_bytes_per_row( let padded_bytes_per_row = RenderDevice::align_copy_bytes_per_row(
(src_image.size.x as usize / block_dimensions.0 as usize) * block_size as usize, (src_image.size.width as usize / block_dimensions.0 as usize) * block_size as usize,
); );
let texture_extent = Extent3d {
width: src_image.size.x,
height: src_image.size.y,
depth_or_array_layers: 1,
};
encoder.copy_texture_to_buffer( encoder.copy_texture_to_buffer(
src_image.texture.as_image_copy(), src_image.texture.as_image_copy(),
ImageCopyBuffer { ImageCopyBuffer {
@ -390,7 +384,7 @@ impl render_graph::Node for ImageCopyDriver {
rows_per_image: None, rows_per_image: None,
}, },
}, },
texture_extent, src_image.size,
); );
let render_queue = world.get_resource::<RenderQueue>().unwrap(); let render_queue = world.get_resource::<RenderQueue>().unwrap();