Skip to content

Commit

Permalink
Extend the Texture asset type to support 3D data (#903)
Browse files Browse the repository at this point in the history
Extend the Texture asset type to support 3D data

Textures are still loaded from images as 2D, but they can be reshaped
according to how the render pipeline would like to use them.

Also add an example of how this can be used with the texture2DArray uniform type.
  • Loading branch information
bonsairobo authored Nov 22, 2020
1 parent eb587b2 commit 46fac78
Show file tree
Hide file tree
Showing 17 changed files with 279 additions and 51 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,10 @@ path = "examples/shader/mesh_custom_attribute.rs"
name = "shader_custom_material"
path = "examples/shader/shader_custom_material.rs"

[[example]]
name = "array_texture"
path = "examples/shader/array_texture.rs"

[[example]]
name = "shader_defs"
path = "examples/shader/shader_defs.rs"
Expand Down
Binary file added assets/textures/array_texture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 5 additions & 2 deletions crates/bevy_gltf/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ use bevy_render::{
pipeline::PrimitiveTopology,
prelude::{Color, Texture},
render_graph::base,
texture::{AddressMode, FilterMode, SamplerDescriptor, TextureFormat},
texture::{
AddressMode, Extent3d, FilterMode, SamplerDescriptor, TextureDimension, TextureFormat,
},
};
use bevy_scene::Scene;
use bevy_transform::{
Expand Down Expand Up @@ -136,7 +138,8 @@ async fn load_gltf<'a, 'b>(
&texture_label,
LoadedAsset::new(Texture {
data: image.clone().into_vec(),
size: bevy_math::f32::vec2(size.0 as f32, size.1 as f32),
size: Extent3d::new(size.0, size.1, 1),
dimension: TextureDimension::D2,
format: TextureFormat::Rgba8Unorm,
sampler: texture_sampler(&texture)?,
}),
Expand Down
16 changes: 10 additions & 6 deletions crates/bevy_render/src/render_graph/nodes/texture_copy_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,17 @@ impl Node for TextureCopyNode {
}

let texture_descriptor: TextureDescriptor = texture.into();
let width = texture.size.x as usize;
let aligned_width = render_context
.resources()
.get_aligned_texture_size(texture.size.x as usize);
let width = texture.size.width as usize;
let aligned_width =
render_context.resources().get_aligned_texture_size(width);
let format_size = texture.format.pixel_size();
let mut aligned_data =
vec![0; format_size * aligned_width * texture.size.y as usize];
let mut aligned_data = vec![
0;
format_size
* aligned_width
* texture.size.height as usize
* texture.size.depth as usize
];
texture
.data
.chunks_exact(format_size * width)
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_render/src/texture/hdr_texture_loader.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use super::{Texture, TextureFormat};
use super::{Extent3d, Texture, TextureDimension, TextureFormat};
use anyhow::Result;
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
use bevy_math::Vec2;
use bevy_utils::BoxedFuture;

/// Loads HDR textures as Texture assets
Expand Down Expand Up @@ -37,7 +36,8 @@ impl AssetLoader for HdrTextureLoader {
}

let texture = Texture::new(
Vec2::new(info.width as f32, info.height as f32),
Extent3d::new(info.width, info.height, 1),
TextureDimension::D2,
rgba_data,
format,
);
Expand Down
10 changes: 7 additions & 3 deletions crates/bevy_render/src/texture/image_texture_loader.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use super::{Texture, TextureFormat};
use super::{Extent3d, Texture, TextureDimension, TextureFormat};
use anyhow::Result;
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
use bevy_math::Vec2;
use bevy_utils::BoxedFuture;

/// Loader for images that can be read by the `image` crate.
Expand Down Expand Up @@ -148,7 +147,12 @@ impl AssetLoader for ImageTextureLoader {
}
}

let texture = Texture::new(Vec2::new(width as f32, height as f32), data, format);
let texture = Texture::new(
Extent3d::new(width, height, 1),
TextureDimension::D2,
data,
format,
);
load_context.set_default_asset(LoadedAsset::new(texture));
Ok(())
})
Expand Down
69 changes: 56 additions & 13 deletions crates/bevy_render/src/texture/texture.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use super::{SamplerDescriptor, TextureDescriptor, TextureFormat};
use super::{Extent3d, SamplerDescriptor, TextureDescriptor, TextureDimension, TextureFormat};
use crate::renderer::{
RenderResource, RenderResourceContext, RenderResourceId, RenderResourceType,
};
use bevy_app::prelude::{EventReader, Events};
use bevy_asset::{AssetEvent, Assets, Handle};
use bevy_ecs::{Res, ResMut};
use bevy_math::Vec2;
use bevy_type_registry::TypeUuid;
use bevy_utils::HashSet;

