Skip to content

Commit 46fac78

Browse files
author
Duncan
authored
Extend the Texture asset type to support 3D data (#903)
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.
1 parent eb587b2 commit 46fac78

17 files changed

Lines changed: 279 additions & 51 deletions

File tree

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,10 @@ path = "examples/shader/mesh_custom_attribute.rs"
266266
name = "shader_custom_material"
267267
path = "examples/shader/shader_custom_material.rs"
268268

269+
[[example]]
270+
name = "array_texture"
271+
path = "examples/shader/array_texture.rs"
272+
269273
[[example]]
270274
name = "shader_defs"
271275
path = "examples/shader/shader_defs.rs"

assets/textures/array_texture.png

460 KB
Loading

crates/bevy_gltf/src/loader.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ use bevy_render::{
1111
pipeline::PrimitiveTopology,
1212
prelude::{Color, Texture},
1313
render_graph::base,
14-
texture::{AddressMode, FilterMode, SamplerDescriptor, TextureFormat},
14+
texture::{
15+
AddressMode, Extent3d, FilterMode, SamplerDescriptor, TextureDimension, TextureFormat,
16+
},
1517
};
1618
use bevy_scene::Scene;
1719
use bevy_transform::{
@@ -136,7 +138,8 @@ async fn load_gltf<'a, 'b>(
136138
&texture_label,
137139
LoadedAsset::new(Texture {
138140
data: image.clone().into_vec(),
139-
size: bevy_math::f32::vec2(size.0 as f32, size.1 as f32),
141+
size: Extent3d::new(size.0, size.1, 1),
142+
dimension: TextureDimension::D2,
140143
format: TextureFormat::Rgba8Unorm,
141144
sampler: texture_sampler(&texture)?,
142145
}),

crates/bevy_render/src/render_graph/nodes/texture_copy_node.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,17 @@ impl Node for TextureCopyNode {
3434
}
3535

3636
let texture_descriptor: TextureDescriptor = texture.into();
37-
let width = texture.size.x as usize;
38-
let aligned_width = render_context
39-
.resources()
40-
.get_aligned_texture_size(texture.size.x as usize);
37+
let width = texture.size.width as usize;
38+
let aligned_width =
39+
render_context.resources().get_aligned_texture_size(width);
4140
let format_size = texture.format.pixel_size();
42-
let mut aligned_data =
43-
vec![0; format_size * aligned_width * texture.size.y as usize];
41+
let mut aligned_data = vec![
42+
0;
43+
format_size
44+
* aligned_width
45+
* texture.size.height as usize
46+
* texture.size.depth as usize
47+
];
4448
texture
4549
.data
4650
.chunks_exact(format_size * width)

crates/bevy_render/src/texture/hdr_texture_loader.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
use super::{Texture, TextureFormat};
1+
use super::{Extent3d, Texture, TextureDimension, TextureFormat};
22
use anyhow::Result;
33
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
4-
use bevy_math::Vec2;
54
use bevy_utils::BoxedFuture;
65

76
/// Loads HDR textures as Texture assets
@@ -37,7 +36,8 @@ impl AssetLoader for HdrTextureLoader {
3736
}
3837

3938
let texture = Texture::new(
40-
Vec2::new(info.width as f32, info.height as f32),
39+
Extent3d::new(info.width, info.height, 1),
40+
TextureDimension::D2,
4141
rgba_data,
4242
format,
4343
);

crates/bevy_render/src/texture/image_texture_loader.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
use super::{Texture, TextureFormat};
1+
use super::{Extent3d, Texture, TextureDimension, TextureFormat};
22
use anyhow::Result;
33
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
4-
use bevy_math::Vec2;
54
use bevy_utils::BoxedFuture;
65

76
/// Loader for images that can be read by the `image` crate.
@@ -148,7 +147,12 @@ impl AssetLoader for ImageTextureLoader {
148147
}
149148
}
150149

151-
let texture = Texture::new(Vec2::new(width as f32, height as f32), data, format);
150+
let texture = Texture::new(
151+
Extent3d::new(width, height, 1),
152+
TextureDimension::D2,
153+
data,
154+
format,
155+
);
152156
load_context.set_default_asset(LoadedAsset::new(texture));
153157
Ok(())
154158
})

