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)]
|
#[cfg(debug_assertions)]
|
||||||
use {bevy_utils::once, tracing::warn};
|
use {bevy_utils::once, tracing::warn};
|
||||||
|
|
||||||
use super::{CompressedImageFormats, Image, TextureError};
|
use super::{CompressedImageFormats, Image, TextureError, TranscodeFormat};
|
||||||
|
|
||||||
#[cfg(feature = "dds")]
|
#[cfg(feature = "dds")]
|
||||||
pub fn dds_buffer_to_image(
|
pub fn dds_buffer_to_image(
|
||||||
@ -20,7 +20,18 @@ pub fn dds_buffer_to_image(
|
|||||||
let mut cursor = Cursor::new(buffer);
|
let mut cursor = Cursor::new(buffer);
|
||||||
let dds = Dds::read(&mut cursor)
|
let dds = Dds::read(&mut cursor)
|
||||||
.map_err(|error| TextureError::InvalidData(format!("Failed to parse DDS file: {error}")))?;
|
.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) {
|
if !supported_compressed_formats.supports(texture_format) {
|
||||||
return Err(TextureError::UnsupportedTextureFormat(format!(
|
return Err(TextureError::UnsupportedTextureFormat(format!(
|
||||||
"Format not supported by this GPU: {texture_format:?}",
|
"Format not supported by this GPU: {texture_format:?}",
|
||||||
@ -86,7 +97,29 @@ pub fn dds_buffer_to_image(
|
|||||||
..Default::default()
|
..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)
|
Ok(image)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,6 +145,9 @@ pub fn dds_format_to_texture_format(
|
|||||||
TextureFormat::Bgra8Unorm
|
TextureFormat::Bgra8Unorm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
D3DFormat::R8G8B8 => {
|
||||||
|
return Err(TextureError::FormatRequiresTranscodingError(TranscodeFormat::Rgb8));
|
||||||
|
},
|
||||||
D3DFormat::G16R16 => TextureFormat::Rg16Uint,
|
D3DFormat::G16R16 => TextureFormat::Rg16Uint,
|
||||||
D3DFormat::A2B10G10R10 => TextureFormat::Rgb10a2Unorm,
|
D3DFormat::A2B10G10R10 => TextureFormat::Rgb10a2Unorm,
|
||||||
D3DFormat::A8L8 => TextureFormat::Rg8Uint,
|
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?
|
// FIXME: Map to argb format and user has to know to ignore the alpha channel?
|
||||||
| D3DFormat::X8B8G8R8
|
| D3DFormat::X8B8G8R8
|
||||||
| D3DFormat::A2R10G10B10
|
| D3DFormat::A2R10G10B10
|
||||||
| D3DFormat::R8G8B8
|
|
||||||
| D3DFormat::X1R5G5B5
|
| D3DFormat::X1R5G5B5
|
||||||
| D3DFormat::A4R4G4B4
|
| D3DFormat::A4R4G4B4
|
||||||
| D3DFormat::X4R4G4B4
|
| D3DFormat::X4R4G4B4
|
||||||
|
|||||||
@ -1491,19 +1491,20 @@ pub enum DataFormat {
|
|||||||
Rg,
|
Rg,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Texture data need to be transcoded from this format for use with `wgpu`.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum TranscodeFormat {
|
pub enum TranscodeFormat {
|
||||||
Etc1s,
|
Etc1s,
|
||||||
Uastc(DataFormat),
|
Uastc(DataFormat),
|
||||||
// Has to be transcoded to R8Unorm for use with `wgpu`
|
// Has to be transcoded to R8Unorm for use with `wgpu`.
|
||||||
R8UnormSrgb,
|
R8UnormSrgb,
|
||||||
// Has to be transcoded to R8G8Unorm for use with `wgpu`
|
// Has to be transcoded to R8G8Unorm for use with `wgpu`.
|
||||||
Rg8UnormSrgb,
|
Rg8UnormSrgb,
|
||||||
// Has to be transcoded to Rgba8 for use with `wgpu`
|
// Has to be transcoded to Rgba8 for use with `wgpu`.
|
||||||
Rgb8,
|
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)]
|
#[derive(Error, Debug)]
|
||||||
pub enum TextureAccessError {
|
pub enum TextureAccessError {
|
||||||
#[error("out of bounds (x: {x}, y: {y}, z: {z})")]
|
#[error("out of bounds (x: {x}, y: {y}, z: {z})")]
|
||||||
@ -1514,25 +1515,34 @@ pub enum TextureAccessError {
|
|||||||
WrongDimension,
|
WrongDimension,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error that occurs when loading a texture
|
/// An error that occurs when loading a texture.
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum TextureError {
|
pub enum TextureError {
|
||||||
|
/// Image MIME type is invalid.
|
||||||
#[error("invalid image mime type: {0}")]
|
#[error("invalid image mime type: {0}")]
|
||||||
InvalidImageMimeType(String),
|
InvalidImageMimeType(String),
|
||||||
|
/// Image extension is invalid.
|
||||||
#[error("invalid image extension: {0}")]
|
#[error("invalid image extension: {0}")]
|
||||||
InvalidImageExtension(String),
|
InvalidImageExtension(String),
|
||||||
|
/// Failed to load an image.
|
||||||
#[error("failed to load an image: {0}")]
|
#[error("failed to load an image: {0}")]
|
||||||
ImageError(#[from] image::ImageError),
|
ImageError(#[from] image::ImageError),
|
||||||
|
/// Texture format isn't supported.
|
||||||
#[error("unsupported texture format: {0}")]
|
#[error("unsupported texture format: {0}")]
|
||||||
UnsupportedTextureFormat(String),
|
UnsupportedTextureFormat(String),
|
||||||
|
/// Supercompression isn't supported.
|
||||||
#[error("supercompression not supported: {0}")]
|
#[error("supercompression not supported: {0}")]
|
||||||
SuperCompressionNotSupported(String),
|
SuperCompressionNotSupported(String),
|
||||||
#[error("failed to load an image: {0}")]
|
/// Failed to decompress an image.
|
||||||
|
#[error("failed to decompress an image: {0}")]
|
||||||
SuperDecompressionError(String),
|
SuperDecompressionError(String),
|
||||||
|
/// Invalid data.
|
||||||
#[error("invalid data: {0}")]
|
#[error("invalid data: {0}")]
|
||||||
InvalidData(String),
|
InvalidData(String),
|
||||||
|
/// Transcode error.
|
||||||
#[error("transcode error: {0}")]
|
#[error("transcode error: {0}")]
|
||||||
TranscodeError(String),
|
TranscodeError(String),
|
||||||
|
/// Format requires transcoding.
|
||||||
#[error("format requires transcoding: {0:?}")]
|
#[error("format requires transcoding: {0:?}")]
|
||||||
FormatRequiresTranscodingError(TranscodeFormat),
|
FormatRequiresTranscodingError(TranscodeFormat),
|
||||||
/// Only cubemaps with six faces are supported.
|
/// Only cubemaps with six faces are supported.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user