Expand All @@ -16,40 +15,58 @@ pub const SAMPLER_ASSET_INDEX: u64 = 1;
#[uuid = "6ea26da6-6cf8-4ea2-9986-1d7bf6c17d6f"]
pub struct Texture {
pub data: Vec<u8>,
pub size: Vec2,
pub size: Extent3d,
pub format: TextureFormat,
pub dimension: TextureDimension,
pub sampler: SamplerDescriptor,
}

impl Default for Texture {
fn default() -> Self {
Texture {
data: Default::default(),
size: Default::default(),
size: Extent3d {
width: 1,
height: 1,
depth: 1,
},
format: TextureFormat::Rgba8UnormSrgb,
dimension: TextureDimension::D2,
sampler: Default::default(),
}
}
}

impl Texture {
pub fn new(size: Vec2, data: Vec<u8>, format: TextureFormat) -> Self {
pub fn new(
size: Extent3d,
dimension: TextureDimension,
data: Vec<u8>,
format: TextureFormat,
) -> Self {
debug_assert_eq!(
size.x as usize * size.y as usize * format.pixel_size(),
size.volume() * format.pixel_size(),
data.len(),
"Pixel data, size and format have to match",
);
Self {
data,
size,
dimension,
format,
..Default::default()
}
}

pub fn new_fill(size: Vec2, pixel: &[u8], format: TextureFormat) -> Self {
pub fn new_fill(
size: Extent3d,
dimension: TextureDimension,
pixel: &[u8],
format: TextureFormat,
) -> Self {
let mut value = Texture {
format,
dimension,
..Default::default()
};
value.resize(size);
Expand All @@ -70,16 +87,42 @@ impl Texture {
value
}

pub fn aspect(&self) -> f32 {
self.size.y / self.size.x
pub fn aspect_2d(&self) -> f32 {
self.size.height as f32 / self.size.width as f32
}

pub fn resize(&mut self, size: Vec2) {
pub fn resize(&mut self, size: Extent3d) {
self.size = size;
let width = size.x as usize;
let height = size.y as usize;
self.data
.resize(width * height * self.format.pixel_size(), 0);
.resize(size.volume() * self.format.pixel_size(), 0);
}

/// Changes the `size`, asserting that the total number of data elements (pixels) remains the same.
pub fn reinterpret_size(&mut self, new_size: Extent3d) {
assert!(
new_size.volume() == self.size.volume(),
"Incompatible sizes: old = {:?} new = {:?}",
self.size,
new_size
);

self.size = new_size;
}

/// Takes a 2D texture containing vertically stacked images of the same size, and reinterprets it as a 2D array texture,
/// where each of the stacked images becomes one layer of the array. This is primarily for use with the `texture2DArray`
/// shader uniform type.
pub fn reinterpret_stacked_2d_as_array(&mut self, layers: u32) {
// Must be a stacked image, and the height must be divisible by layers.
assert!(self.dimension == TextureDimension::D2);
assert!(self.size.depth == 1);
assert_eq!(self.size.height % layers, 0);

self.reinterpret_size(Extent3d {
width: self.size.width,
height: self.size.height / layers,
depth: layers,
});
}

pub fn texture_resource_system(
Expand Down
8 changes: 2 additions & 6 deletions crates/bevy_render/src/texture/texture_descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,10 @@ pub struct TextureDescriptor {
impl From<&Texture> for TextureDescriptor {
fn from(texture: &Texture) -> Self {
TextureDescriptor {
size: Extent3d {
width: texture.size.x as u32,
height: texture.size.y as u32,
depth: 1,
},
size: texture.size,
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
dimension: texture.dimension,
format: texture.format,
usage: TextureUsage::SAMPLED | TextureUsage::COPY_DST,
}
Expand Down
20 changes: 20 additions & 0 deletions crates/bevy_render/src/texture/texture_dimension.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// NOTE: These are currently just copies of the wgpu types, but they might change in the future

use bevy_math::Vec3;

/// Dimensions of a particular texture view.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum TextureViewDimension {
Expand Down Expand Up @@ -27,6 +29,24 @@ pub struct Extent3d {
pub depth: u32,
}

impl Extent3d {
pub fn new(width: u32, height: u32, depth: u32) -> Self {
Self {
width,
height,
depth,
}
}

pub fn volume(&self) -> usize {
(self.width * self.height * self.depth) as usize
}

pub fn as_vec3(&self) -> Vec3 {
Vec3::new(self.width as f32, self.height as f32, self.depth as f32)
}
}

/// Type of data shaders will read from a texture.
#[derive(Copy, Hash, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum TextureComponentType {
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_sprite/src/dynamic_texture_atlas_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ impl DynamicTextureAtlasBuilder {
texture: &Texture,
) -> Option<u32> {
let allocation = self.atlas_allocator.allocate(size2(
texture.size.x as i32 + self.padding,
texture.size.y as i32 + self.padding,
texture.size.width as i32 + self.padding,
texture.size.height as i32 + self.padding,
));
if let Some(allocation) = allocation {
let atlas_texture = textures.get_mut(&texture_atlas.texture).unwrap();
Expand Down Expand Up @@ -72,7 +72,7 @@ impl DynamicTextureAtlasBuilder {
let mut rect = allocation.rectangle;
rect.max.x -= self.padding;
rect.max.y -= self.padding;
let atlas_width = atlas_texture.size.x as usize;
let atlas_width = atlas_texture.size.width as usize;
let rect_width = rect.width() as usize;
let format_size = atlas_texture.format.pixel_size();

Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_sprite/src/sprite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub fn sprite_system(
let material = materials.get(handle).unwrap();
if let Some(ref texture_handle) = material.texture {
if let Some(texture) = textures.get(texture_handle) {
sprite.size = texture.size;
sprite.size = texture.size.as_vec3().truncate();
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions crates/bevy_sprite/src/texture_atlas_builder.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{Rect, TextureAtlas};
use bevy_asset::{Assets, Handle};
use bevy_math::Vec2;
use bevy_render::texture::{Texture, TextureFormat};
use bevy_render::texture::{Extent3d, Texture, TextureDimension, TextureFormat};
use bevy_utils::HashMap;
use rectangle_pack::{
contains_smallest_box, pack_rects, volume_heuristic, GroupedRectsToPlace, PackedLocation,
Expand Down Expand Up @@ -43,7 +43,7 @@ impl TextureAtlasBuilder {
self.rects_to_place.push_rect(
texture_handle,
None,
RectToInsert::new(texture.size.x as u32, texture.size.y as u32, 1),
RectToInsert::new(texture.size.width, texture.size.height, 1),
)
}

Expand All @@ -57,7 +57,7 @@ impl TextureAtlasBuilder {
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.size.x as usize;
let atlas_width = atlas_texture.size.width as usize;
let format_size = atlas_texture.format.pixel_size();

for (texture_y, bound_y) in (rect_y..rect_y + rect_height).enumerate() {
Expand Down Expand Up @@ -93,7 +93,6 @@ impl TextureAtlasBuilder {

let mut target_bins = std::collections::BTreeMap::new();
target_bins.insert(0, TargetBin::new(current_width, current_height, 1));

rect_placements = match pack_rects(
&self.rects_to_place,
target_bins,
Expand All @@ -102,7 +101,8 @@ impl TextureAtlasBuilder {
) {
Ok(rect_placements) => {
atlas_texture = Texture::new_fill(
Vec2::new(current_width as f32, current_height as f32),
Extent3d::new(current_width, current_height, 1),
TextureDimension::D2,
&[0, 0, 0, 0],
TextureFormat::Rgba8UnormSrgb,
);
Expand Down Expand Up @@ -137,7 +137,7 @@ impl TextureAtlasBuilder {
self.place_texture(&mut atlas_texture, texture, packed_location);
}
Ok(TextureAtlas {
size: atlas_texture.size,
size: atlas_texture.size.as_vec3().truncate(),
texture: textures.add(atlas_texture),
textures: texture_rects,
texture_handles: Some(texture_handles),
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_text/src/font.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use ab_glyph::{FontArc, FontVec, InvalidFont, OutlinedGlyph};
use bevy_math::Vec2;
use bevy_render::{
color::Color,
texture::{Texture, TextureFormat},
texture::{Extent3d, Texture, TextureDimension, TextureFormat},
};
use bevy_type_registry::TypeUuid;

Expand Down Expand Up @@ -36,7 +35,8 @@ impl Font {
(color.b() * 255.0) as u8,
];
Texture::new(
Vec2::new(width as f32, height as f32),
Extent3d::new(width as u32, height as u32, 1),
TextureDimension::D2,
alpha
.iter()
.map(|a| {
Expand Down
5 changes: 3 additions & 2 deletions crates/bevy_text/src/font_atlas.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use ab_glyph::GlyphId;
use bevy_asset::{Assets, Handle};
use bevy_math::Vec2;
use bevy_render::texture::{Texture, TextureFormat};
use bevy_render::texture::{Extent3d, Texture, TextureDimension, TextureFormat};
use bevy_sprite::{DynamicTextureAtlasBuilder, TextureAtlas};
use bevy_utils::HashMap;

Expand All @@ -18,7 +18,8 @@ impl FontAtlas {
size: Vec2,
) -> FontAtlas {
let atlas_texture = textures.add(Texture::new_fill(
size,
Extent3d::new(size.x as u32, size.y as u32, 1),
TextureDimension::D2,
&[0, 0, 0, 0],
TextureFormat::Rgba8UnormSrgb,
));
Expand Down
Loading

0 comments on commit 46fac78

Please sign in to comment.