crates/bevy_render/src/texture/texture.rs

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
use super::{SamplerDescriptor, TextureDescriptor, TextureFormat};
1+
use super::{Extent3d, SamplerDescriptor, TextureDescriptor, TextureDimension, TextureFormat};
22
use crate::renderer::{
33
RenderResource, RenderResourceContext, RenderResourceId, RenderResourceType,
44
};
55
use bevy_app::prelude::{EventReader, Events};
66
use bevy_asset::{AssetEvent, Assets, Handle};
77
use bevy_ecs::{Res, ResMut};
8-
use bevy_math::Vec2;
98
use bevy_type_registry::TypeUuid;
109
use bevy_utils::HashSet;
1110

@@ -16,40 +15,58 @@ pub const SAMPLER_ASSET_INDEX: u64 = 1;
1615
#[uuid = "6ea26da6-6cf8-4ea2-9986-1d7bf6c17d6f"]
1716
pub struct Texture {
1817
pub data: Vec<u8>,
19-
pub size: Vec2,
18+
pub size: Extent3d,
2019
pub format: TextureFormat,
20+
pub dimension: TextureDimension,
2121
pub sampler: SamplerDescriptor,
2222
}
2323

2424
impl Default for Texture {
2525
fn default() -> Self {
2626
Texture {
2727
data: Default::default(),
28-
size: Default::default(),
28+
size: Extent3d {
29+
width: 1,
30+
height: 1,
31+
depth: 1,
32+
},
2933
format: TextureFormat::Rgba8UnormSrgb,
34+
dimension: TextureDimension::D2,
3035
sampler: Default::default(),
3136
}
3237
}
3338
}
3439

