use TextureFormat for Textures

This commit also inserts debug asserts that texture data roughly respects
the format.
This commit is contained in:
Thomas Herzog 2020-07-26 21:08:41 +02:00
parent b4c185eb0c
commit 4cf0f53eae
9 changed files with 178 additions and 29 deletions

View file

@ -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

View file

@ -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<Texture> 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"];

View file

@ -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<u8>,
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<u8>, size: Vec2) -> Self {
Self { data, size }
pub fn new(size: Vec2, data: Vec<u8>, 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(

View file

@ -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,
}
}

View file

@ -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 {

View file

@ -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]);
}

View file

@ -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,

View file

@ -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::<Vec<u8>>(),
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::<Vec<u8>>(),
Vec2::new(width as f32, height as f32),
TextureFormat::Rgba8UnormSrgb,
)
}
}

View file

@ -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<TextureAtlas>,
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),