use wgpu TextureDataOrder (#19829)

# Objective

- Fixes #19140

## Solution

- Use TextureDataOrder

## Testing

- ran some examples that use ktx2 textures, they look fine
This commit is contained in:
atlv 2025-06-27 02:57:29 -04:00 committed by GitHub
parent 479c93d72b
commit 5fabb5343d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 15 additions and 38 deletions

View File

@ -447,6 +447,7 @@ pub fn lut_placeholder() -> Image {
let data = vec![255, 0, 255, 255]; let data = vec![255, 0, 255, 255];
Image { Image {
data: Some(data), data: Some(data),
data_order: TextureDataOrder::default(),
texture_descriptor: TextureDescriptor { texture_descriptor: TextureDescriptor {
size: Extent3d::default(), size: Extent3d::default(),
format, format,

View File

@ -18,8 +18,8 @@ use serde::{Deserialize, Serialize};
use thiserror::Error; use thiserror::Error;
use wgpu_types::{ use wgpu_types::{
AddressMode, CompareFunction, Extent3d, Features, FilterMode, SamplerBorderColor, AddressMode, CompareFunction, Extent3d, Features, FilterMode, SamplerBorderColor,
SamplerDescriptor, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, SamplerDescriptor, TextureDataOrder, TextureDescriptor, TextureDimension, TextureFormat,
TextureViewDescriptor, TextureUsages, TextureViewDescriptor,
}; };
/// Trait used to provide default values for Bevy-external types that /// Trait used to provide default values for Bevy-external types that
@ -372,6 +372,10 @@ pub struct Image {
/// CPU, then this should be `None`. /// CPU, then this should be `None`.
/// Otherwise, it should always be `Some`. /// Otherwise, it should always be `Some`.
pub data: Option<Vec<u8>>, pub data: Option<Vec<u8>>,
/// For texture data with layers and mips, this field controls how wgpu interprets the buffer layout.
///
/// Use [`TextureDataOrder::default()`] for all other cases.
pub data_order: TextureDataOrder,
// TODO: this nesting makes accessing Image metadata verbose. Either flatten out descriptor or add accessors. // TODO: this nesting makes accessing Image metadata verbose. Either flatten out descriptor or add accessors.
pub texture_descriptor: TextureDescriptor<Option<&'static str>, &'static [TextureFormat]>, pub texture_descriptor: TextureDescriptor<Option<&'static str>, &'static [TextureFormat]>,
/// The [`ImageSampler`] to use during rendering. /// The [`ImageSampler`] to use during rendering.
@ -764,6 +768,7 @@ impl Image {
) -> Self { ) -> Self {
Image { Image {
data: None, data: None,
data_order: TextureDataOrder::default(),
texture_descriptor: TextureDescriptor { texture_descriptor: TextureDescriptor {
size, size,
format, format,

View File

@ -238,43 +238,12 @@ pub fn ktx2_buffer_to_image(
))); )));
} }
// Reorder data from KTX2 MipXLayerYFaceZ to wgpu LayerYFaceZMipX
let texture_format_info = texture_format;
let (block_width_pixels, block_height_pixels) = (
texture_format_info.block_dimensions().0 as usize,
texture_format_info.block_dimensions().1 as usize,
);
// Texture is not a depth or stencil format, it is possible to pass `None` and unwrap
let block_bytes = texture_format_info.block_copy_size(None).unwrap() as usize;
let mut wgpu_data = vec![Vec::default(); (layer_count * face_count) as usize];
for (level, level_data) in levels.iter().enumerate() {
let (level_width, level_height, level_depth) = (
(width as usize >> level).max(1),
(height as usize >> level).max(1),
(depth as usize >> level).max(1),
);
let (num_blocks_x, num_blocks_y) = (
level_width.div_ceil(block_width_pixels).max(1),
level_height.div_ceil(block_height_pixels).max(1),
);
let level_bytes = num_blocks_x * num_blocks_y * level_depth * block_bytes;
let mut index = 0;
for _layer in 0..layer_count {
for _face in 0..face_count {
let offset = index * level_bytes;
wgpu_data[index].extend_from_slice(&level_data[offset..(offset + level_bytes)]);
index += 1;
}
}
}
// Assign the data and fill in the rest of the metadata now the possible // Assign the data and fill in the rest of the metadata now the possible
// error cases have been handled // error cases have been handled
let mut image = Image::default(); let mut image = Image::default();
image.texture_descriptor.format = texture_format; image.texture_descriptor.format = texture_format;
image.data = Some(wgpu_data.into_iter().flatten().collect::<Vec<_>>()); image.data = Some(levels.into_iter().flatten().collect::<Vec<_>>());
image.data_order = wgpu_types::TextureDataOrder::MipMajor;
// Note: we must give wgpu the logical texture dimensions, so it can correctly compute mip sizes. // Note: we must give wgpu the logical texture dimensions, so it can correctly compute mip sizes.
// However this currently causes wgpu to panic if the dimensions arent a multiple of blocksize. // However this currently causes wgpu to panic if the dimensions arent a multiple of blocksize.
// See https://github.com/gfx-rs/wgpu/issues/7677 for more context. // See https://github.com/gfx-rs/wgpu/issues/7677 for more context.

View File

@ -51,8 +51,7 @@ impl RenderAsset for GpuImage {
render_device.create_texture_with_data( render_device.create_texture_with_data(
render_queue, render_queue,
&image.texture_descriptor, &image.texture_descriptor,
// TODO: Is this correct? Do we need to use `MipMajor` if it's a ktx2 file? image.data_order,
wgpu::util::TextureDataOrder::default(),
data, data,
) )
} else { } else {

View File

@ -16,7 +16,9 @@ use bevy_math::{FloatOrd, UVec2, Vec2, Vec3};
use bevy_platform::collections::HashMap; use bevy_platform::collections::HashMap;
use bevy_render::{ use bevy_render::{
mesh::{Indices, Mesh, Mesh2d, PrimitiveTopology}, mesh::{Indices, Mesh, Mesh2d, PrimitiveTopology},
render_resource::{TextureDescriptor, TextureDimension, TextureFormat, TextureUsages}, render_resource::{
TextureDataOrder, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
},
}; };
use tracing::warn; use tracing::warn;
@ -198,6 +200,7 @@ fn make_chunk_image(size: &UVec2, indices: &[Option<u16>]) -> Image {
.flat_map(|i| u16::to_ne_bytes(i.unwrap_or(u16::MAX))) .flat_map(|i| u16::to_ne_bytes(i.unwrap_or(u16::MAX)))
.collect(), .collect(),
), ),
data_order: TextureDataOrder::default(),
texture_descriptor: TextureDescriptor { texture_descriptor: TextureDescriptor {
size: size.to_extents(), size: size.to_extents(),
dimension: TextureDimension::D2, dimension: TextureDimension::D2,