3540
impl Texture {
36-
pub fn new(size: Vec2, data: Vec<u8>, format: TextureFormat) -> Self {
41+
pub fn new(
42+
size: Extent3d,
43+
dimension: TextureDimension,
44+
data: Vec<u8>,
45+
format: TextureFormat,
46+
) -> Self {
3747
debug_assert_eq!(
38-
size.x as usize * size.y as usize * format.pixel_size(),
48+
size.volume() * format.pixel_size(),
3949
data.len(),
4050
"Pixel data, size and format have to match",
4151
);
4252
Self {
4353
data,
4454
size,
55+
dimension,
4556
format,
4657
..Default::default()
4758
}
4859
}
4960

50-
pub fn new_fill(size: Vec2, pixel: &[u8], format: TextureFormat) -> Self {
61+
pub fn new_fill(
62+
size: Extent3d,
63+
dimension: TextureDimension,
64+
pixel: &[u8],
65+
format: TextureFormat,
66+
) -> Self {
5167
let mut value = Texture {
5268
format,
69+
dimension,
5370
..Default::default()
5471
};
5572
value.resize(size);
@@ -70,16 +87,42 @@ impl Texture {
7087
value
7188
}
7289

73-
pub fn aspect(&self) -> f32 {
74-
self.size.y / self.size.x
90+
pub fn aspect_2d(&self) -> f32 {
91+
self.size.height as f32 / self.size.width as f32
7592
}
7693

77-
pub fn resize(&mut self, size: Vec2) {
94+
pub fn resize(&mut self, size: Extent3d) {
7895
self.size = size;
79-
let width = size.x as usize;
80-
let height = size.y as usize;
8196
self.data
82-
.resize(width * height * self.format.pixel_size(), 0);
97+
.resize(size.volume() * self.format.pixel_size(), 0);
98+
}
99+
100+
/// Changes the `size`, asserting that the total number of data elements (pixels) remains the same.
101+
pub fn reinterpret_size(&mut self, new_size: Extent3d) {
102+
assert!(
103+
new_size.volume() == self.size.volume(),
104+
"Incompatible sizes: old = {:?} new = {:?}",
105+
self.size,
106+
new_size
107+
);
108+
109+
self.size = new_size;
110+
}
111+
112+
/// Takes a 2D texture containing vertically stacked images of the same size, and reinterprets it as a 2D array texture,
113+
/// where each of the stacked images becomes one layer of the array. This is primarily for use with the `texture2DArray`
114+
/// shader uniform type.
115+
pub fn reinterpret_stacked_2d_as_array(&mut self, layers: u32) {
116+
// Must be a stacked image, and the height must be divisible by layers.
117+
assert!(self.dimension == TextureDimension::D2);
118+
assert!(self.size.depth == 1);
119+
assert_eq!(self.size.height % layers, 0);
120+
121+
self.reinterpret_size(Extent3d {
122+
width: self.size.width,
123+
height: self.size.height / layers,
124+
depth: layers,
125+
});
83126
}
84127

85128
pub fn texture_resource_system(

crates/bevy_render/src/texture/texture_descriptor.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,10 @@ pub struct TextureDescriptor {
1414
impl From<&Texture> for TextureDescriptor {
1515
fn from(texture: &Texture) -> Self {
1616
TextureDescriptor {
17-
size: Extent3d {
18-
width: texture.size.x as u32,
19-
height: texture.size.y as u32,
20-
depth: 1,
21-
},
17+
size: texture.size,
2218
mip_level_count: 1,
2319
sample_count: 1,
24-
dimension: TextureDimension::D2,
20+
dimension: texture.dimension,
2521
format: texture.format,
2622
usage: TextureUsage::SAMPLED | TextureUsage::COPY_DST,
2723
}

crates/bevy_render/src/texture/texture_dimension.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// NOTE: These are currently just copies of the wgpu types, but they might change in the future
22

3+
use bevy_math::Vec3;
4+
35
/// Dimensions of a particular texture view.
46
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
57
pub enum TextureViewDimension {
@@ -27,6 +29,24 @@ pub struct Extent3d {
2729
pub depth: u32,
2830
}
2931

32+
impl Extent3d {
33+
pub fn new(width: u32, height: u32, depth: u32) -> Self {
34+
Self {
35+
width,
36+
height,
37+
depth,
38+
}
39+
}
40+
41+
pub fn volume(&self) -> usize {
42+
(self.width * self.height * self.depth) as usize
43+
}
44+
45+
pub fn as_vec3(&self) -> Vec3 {
46+
Vec3::new(self.width as f32, self.height as f32, self.depth as f32)
47+
}
48+
}
49+
3050
/// Type of data shaders will read from a texture.
3151
#[derive(Copy, Hash, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
3252
pub enum TextureComponentType {

crates/bevy_sprite/src/dynamic_texture_atlas_builder.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ impl DynamicTextureAtlasBuilder {
2424
texture: &Texture,
2525
) -> Option<u32> {
2626
let allocation = self.atlas_allocator.allocate(size2(
27-
texture.size.x as i32 + self.padding,
28-
texture.size.y as i32 + self.padding,
27+
texture.size.width as i32 + self.padding,
28+
texture.size.height as i32 + self.padding,
2929
));
3030
if let Some(allocation) = allocation {
3131
let atlas_texture = textures.get_mut(&texture_atlas.texture).unwrap();
@@ -72,7 +72,7 @@ impl DynamicTextureAtlasBuilder {
7272
let mut rect = allocation.rectangle;
7373
rect.max.x -= self.padding;
7474
rect.max.y -= self.padding;
75-
let atlas_width = atlas_texture.size.x as usize;
75+
let atlas_width = atlas_texture.size.width as usize;
7676
let rect_width = rect.width() as usize;
7777
let format_size = atlas_texture.format.pixel_size();
7878

0 commit comments

Comments
 (0)