diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 15fa70b81e8df..1da85d518a4db 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -22,9 +22,7 @@ use bevy_render::{ render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass}, render_resource::*, renderer::{RenderDevice, RenderQueue}, - texture::{ - BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo, - }, + texture::{BevyDefault, BytesPerRow, DefaultImageSampler, GpuImage, Image, ImageSampler}, view::{ComputedVisibility, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms}, Extract, RenderApp, RenderStage, }; @@ -446,7 +444,6 @@ impl FromWorld for MeshPipeline { ImageSampler::Descriptor(descriptor) => render_device.create_sampler(&descriptor), }; - let format_size = image.texture_descriptor.format.pixel_size(); render_queue.write_texture( ImageCopyTexture { texture: &texture, @@ -459,7 +456,10 @@ impl FromWorld for MeshPipeline { offset: 0, bytes_per_row: Some( std::num::NonZeroU32::new( - image.texture_descriptor.size.width * format_size as u32, + image + .texture_descriptor + .size + .bytes_per_row(image.texture_descriptor.format), ) .unwrap(), ), diff --git a/crates/bevy_render/src/texture/hdr_texture_loader.rs b/crates/bevy_render/src/texture/hdr_texture_loader.rs index 81c539a061664..537485c83ff35 100644 --- a/crates/bevy_render/src/texture/hdr_texture_loader.rs +++ b/crates/bevy_render/src/texture/hdr_texture_loader.rs @@ -1,4 +1,4 @@ -use crate::texture::{Image, TextureFormatPixelInfo}; +use crate::texture::Image; use anyhow::Result; use bevy_asset::{AssetLoader, LoadContext, LoadedAsset}; use bevy_utils::BoxedFuture; @@ -16,16 +16,12 @@ impl AssetLoader for HdrTextureLoader { ) -> BoxedFuture<'a, Result<()>> { Box::pin(async move { let format = TextureFormat::Rgba32Float; - debug_assert_eq!( - format.pixel_size(), - 4 * 4, - "Format should have 32bit x 4 size" - ); let decoder = image::codecs::hdr::HdrDecoder::new(bytes)?; let info = decoder.metadata(); let rgb_data = decoder.read_image_hdr()?; - let mut rgba_data = Vec::with_capacity(rgb_data.len() * format.pixel_size()); + let mut rgba_data = + Vec::with_capacity(rgb_data.len() * format.describe().block_size as usize); for rgb in rgb_data { let alpha = 1.0f32; diff --git a/crates/bevy_render/src/texture/image.rs b/crates/bevy_render/src/texture/image.rs index 54ee7338c8c72..5d93c76500f57 100644 --- a/crates/bevy_render/src/texture/image.rs +++ b/crates/bevy_render/src/texture/image.rs @@ -172,7 +172,8 @@ pub struct DefaultImageSampler(pub(crate) Sampler); impl Default for Image { fn default() -> Self { let format = wgpu::TextureFormat::bevy_default(); - let data = vec![255; format.pixel_size()]; + // TODO: check if this breaks if bevy_default is a compressed texture + let data = vec![255; format.describe().block_size as usize]; Image { data, texture_descriptor: wgpu::TextureDescriptor { @@ -207,7 +208,7 @@ impl Image { format: TextureFormat, ) -> Self { debug_assert_eq!( - size.volume() * format.pixel_size(), + size.total_bytes(format) as usize, data.len(), "Pixel data, size and format have to match", ); @@ -239,7 +240,7 @@ impl Image { value.resize(size); debug_assert_eq!( - pixel.len() % format.pixel_size(), + pixel.len() % format.describe().block_size as usize, 0, "Must not have incomplete pixel data." ); @@ -271,10 +272,8 @@ impl Image { /// Does not properly resize the contents of the image, but only its internal `data` buffer. pub fn resize(&mut self, size: Extent3d) { self.texture_descriptor.size = size; - self.data.resize( - size.volume() * self.texture_descriptor.format.pixel_size(), - 0, - ); + self.data + .resize(size.total_bytes(self.texture_descriptor.format) as usize, 0); } /// Changes the `size`, asserting that the total number of data elements (pixels) remains the @@ -467,15 +466,35 @@ impl Volume for Extent3d { } } -/// Extends the wgpu [`TextureFormat`] with information about the pixel. -pub trait TextureFormatPixelInfo { - /// Returns the size in bytes of a pixel of the format. - fn pixel_size(&self) -> usize; +pub trait BytesPerRow { + fn bytes_per_row(&self, format: TextureFormat) -> u32; +} + +impl BytesPerRow for Extent3d { + fn bytes_per_row(&self, format: TextureFormat) -> u32 { + let info = format.describe(); + self.physical_size(format).width * info.block_size as u32 / info.block_dimensions.0 as u32 + } +} + +pub trait TotalBytes { + fn total_bytes(&self, format: TextureFormat) -> u32; } -impl TextureFormatPixelInfo for TextureFormat { - fn pixel_size(&self) -> usize { - self.describe().block_size.into() +impl TotalBytes for Extent3d { + fn total_bytes(&self, format: TextureFormat) -> u32 { + self.rows_per_image(format) * self.bytes_per_row(format) * self.depth_or_array_layers + } +} + +pub trait RowsPerImage { + fn rows_per_image(&self, format: TextureFormat) -> u32; +} + +impl RowsPerImage for Extent3d { + fn rows_per_image(&self, format: TextureFormat) -> u32 { + let info = format.describe(); + self.physical_size(format).height / info.block_dimensions.1 as u32 } } @@ -517,7 +536,6 @@ impl RenderAsset for Image { ) } else { let texture = render_device.create_texture(&image.texture_descriptor); - let format_size = image.texture_descriptor.format.pixel_size(); render_queue.write_texture( ImageCopyTexture { texture: &texture, @@ -530,12 +548,20 @@ impl RenderAsset for Image { offset: 0, bytes_per_row: Some( std::num::NonZeroU32::new( - image.texture_descriptor.size.width * format_size as u32, + image + .texture_descriptor + .size + .bytes_per_row(image.texture_descriptor.format), ) .unwrap(), ), rows_per_image: if image.texture_descriptor.size.depth_or_array_layers > 1 { - std::num::NonZeroU32::new(image.texture_descriptor.size.height) + std::num::NonZeroU32::new( + image + .texture_descriptor + .size + .rows_per_image(image.texture_descriptor.format), + ) } else { None }, diff --git a/crates/bevy_render/src/texture/image_texture_conversion.rs b/crates/bevy_render/src/texture/image_texture_conversion.rs index 45ae5bbc22582..0ead0afc0b078 100644 --- a/crates/bevy_render/src/texture/image_texture_conversion.rs +++ b/crates/bevy_render/src/texture/image_texture_conversion.rs @@ -1,4 +1,4 @@ -use crate::texture::{Image, TextureFormatPixelInfo}; +use crate::texture::Image; use anyhow::anyhow; use image::{DynamicImage, ImageBuffer}; use wgpu::{Extent3d, TextureDimension, TextureFormat}; @@ -84,8 +84,9 @@ impl Image { height = image.height(); format = TextureFormat::Rgba16Uint; - let mut local_data = - Vec::with_capacity(width as usize * height as usize * format.pixel_size()); + let mut local_data = Vec::with_capacity( + width as usize * height as usize * format.describe().block_size as usize, + ); for pixel in image.into_raw().chunks_exact(3) { // TODO: use the array_chunks method once stabilised @@ -117,8 +118,9 @@ impl Image { height = image.height(); format = TextureFormat::Rgba32Float; - let mut local_data = - Vec::with_capacity(width as usize * height as usize * format.pixel_size()); + let mut local_data = Vec::with_capacity( + width as usize * height as usize * format.describe().block_size as usize, + ); for pixel in image.into_raw().chunks_exact(3) { // TODO: use the array_chunks method once stabilised diff --git a/crates/bevy_sprite/src/dynamic_texture_atlas_builder.rs b/crates/bevy_sprite/src/dynamic_texture_atlas_builder.rs index 1155e0c5e23ce..6a1094528896c 100644 --- a/crates/bevy_sprite/src/dynamic_texture_atlas_builder.rs +++ b/crates/bevy_sprite/src/dynamic_texture_atlas_builder.rs @@ -1,7 +1,7 @@ use crate::TextureAtlas; use bevy_asset::Assets; use bevy_math::{IVec2, Rect, Vec2}; -use bevy_render::texture::{Image, TextureFormatPixelInfo}; +use bevy_render::texture::Image; use guillotiere::{size2, Allocation, AtlasAllocator}; pub struct DynamicTextureAtlasBuilder { @@ -67,12 +67,26 @@ impl DynamicTextureAtlasBuilder { allocation: Allocation, texture: &Image, ) { + debug_assert_eq!( + atlas_texture + .texture_descriptor + .format + .describe() + .block_dimensions, + (1, 1), + "Compressed textures are unsupported" + ); + let mut rect = allocation.rectangle; rect.max.x -= self.padding; rect.max.y -= self.padding; let atlas_width = atlas_texture.texture_descriptor.size.width as usize; let rect_width = rect.width() as usize; - let format_size = atlas_texture.texture_descriptor.format.pixel_size(); + let format_size = atlas_texture + .texture_descriptor + .format + .describe() + .block_size as usize; for (texture_y, bound_y) in (rect.min.y..rect.max.y).map(|i| i as usize).enumerate() { let begin = (bound_y * atlas_width + rect.min.x as usize) * format_size; diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index 0910463f26c9f..6e1d1b88b8bc9 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -14,9 +14,7 @@ use bevy_render::{ render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass}, render_resource::*, renderer::{RenderDevice, RenderQueue}, - texture::{ - BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo, - }, + texture::{BevyDefault, BytesPerRow, DefaultImageSampler, GpuImage, Image, ImageSampler}, view::{ ComputedVisibility, ExtractedView, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms, }, @@ -218,7 +216,6 @@ impl FromWorld for Mesh2dPipeline { ImageSampler::Descriptor(descriptor) => render_device.create_sampler(&descriptor), }; - let format_size = image.texture_descriptor.format.pixel_size(); let render_queue = world.resource_mut::(); render_queue.write_texture( ImageCopyTexture { @@ -232,7 +229,10 @@ impl FromWorld for Mesh2dPipeline { offset: 0, bytes_per_row: Some( std::num::NonZeroU32::new( - image.texture_descriptor.size.width * format_size as u32, + image + .texture_descriptor + .size + .bytes_per_row(image.texture_descriptor.format), ) .unwrap(), ), diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 1cdc6d93782d8..b8b5d14292b9e 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -21,9 +21,7 @@ use bevy_render::{ }, render_resource::*, renderer::{RenderDevice, RenderQueue}, - texture::{ - BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo, - }, + texture::{BevyDefault, BytesPerRow, DefaultImageSampler, GpuImage, Image, ImageSampler}, view::{ ComputedVisibility, ExtractedView, Msaa, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms, VisibleEntities, @@ -100,7 +98,6 @@ impl FromWorld for SpritePipeline { ImageSampler::Descriptor(descriptor) => render_device.create_sampler(&descriptor), }; - let format_size = image.texture_descriptor.format.pixel_size(); render_queue.write_texture( ImageCopyTexture { texture: &texture, @@ -113,7 +110,10 @@ impl FromWorld for SpritePipeline { offset: 0, bytes_per_row: Some( std::num::NonZeroU32::new( - image.texture_descriptor.size.width * format_size as u32, + image + .texture_descriptor + .size + .bytes_per_row(image.texture_descriptor.format), ) .unwrap(), ), diff --git a/crates/bevy_sprite/src/texture_atlas_builder.rs b/crates/bevy_sprite/src/texture_atlas_builder.rs index f77e0cce90c31..f847459fe87ab 100644 --- a/crates/bevy_sprite/src/texture_atlas_builder.rs +++ b/crates/bevy_sprite/src/texture_atlas_builder.rs @@ -3,7 +3,7 @@ use bevy_log::{debug, error, warn}; use bevy_math::{Rect, Vec2}; use bevy_render::{ render_resource::{Extent3d, TextureDimension, TextureFormat}, - texture::{Image, TextureFormatPixelInfo}, + texture::Image, }; use bevy_utils::HashMap; use rectangle_pack::{ @@ -97,12 +97,26 @@ impl TextureAtlasBuilder { texture: &Image, packed_location: &PackedLocation, ) { + debug_assert_eq!( + atlas_texture + .texture_descriptor + .format + .describe() + .block_dimensions, + (1, 1), + "Compressed textures are unsupported" + ); + let rect_width = packed_location.width() as usize; let rect_height = packed_location.height() as usize; let rect_x = packed_location.x() as usize; let rect_y = packed_location.y() as usize; let atlas_width = atlas_texture.texture_descriptor.size.width as usize; - let format_size = atlas_texture.texture_descriptor.format.pixel_size(); + let format_size = atlas_texture + .texture_descriptor + .format + .describe() + .block_size as usize; for (texture_y, bound_y) in (rect_y..rect_y + rect_height).enumerate() { let begin = (bound_y * atlas_width + rect_x) * format_size; @@ -161,6 +175,16 @@ impl TextureAtlasBuilder { let mut rect_placements = None; let mut atlas_texture = Image::default(); + debug_assert_eq!( + atlas_texture + .texture_descriptor + .format + .describe() + .block_dimensions, + (1, 1), + "Compressed textures are unsupported" + ); + while rect_placements.is_none() { if current_width > max_width || current_height > max_height { break; @@ -186,7 +210,8 @@ impl TextureAtlasBuilder { TextureDimension::D2, vec![ 0; - self.format.pixel_size() * (current_width * current_height) as usize + self.format.describe().block_size as usize + * (current_width * current_height) as usize ], self.format, );