Load and convert RGB8 dds textures (#12952)
# Objective - Closes #12944. ## Solution - Load `R8G8B8` textures by transcoding to an rgba format since `wgpu` does not support texture formats with 3 channels. - Switch to erroring out instead of panicking on an invalid dds file. --- ## Changelog ### Added - DDS Textures with the `R8G8B8` format are now supported. They require an additional conversion step, so using `R8G8B8A8` or a similar format is preferable for texture loading performance.
This commit is contained in:
parent
0855a0e8ad
commit
7c7b1e9fc0
@ -8,7 +8,7 @@ use wgpu_types::{
|
||||
#[cfg(debug_assertions)]
|
||||
use {bevy_utils::once, tracing::warn};
|
||||
|
||||
use super::{CompressedImageFormats, Image, TextureError};
|
||||
use super::{CompressedImageFormats, Image, TextureError, TranscodeFormat};
|
||||
|
||||
#[cfg(feature = "dds")]
|
||||
pub fn dds_buffer_to_image(
|
||||
@ -20,7 +20,18 @@ pub fn dds_buffer_to_image(
|
||||
let mut cursor = Cursor::new(buffer);
|
||||
let dds = Dds::read(&mut cursor)
|
||||
.map_err(|error| TextureError::InvalidData(format!("Failed to parse DDS file: {error}")))?;
|
||||
let texture_format = dds_format_to_texture_format(&dds, is_srgb)?;
|
||||
let (texture_format, transcode_format) = match dds_format_to_texture_format(&dds, is_srgb) {
|
||||
Ok(format) => (format, None),
|
||||
Err(TextureError::FormatRequiresTranscodingError(TranscodeFormat::Rgb8)) => {
|
||||
let format = if is_srgb {
|
||||
TextureFormat::Bgra8UnormSrgb
|
||||
} else {
|
||||
TextureFormat::Bgra8Unorm
|
||||
};
|
||||
(format, Some(TranscodeFormat::Rgb8))
|
||||
}
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
if !supported_compressed_formats.supports(texture_format) {
|
||||
return Err(TextureError::UnsupportedTextureFormat(format!(
|
||||
"Format not supported by this GPU: {texture_format:?}",
|
||||
@ -86,7 +97,29 @@ pub fn dds_buffer_to_image(
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
image.data = Some(dds.data);
|
||||
|
||||
// DDS mipmap layout is directly compatible with wgpu's layout (Slice -> Face -> Mip):
|
||||
// https://learn.microsoft.com/fr-fr/windows/win32/direct3ddds/dx-graphics-dds-reference
|
||||
image.data = if let Some(transcode_format) = transcode_format {
|
||||
match transcode_format {
|
||||
TranscodeFormat::Rgb8 => {
|
||||
let data = dds
|
||||
.data
|
||||
.chunks_exact(3)
|
||||
.flat_map(|pixel| [pixel[0], pixel[1], pixel[2], u8::MAX])
|
||||
.collect();
|
||||
Some(data)
|
||||
}
|
||||
_ => {
|
||||
return Err(TextureError::TranscodeError(format!(
|
||||
"unsupported transcode from {transcode_format:?} to {texture_format:?}"
|
||||
)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Some(dds.data)
|
||||
};
|
||||
|
||||
Ok(image)
|
||||
}
|
||||
|
||||
@ -112,6 +145,9 @@ pub fn dds_format_to_texture_format(
|
||||
TextureFormat::Bgra8Unorm
|
||||
}
|
||||
}
|
||||
D3DFormat::R8G8B8 => {
|
||||
return Err(TextureError::FormatRequiresTranscodingError(TranscodeFormat::Rgb8));
|
||||
},
|
||||
D3DFormat::G16R16 => TextureFormat::Rg16Uint,
|
||||
D3DFormat::A2B10G10R10 => TextureFormat::Rgb10a2Unorm,
|
||||
D3DFormat::A8L8 => TextureFormat::Rg8Uint,
|
||||
@ -153,7 +189,6 @@ pub fn dds_format_to_texture_format(
|
||||
// FIXME: Map to argb format and user has to know to ignore the alpha channel?
|
||||
| D3DFormat::X8B8G8R8
|
||||
| D3DFormat::A2R10G10B10
|
||||
| D3DFormat::R8G8B8
|
||||
| D3DFormat::X1R5G5B5
|
||||
| D3DFormat::A4R4G4B4
|
||||
| D3DFormat::X4R4G4B4
|
||||
|
||||
@ -1491,19 +1491,20 @@ pub enum DataFormat {
|
||||
Rg,
|
||||
}
|
||||
|
||||
/// Texture data need to be transcoded from this format for use with `wgpu`.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum TranscodeFormat {
|
||||
Etc1s,
|
||||
Uastc(DataFormat),
|
||||
// Has to be transcoded to R8Unorm for use with `wgpu`
|
||||
// Has to be transcoded to R8Unorm for use with `wgpu`.
|
||||
R8UnormSrgb,
|
||||
// Has to be transcoded to R8G8Unorm for use with `wgpu`
|
||||
// Has to be transcoded to R8G8Unorm for use with `wgpu`.
|
||||
Rg8UnormSrgb,
|
||||
// Has to be transcoded to Rgba8 for use with `wgpu`
|
||||
// Has to be transcoded to Rgba8 for use with `wgpu`.
|
||||
Rgb8,
|
||||
}
|
||||
|
||||
/// An error that occurs when accessing specific pixels in a texture
|
||||
/// An error that occurs when accessing specific pixels in a texture.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum TextureAccessError {
|
||||
#[error("out of bounds (x: {x}, y: {y}, z: {z})")]
|
||||
@ -1514,25 +1515,34 @@ pub enum TextureAccessError {
|
||||
WrongDimension,
|
||||
}
|
||||
|
||||
/// An error that occurs when loading a texture
|
||||
/// An error that occurs when loading a texture.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum TextureError {
|
||||
/// Image MIME type is invalid.
|
||||
#[error("invalid image mime type: {0}")]
|
||||
InvalidImageMimeType(String),
|
||||
/// Image extension is invalid.
|
||||
#[error("invalid image extension: {0}")]
|
||||
InvalidImageExtension(String),
|
||||
/// Failed to load an image.
|
||||
#[error("failed to load an image: {0}")]
|
||||
ImageError(#[from] image::ImageError),
|
||||
/// Texture format isn't supported.
|
||||
#[error("unsupported texture format: {0}")]
|
||||
UnsupportedTextureFormat(String),
|
||||
/// Supercompression isn't supported.
|
||||
#[error("supercompression not supported: {0}")]
|
||||
SuperCompressionNotSupported(String),
|
||||
#[error("failed to load an image: {0}")]
|
||||
/// Failed to decompress an image.
|
||||
#[error("failed to decompress an image: {0}")]
|
||||
SuperDecompressionError(String),
|
||||
/// Invalid data.
|
||||
#[error("invalid data: {0}")]
|
||||
InvalidData(String),
|
||||
/// Transcode error.
|
||||
#[error("transcode error: {0}")]
|
||||
TranscodeError(String),
|
||||
/// Format requires transcoding.
|
||||
#[error("format requires transcoding: {0:?}")]
|
||||
FormatRequiresTranscodingError(TranscodeFormat),
|
||||
/// Only cubemaps with six faces are supported.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user