diff --git a/crates/bevy_render/src/render_graph/nodes/texture_copy_node.rs b/crates/bevy_render/src/render_graph/nodes/texture_copy_node.rs index 4164cc047f..5e59b37e07 100644 --- a/crates/bevy_render/src/render_graph/nodes/texture_copy_node.rs +++ b/crates/bevy_render/src/render_graph/nodes/texture_copy_node.rs @@ -35,7 +35,7 @@ impl Node for TextureCopyNode { let texture_descriptor: TextureDescriptor = texture.into(); let width = texture.size.x() as usize; let aligned_width = get_aligned(texture.size.x()); - let format_size = 4; // TODO: this will be incorrect for some formats + let format_size = texture.format.pixel_size(); let mut aligned_data = vec![0; format_size * aligned_width * texture.size.y() as usize]; texture diff --git a/crates/bevy_render/src/texture/png_texture_loader.rs b/crates/bevy_render/src/texture/png_texture_loader.rs index d581534f6c..daeb9ec321 100644 --- a/crates/bevy_render/src/texture/png_texture_loader.rs +++ b/crates/bevy_render/src/texture/png_texture_loader.rs @@ -1,4 +1,4 @@ -use super::Texture; +use super::{Texture, TextureFormat}; use anyhow::Result; use bevy_asset::AssetLoader; use bevy_math::Vec2; @@ -13,10 +13,11 @@ impl AssetLoader for PngTextureLoader { let (info, mut reader) = decoder.read_info()?; let mut data = vec![0; info.buffer_size()]; reader.next_frame(&mut data)?; - Ok(Texture { + Ok(Texture::new( + Vec2::new(info.width as f32, info.height as f32), data, - size: Vec2::new(info.width as f32, info.height as f32), - }) + TextureFormat::Rgba8UnormSrgb, + )) } fn extensions(&self) -> &[&str] { static EXTENSIONS: &[&str] = &["png"]; diff --git a/crates/bevy_render/src/texture/texture.rs b/crates/bevy_render/src/texture/texture.rs index 7148c5030f..546ceaf757 100644 --- a/crates/bevy_render/src/texture/texture.rs +++ b/crates/bevy_render/src/texture/texture.rs @@ -1,4 +1,4 @@ -use super::{SamplerDescriptor, TextureDescriptor}; +use super::{SamplerDescriptor, TextureDescriptor, TextureFormat}; use crate::renderer::{ RenderResource, RenderResourceContext, RenderResourceId, RenderResourceType, }; @@ -11,22 +11,48 @@ use std::collections::HashSet; pub const TEXTURE_ASSET_INDEX: usize = 0; pub const SAMPLER_ASSET_INDEX: usize = 1; -#[derive(Default, Clone)] +#[derive(Clone)] pub struct Texture { pub data: Vec, pub size: Vec2, + pub format: TextureFormat, } -const FORMAT_SIZE: usize = 4; // TODO: get this from an actual format type +impl Default for Texture { + fn default() -> Self { + Texture { + data: Default::default(), + size: Default::default(), + format: TextureFormat::Rgba8UnormSrgb, + } + } +} impl Texture { - pub fn new(data: Vec, size: Vec2) -> Self { - Self { data, size } + pub fn new(size: Vec2, data: Vec, format: TextureFormat) -> Self { + debug_assert_eq!( + size.x() as usize * size.y() as usize * format.pixel_size(), + data.len(), + "Pixel data, size and format have to match", + ); + Self { data, size, format } } - pub fn new_fill(size: Vec2, pixel: &[u8]) -> Self { + pub fn new_fill(size: Vec2, pixel: &[u8], format: TextureFormat) -> Self { let mut value = Self::default(); + value.format = format; value.resize(size); + + debug_assert_eq!( + pixel.len() % format.pixel_size(), + 0, + "Must not have incomplete pixel data" + ); + debug_assert!( + pixel.len() <= value.data.len(), + "Fill data must fit within pixel buffer" + ); + for current_pixel in value.data.chunks_exact_mut(pixel.len()) { current_pixel.copy_from_slice(&pixel); } @@ -41,7 +67,7 @@ impl Texture { self.size = size; let width = size.x() as usize; let height = size.y() as usize; - self.data.resize(width * height * FORMAT_SIZE, 0); + self.data.resize(width * height * self.format.pixel_size(), 0); } pub fn texture_resource_system( diff --git a/crates/bevy_render/src/texture/texture_descriptor.rs b/crates/bevy_render/src/texture/texture_descriptor.rs index 482ac5dd22..114f4aa1a5 100644 --- a/crates/bevy_render/src/texture/texture_descriptor.rs +++ b/crates/bevy_render/src/texture/texture_descriptor.rs @@ -21,7 +21,7 @@ impl From<&Texture> for TextureDescriptor { mip_level_count: 1, sample_count: 1, dimension: TextureDimension::D2, - format: TextureFormat::Rgba8UnormSrgb, + format: texture.format, usage: TextureUsage::SAMPLED | TextureUsage::COPY_DST, } } diff --git a/crates/bevy_render/src/texture/texture_dimension.rs b/crates/bevy_render/src/texture/texture_dimension.rs index b702bb3b4b..26ae3e88c7 100644 --- a/crates/bevy_render/src/texture/texture_dimension.rs +++ b/crates/bevy_render/src/texture/texture_dimension.rs @@ -32,6 +32,11 @@ pub enum TextureComponentType { Uint, } +pub struct PixelInfo { + pub type_size: usize, + pub num_components: usize, +} + #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] pub enum TextureFormat { // Normal 8 bit formats @@ -87,6 +92,113 @@ pub enum TextureFormat { Depth24PlusStencil8 = 37, } +impl TextureFormat { + pub fn pixel_info(&self) -> PixelInfo { + let type_size = match self { + // 8bit + TextureFormat::R8Unorm + | TextureFormat::R8Snorm + | TextureFormat::R8Uint + | TextureFormat::R8Sint + | TextureFormat::Rg8Unorm + | TextureFormat::Rg8Snorm + | TextureFormat::Rg8Uint + | TextureFormat::Rg8Sint + | TextureFormat::Rgba8Unorm + | TextureFormat::Rgba8UnormSrgb + | TextureFormat::Rgba8Snorm + | TextureFormat::Rgba8Uint + | TextureFormat::Rgba8Sint + | TextureFormat::Bgra8Unorm + | TextureFormat::Bgra8UnormSrgb => 1, + + // 16bit + TextureFormat::R16Uint + | TextureFormat::R16Sint + | TextureFormat::R16Float + | TextureFormat::Rg16Uint + | TextureFormat::Rg16Sint + | TextureFormat::Rg16Float + | TextureFormat::Rgba16Uint + | TextureFormat::Rgba16Sint + | TextureFormat::Rgba16Float => 2, + + // 32bit + TextureFormat::R32Uint + | TextureFormat::R32Sint + | TextureFormat::R32Float + | TextureFormat::Rg32Uint + | TextureFormat::Rg32Sint + | TextureFormat::Rg32Float + | TextureFormat::Rgba32Uint + | TextureFormat::Rgba32Sint + | TextureFormat::Rgba32Float + | TextureFormat::Depth32Float => 4, + + // special cases + TextureFormat::Rgb10a2Unorm => 4, + TextureFormat::Rg11b10Float => 4, + TextureFormat::Depth24Plus => 3, // FIXME is this correct? + TextureFormat::Depth24PlusStencil8 => 4, + }; + + let components = match self { + TextureFormat::R8Unorm + | TextureFormat::R8Snorm + | TextureFormat::R8Uint + | TextureFormat::R8Sint + | TextureFormat::R16Uint + | TextureFormat::R16Sint + | TextureFormat::R16Float + | TextureFormat::R32Uint + | TextureFormat::R32Sint + | TextureFormat::R32Float => 1, + + TextureFormat::Rg8Unorm + | TextureFormat::Rg8Snorm + | TextureFormat::Rg8Uint + | TextureFormat::Rg8Sint + | TextureFormat::Rg16Uint + | TextureFormat::Rg16Sint + | TextureFormat::Rg16Float + | TextureFormat::Rg32Uint + | TextureFormat::Rg32Sint + | TextureFormat::Rg32Float => 2, + + TextureFormat::Rgba8Unorm + | TextureFormat::Rgba8UnormSrgb + | TextureFormat::Rgba8Snorm + | TextureFormat::Rgba8Uint + | TextureFormat::Rgba8Sint + | TextureFormat::Bgra8Unorm + | TextureFormat::Bgra8UnormSrgb + | TextureFormat::Rgba16Uint + | TextureFormat::Rgba16Sint + | TextureFormat::Rgba16Float + | TextureFormat::Rgba32Uint + | TextureFormat::Rgba32Sint + | TextureFormat::Rgba32Float => 4, + + // special cases + TextureFormat::Rgb10a2Unorm + | TextureFormat::Rg11b10Float + | TextureFormat::Depth32Float + | TextureFormat::Depth24Plus + | TextureFormat::Depth24PlusStencil8 => 1, + }; + + PixelInfo { + type_size, + num_components: components, + } + } + + pub fn pixel_size(&self) -> usize { + let info = self.pixel_info(); + info.type_size * info.num_components + } +} + bitflags::bitflags! { #[repr(transparent)] pub struct TextureUsage: u32 { diff --git a/crates/bevy_sprite/src/dynamic_texture_atlas_builder.rs b/crates/bevy_sprite/src/dynamic_texture_atlas_builder.rs index 13c643d170..3f227ac760 100644 --- a/crates/bevy_sprite/src/dynamic_texture_atlas_builder.rs +++ b/crates/bevy_sprite/src/dynamic_texture_atlas_builder.rs @@ -10,7 +10,6 @@ pub struct DynamicTextureAtlasBuilder { pub atlas_allocator: AtlasAllocator, } -const FORMAT_SIZE: usize = 4; // TODO: get this from an actual format type impl DynamicTextureAtlasBuilder { pub fn new(size: Vec2) -> Self { Self { @@ -70,12 +69,13 @@ impl DynamicTextureAtlasBuilder { let rect = allocation.rectangle; let atlas_width = atlas_texture.size.x() as usize; let rect_width = rect.width() as usize; + let format_size = atlas_texture.format.pixel_size(); 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; - let end = begin + rect_width * FORMAT_SIZE; - let texture_begin = texture_y * rect_width * FORMAT_SIZE; - let texture_end = texture_begin + rect_width * FORMAT_SIZE; + let begin = (bound_y * atlas_width + rect.min.x as usize) * format_size; + let end = begin + rect_width * format_size; + let texture_begin = texture_y * rect_width * format_size; + let texture_end = texture_begin + rect_width * format_size; atlas_texture.data[begin..end] .copy_from_slice(&texture.data[texture_begin..texture_end]); } diff --git a/crates/bevy_sprite/src/texture_atlas_builder.rs b/crates/bevy_sprite/src/texture_atlas_builder.rs index cebbf6463c..3228aa7412 100644 --- a/crates/bevy_sprite/src/texture_atlas_builder.rs +++ b/crates/bevy_sprite/src/texture_atlas_builder.rs @@ -1,7 +1,7 @@ use crate::{Rect, TextureAtlas}; use bevy_asset::{Assets, Handle}; use bevy_math::Vec2; -use bevy_render::texture::Texture; +use bevy_render::texture::{Texture, TextureFormat}; use rectangle_pack::{ contains_smallest_box, pack_rects, volume_heuristic, GroupedRectsToPlace, PackedLocation, RectToInsert, TargetBin, @@ -28,7 +28,6 @@ pub enum RectanglePackError { NotEnoughSpace, } -const FORMAT_SIZE: usize = 4; // TODO: get this from an actual format type impl TextureAtlasBuilder { pub fn new(initial_size: Vec2, max_size: Vec2) -> Self { Self { @@ -58,12 +57,13 @@ impl TextureAtlasBuilder { let rect_x = packed_location.x() as usize; let rect_y = packed_location.y() as usize; let atlas_width = atlas_texture.size.x() as usize; + let format_size = atlas_texture.format.pixel_size(); for (texture_y, bound_y) in (rect_y..rect_y + rect_height).enumerate() { - let begin = (bound_y * atlas_width + rect_x) * FORMAT_SIZE; - let end = begin + rect_width * FORMAT_SIZE; - let texture_begin = texture_y * rect_width * FORMAT_SIZE; - let texture_end = texture_begin + rect_width * FORMAT_SIZE; + let begin = (bound_y * atlas_width + rect_x) * format_size; + let end = begin + rect_width * format_size; + let texture_begin = texture_y * rect_width * format_size; + let texture_end = texture_begin + rect_width * format_size; atlas_texture.data[begin..end] .copy_from_slice(&texture.data[texture_begin..texture_end]); } @@ -93,6 +93,7 @@ impl TextureAtlasBuilder { atlas_texture = Texture::new_fill( Vec2::new(current_width as f32, current_height as f32), &[0, 0, 0, 0], + TextureFormat::Rgba8UnormSrgb, ); rect_placements = match pack_rects( &self.rects_to_place, diff --git a/crates/bevy_text/src/font.rs b/crates/bevy_text/src/font.rs index 8b44813c99..93e61248ac 100644 --- a/crates/bevy_text/src/font.rs +++ b/crates/bevy_text/src/font.rs @@ -1,6 +1,9 @@ use ab_glyph::{FontVec, Glyph, InvalidFont, OutlinedGlyph, Point, PxScale, ScaleFont}; use bevy_math::Vec2; -use bevy_render::{color::Color, texture::Texture}; +use bevy_render::{ + color::Color, + texture::{Texture, TextureFormat}, +}; pub struct Font { pub font: FontVec, @@ -32,6 +35,7 @@ impl Font { (color.b * 255.0) as u8, ]; Texture::new( + Vec2::new(width as f32, height as f32), alpha .iter() .map(|a| { @@ -44,7 +48,7 @@ impl Font { }) .flatten() .collect::>(), - Vec2::new(width as f32, height as f32), + TextureFormat::Rgba8UnormSrgb, ) } @@ -96,6 +100,7 @@ impl Font { } Texture::new( + Vec2::new(width as f32, height as f32), alpha .iter() .map(|a| { @@ -108,7 +113,7 @@ impl Font { }) .flatten() .collect::>(), - Vec2::new(width as f32, height as f32), + TextureFormat::Rgba8UnormSrgb, ) } } diff --git a/crates/bevy_text/src/font_atlas.rs b/crates/bevy_text/src/font_atlas.rs index ce517bb703..3c70491a3f 100644 --- a/crates/bevy_text/src/font_atlas.rs +++ b/crates/bevy_text/src/font_atlas.rs @@ -1,6 +1,6 @@ use bevy_asset::{Assets, Handle}; use bevy_math::Vec2; -use bevy_render::texture::Texture; +use bevy_render::texture::{Texture, TextureFormat}; use bevy_sprite::{DynamicTextureAtlasBuilder, TextureAtlas}; use std::collections::HashMap; @@ -16,7 +16,11 @@ impl FontAtlas { texture_atlases: &mut Assets, size: Vec2, ) -> FontAtlas { - let atlas_texture = textures.add(Texture::new_fill(size, &[0, 0, 0, 0])); + let atlas_texture = textures.add(Texture::new_fill( + size, + &[0, 0, 0, 0], + TextureFormat::Rgba8UnormSrgb, + )); let texture_atlas = TextureAtlas::new_empty(atlas_texture, size); Self { texture_atlas: texture_atlases.add(texture_atlas),