mirror of
https://github.com/bevyengine/bevy
synced 2024-12-20 10:03:07 +00:00
d4883a9b5f
# Objective - dont depend on wgpu if we dont have to ## Solution - works towards this, but doesnt fully accomplish it. the remaining types stopping us from doing this need to be moved upstream, i will PR this ## Testing - 3d_scene runs --------- Co-authored-by: François Mockers <francois.mockers@vleue.com>
379 lines
16 KiB
Rust
379 lines
16 KiB
Rust
//! [DirectDraw Surface](https://en.wikipedia.org/wiki/DirectDraw_Surface) functionality.
|
|
|
|
#[cfg(debug_assertions)]
|
|
use bevy_utils::warn_once;
|
|
use ddsfile::{Caps2, D3DFormat, Dds, DxgiFormat};
|
|
use std::io::Cursor;
|
|
use wgpu::TextureViewDescriptor;
|
|
use wgpu_types::{Extent3d, TextureDimension, TextureFormat, TextureViewDimension};
|
|
|
|
use super::{CompressedImageFormats, Image, TextureError};
|
|
|
|
#[cfg(feature = "dds")]
|
|
pub fn dds_buffer_to_image(
|
|
#[cfg(debug_assertions)] name: String,
|
|
buffer: &[u8],
|
|
supported_compressed_formats: CompressedImageFormats,
|
|
is_srgb: bool,
|
|
) -> Result<Image, TextureError> {
|
|
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)?;
|
|
if !supported_compressed_formats.supports(texture_format) {
|
|
return Err(TextureError::UnsupportedTextureFormat(format!(
|
|
"Format not supported by this GPU: {texture_format:?}",
|
|
)));
|
|
}
|
|
let mut image = Image::default();
|
|
let is_cubemap = dds.header.caps2.contains(Caps2::CUBEMAP);
|
|
let depth_or_array_layers = if dds.get_num_array_layers() > 1 {
|
|
dds.get_num_array_layers()
|
|
} else {
|
|
dds.get_depth()
|
|
};
|
|
if is_cubemap
|
|
&& !dds.header.caps2.contains(
|
|
Caps2::CUBEMAP_NEGATIVEX
|
|
| Caps2::CUBEMAP_NEGATIVEY
|
|
| Caps2::CUBEMAP_NEGATIVEZ
|
|
| Caps2::CUBEMAP_POSITIVEX
|
|
| Caps2::CUBEMAP_POSITIVEY
|
|
| Caps2::CUBEMAP_POSITIVEZ,
|
|
)
|
|
{
|
|
return Err(TextureError::IncompleteCubemap);
|
|
}
|
|
image.texture_descriptor.size = Extent3d {
|
|
width: dds.get_width(),
|
|
height: dds.get_height(),
|
|
depth_or_array_layers,
|
|
}
|
|
.physical_size(texture_format);
|
|
let mip_map_level = match dds.get_num_mipmap_levels() {
|
|
0 => {
|
|
#[cfg(debug_assertions)]
|
|
warn_once!(
|
|
"Mipmap levels for texture {} are 0, bumping them to 1",
|
|
name
|
|
);
|
|
1
|
|
}
|
|
t => t,
|
|
};
|
|
image.texture_descriptor.mip_level_count = mip_map_level;
|
|
image.texture_descriptor.format = texture_format;
|
|
image.texture_descriptor.dimension = if dds.get_depth() > 1 {
|
|
TextureDimension::D3
|
|
} else if image.is_compressed() || dds.get_height() > 1 {
|
|
TextureDimension::D2
|
|
} else {
|
|
TextureDimension::D1
|
|
};
|
|
if is_cubemap {
|
|
let dimension = if image.texture_descriptor.size.depth_or_array_layers > 6 {
|
|
TextureViewDimension::CubeArray
|
|
} else {
|
|
TextureViewDimension::Cube
|
|
};
|
|
image.texture_view_descriptor = Some(TextureViewDescriptor {
|
|
dimension: Some(dimension),
|
|
..Default::default()
|
|
});
|
|
}
|
|
image.data = dds.data;
|
|
Ok(image)
|
|
}
|
|
|
|
#[cfg(feature = "dds")]
|
|
pub fn dds_format_to_texture_format(
|
|
dds: &Dds,
|
|
is_srgb: bool,
|
|
) -> Result<TextureFormat, TextureError> {
|
|
Ok(if let Some(d3d_format) = dds.get_d3d_format() {
|
|
match d3d_format {
|
|
D3DFormat::A8B8G8R8 => {
|
|
if is_srgb {
|
|
TextureFormat::Rgba8UnormSrgb
|
|
} else {
|
|
TextureFormat::Rgba8Unorm
|
|
}
|
|
}
|
|
D3DFormat::A8 => TextureFormat::R8Unorm,
|
|
D3DFormat::A8R8G8B8 => {
|
|
if is_srgb {
|
|
TextureFormat::Bgra8UnormSrgb
|
|
} else {
|
|
TextureFormat::Bgra8Unorm
|
|
}
|
|
}
|
|
D3DFormat::G16R16 => TextureFormat::Rg16Uint,
|
|
D3DFormat::A2B10G10R10 => TextureFormat::Rgb10a2Unorm,
|
|
D3DFormat::A8L8 => TextureFormat::Rg8Uint,
|
|
D3DFormat::L16 => TextureFormat::R16Uint,
|
|
D3DFormat::L8 => TextureFormat::R8Uint,
|
|
D3DFormat::DXT1 => {
|
|
if is_srgb {
|
|
TextureFormat::Bc1RgbaUnormSrgb
|
|
} else {
|
|
TextureFormat::Bc1RgbaUnorm
|
|
}
|
|
}
|
|
D3DFormat::DXT3 | D3DFormat::DXT2 => {
|
|
if is_srgb {
|
|
TextureFormat::Bc2RgbaUnormSrgb
|
|
} else {
|
|
TextureFormat::Bc2RgbaUnorm
|
|
}
|
|
}
|
|
D3DFormat::DXT5 | D3DFormat::DXT4 => {
|
|
if is_srgb {
|
|
TextureFormat::Bc3RgbaUnormSrgb
|
|
} else {
|
|
TextureFormat::Bc3RgbaUnorm
|
|
}
|
|
}
|
|
D3DFormat::A16B16G16R16 => TextureFormat::Rgba16Unorm,
|
|
D3DFormat::Q16W16V16U16 => TextureFormat::Rgba16Sint,
|
|
D3DFormat::R16F => TextureFormat::R16Float,
|
|
D3DFormat::G16R16F => TextureFormat::Rg16Float,
|
|
D3DFormat::A16B16G16R16F => TextureFormat::Rgba16Float,
|
|
D3DFormat::R32F => TextureFormat::R32Float,
|
|
D3DFormat::G32R32F => TextureFormat::Rg32Float,
|
|
D3DFormat::A32B32G32R32F => TextureFormat::Rgba32Float,
|
|
D3DFormat::A1R5G5B5
|
|
| D3DFormat::R5G6B5
|
|
// FIXME: Map to argb format and user has to know to ignore the alpha channel?
|
|
| D3DFormat::X8R8G8B8
|
|
// 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
|
|
| D3DFormat::A8R3G3B2
|
|
| D3DFormat::A4L4
|
|
| D3DFormat::R8G8_B8G8
|
|
| D3DFormat::G8R8_G8B8
|
|
| D3DFormat::UYVY
|
|
| D3DFormat::YUY2
|
|
| D3DFormat::CXV8U8 => {
|
|
return Err(TextureError::UnsupportedTextureFormat(format!(
|
|
"{d3d_format:?}",
|
|
)))
|
|
}
|
|
}
|
|
} else if let Some(dxgi_format) = dds.get_dxgi_format() {
|
|
match dxgi_format {
|
|
DxgiFormat::R32G32B32A32_Typeless | DxgiFormat::R32G32B32A32_Float => {
|
|
TextureFormat::Rgba32Float
|
|
}
|
|
DxgiFormat::R32G32B32A32_UInt => TextureFormat::Rgba32Uint,
|
|
DxgiFormat::R32G32B32A32_SInt => TextureFormat::Rgba32Sint,
|
|
DxgiFormat::R16G16B16A16_Typeless | DxgiFormat::R16G16B16A16_Float => {
|
|
TextureFormat::Rgba16Float
|
|
}
|
|
DxgiFormat::R16G16B16A16_UNorm => TextureFormat::Rgba16Unorm,
|
|
DxgiFormat::R16G16B16A16_UInt => TextureFormat::Rgba16Uint,
|
|
DxgiFormat::R16G16B16A16_SNorm => TextureFormat::Rgba16Snorm,
|
|
DxgiFormat::R16G16B16A16_SInt => TextureFormat::Rgba16Sint,
|
|
DxgiFormat::R32G32_Typeless | DxgiFormat::R32G32_Float => TextureFormat::Rg32Float,
|
|
DxgiFormat::R32G32_UInt => TextureFormat::Rg32Uint,
|
|
DxgiFormat::R32G32_SInt => TextureFormat::Rg32Sint,
|
|
DxgiFormat::R10G10B10A2_Typeless | DxgiFormat::R10G10B10A2_UNorm => {
|
|
TextureFormat::Rgb10a2Unorm
|
|
}
|
|
DxgiFormat::R11G11B10_Float => TextureFormat::Rg11b10Ufloat,
|
|
DxgiFormat::R8G8B8A8_Typeless
|
|
| DxgiFormat::R8G8B8A8_UNorm
|
|
| DxgiFormat::R8G8B8A8_UNorm_sRGB => {
|
|
if is_srgb {
|
|
TextureFormat::Rgba8UnormSrgb
|
|
} else {
|
|
TextureFormat::Rgba8Unorm
|
|
}
|
|
}
|
|
DxgiFormat::R8G8B8A8_UInt => TextureFormat::Rgba8Uint,
|
|
DxgiFormat::R8G8B8A8_SNorm => TextureFormat::Rgba8Snorm,
|
|
DxgiFormat::R8G8B8A8_SInt => TextureFormat::Rgba8Sint,
|
|
DxgiFormat::R16G16_Typeless | DxgiFormat::R16G16_Float => TextureFormat::Rg16Float,
|
|
DxgiFormat::R16G16_UNorm => TextureFormat::Rg16Unorm,
|
|
DxgiFormat::R16G16_UInt => TextureFormat::Rg16Uint,
|
|
DxgiFormat::R16G16_SNorm => TextureFormat::Rg16Snorm,
|
|
DxgiFormat::R16G16_SInt => TextureFormat::Rg16Sint,
|
|
DxgiFormat::R32_Typeless | DxgiFormat::R32_Float => TextureFormat::R32Float,
|
|
DxgiFormat::D32_Float => TextureFormat::Depth32Float,
|
|
DxgiFormat::R32_UInt => TextureFormat::R32Uint,
|
|
DxgiFormat::R32_SInt => TextureFormat::R32Sint,
|
|
DxgiFormat::R24G8_Typeless | DxgiFormat::D24_UNorm_S8_UInt => {
|
|
TextureFormat::Depth24PlusStencil8
|
|
}
|
|
DxgiFormat::R24_UNorm_X8_Typeless => TextureFormat::Depth24Plus,
|
|
DxgiFormat::R8G8_Typeless | DxgiFormat::R8G8_UNorm => TextureFormat::Rg8Unorm,
|
|
DxgiFormat::R8G8_UInt => TextureFormat::Rg8Uint,
|
|
DxgiFormat::R8G8_SNorm => TextureFormat::Rg8Snorm,
|
|
DxgiFormat::R8G8_SInt => TextureFormat::Rg8Sint,
|
|
DxgiFormat::R16_Typeless | DxgiFormat::R16_Float => TextureFormat::R16Float,
|
|
DxgiFormat::R16_UNorm => TextureFormat::R16Unorm,
|
|
DxgiFormat::R16_UInt => TextureFormat::R16Uint,
|
|
DxgiFormat::R16_SNorm => TextureFormat::R16Snorm,
|
|
DxgiFormat::R16_SInt => TextureFormat::R16Sint,
|
|
DxgiFormat::R8_Typeless | DxgiFormat::R8_UNorm => TextureFormat::R8Unorm,
|
|
DxgiFormat::R8_UInt => TextureFormat::R8Uint,
|
|
DxgiFormat::R8_SNorm => TextureFormat::R8Snorm,
|
|
DxgiFormat::R8_SInt => TextureFormat::R8Sint,
|
|
DxgiFormat::R9G9B9E5_SharedExp => TextureFormat::Rgb9e5Ufloat,
|
|
DxgiFormat::BC1_Typeless | DxgiFormat::BC1_UNorm | DxgiFormat::BC1_UNorm_sRGB => {
|
|
if is_srgb {
|
|
TextureFormat::Bc1RgbaUnormSrgb
|
|
} else {
|
|
TextureFormat::Bc1RgbaUnorm
|
|
}
|
|
}
|
|
DxgiFormat::BC2_Typeless | DxgiFormat::BC2_UNorm | DxgiFormat::BC2_UNorm_sRGB => {
|
|
if is_srgb {
|
|
TextureFormat::Bc2RgbaUnormSrgb
|
|
} else {
|
|
TextureFormat::Bc2RgbaUnorm
|
|
}
|
|
}
|
|
DxgiFormat::BC3_Typeless | DxgiFormat::BC3_UNorm | DxgiFormat::BC3_UNorm_sRGB => {
|
|
if is_srgb {
|
|
TextureFormat::Bc3RgbaUnormSrgb
|
|
} else {
|
|
TextureFormat::Bc3RgbaUnorm
|
|
}
|
|
}
|
|
DxgiFormat::BC4_Typeless | DxgiFormat::BC4_UNorm => TextureFormat::Bc4RUnorm,
|
|
DxgiFormat::BC4_SNorm => TextureFormat::Bc4RSnorm,
|
|
DxgiFormat::BC5_Typeless | DxgiFormat::BC5_UNorm => TextureFormat::Bc5RgUnorm,
|
|
DxgiFormat::BC5_SNorm => TextureFormat::Bc5RgSnorm,
|
|
DxgiFormat::B8G8R8A8_UNorm
|
|
| DxgiFormat::B8G8R8A8_Typeless
|
|
| DxgiFormat::B8G8R8A8_UNorm_sRGB => {
|
|
if is_srgb {
|
|
TextureFormat::Bgra8UnormSrgb
|
|
} else {
|
|
TextureFormat::Bgra8Unorm
|
|
}
|
|
}
|
|
|
|
DxgiFormat::BC6H_Typeless | DxgiFormat::BC6H_UF16 => TextureFormat::Bc6hRgbUfloat,
|
|
DxgiFormat::BC6H_SF16 => TextureFormat::Bc6hRgbFloat,
|
|
DxgiFormat::BC7_Typeless | DxgiFormat::BC7_UNorm | DxgiFormat::BC7_UNorm_sRGB => {
|
|
if is_srgb {
|
|
TextureFormat::Bc7RgbaUnormSrgb
|
|
} else {
|
|
TextureFormat::Bc7RgbaUnorm
|
|
}
|
|
}
|
|
_ => {
|
|
return Err(TextureError::UnsupportedTextureFormat(format!(
|
|
"{dxgi_format:?}",
|
|
)))
|
|
}
|
|
}
|
|
} else {
|
|
return Err(TextureError::UnsupportedTextureFormat(
|
|
"unspecified".to_string(),
|
|
));
|
|
})
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use wgpu::util::TextureDataOrder;
|
|
use wgpu_types::{TextureDescriptor, TextureDimension, TextureFormat};
|
|
|
|
use crate::CompressedImageFormats;
|
|
|
|
use super::dds_buffer_to_image;
|
|
|
|
/// `wgpu::create_texture_with_data` that reads from data structure but doesn't actually talk to your GPU
|
|
fn fake_wgpu_create_texture_with_data(
|
|
desc: &TextureDescriptor<Option<&'_ str>, &'_ [TextureFormat]>,
|
|
data: &[u8],
|
|
) {
|
|
// Will return None only if it's a combined depth-stencil format
|
|
// If so, default to 4, validation will fail later anyway since the depth or stencil
|
|
// aspect needs to be written to individually
|
|
let block_size = desc.format.block_copy_size(None).unwrap_or(4);
|
|
let (block_width, block_height) = desc.format.block_dimensions();
|
|
let layer_iterations = desc.array_layer_count();
|
|
|
|
let outer_iteration;
|
|
let inner_iteration;
|
|
match TextureDataOrder::default() {
|
|
TextureDataOrder::LayerMajor => {
|
|
outer_iteration = layer_iterations;
|
|
inner_iteration = desc.mip_level_count;
|
|
}
|
|
TextureDataOrder::MipMajor => {
|
|
outer_iteration = desc.mip_level_count;
|
|
inner_iteration = layer_iterations;
|
|
}
|
|
}
|
|
|
|
let mut binary_offset = 0;
|
|
for outer in 0..outer_iteration {
|
|
for inner in 0..inner_iteration {
|
|
let (_layer, mip) = match TextureDataOrder::default() {
|
|
TextureDataOrder::LayerMajor => (outer, inner),
|
|
TextureDataOrder::MipMajor => (inner, outer),
|
|
};
|
|
|
|
let mut mip_size = desc.mip_level_size(mip).unwrap();
|
|
// copying layers separately
|
|
if desc.dimension != TextureDimension::D3 {
|
|
mip_size.depth_or_array_layers = 1;
|
|
}
|
|
|
|
// When uploading mips of compressed textures and the mip is supposed to be
|
|
// a size that isn't a multiple of the block size, the mip needs to be uploaded
|
|
// as its "physical size" which is the size rounded up to the nearest block size.
|
|
let mip_physical = mip_size.physical_size(desc.format);
|
|
|
|
// All these calculations are performed on the physical size as that's the
|
|
// data that exists in the buffer.
|
|
let width_blocks = mip_physical.width / block_width;
|
|
let height_blocks = mip_physical.height / block_height;
|
|
|
|
let bytes_per_row = width_blocks * block_size;
|
|
let data_size = bytes_per_row * height_blocks * mip_size.depth_or_array_layers;
|
|
|
|
let end_offset = binary_offset + data_size as usize;
|
|
|
|
assert!(binary_offset < data.len());
|
|
assert!(end_offset <= data.len());
|
|
// those asserts match how the data will be accessed by wgpu:
|
|
// data[binary_offset..end_offset])
|
|
|
|
binary_offset = end_offset;
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn dds_skybox() {
|
|
let buffer: [u8; 224] = [
|
|
0x44, 0x44, 0x53, 0x20, 0x7c, 0, 0, 0, 7, 0x10, 0x08, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0x10,
|
|
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0x47, 0x49, 0x4d, 0x50, 0x2d, 0x44, 0x44, 0x53, 0x5c,
|
|
0x09, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0, 4, 0, 0, 0, 0x44, 0x58, 0x54, 0x35, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x10, 0, 0, 0, 0xfe, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x49, 0x92, 0x24, 0x49, 0x92, 0x24, 0xda, 0xd6,
|
|
0x2f, 0x5b, 0x8a, 0, 0xff, 0x55, 0xff, 0xff, 0x49, 0x92, 0x24, 0x49, 0x92, 0x24, 0xd5,
|
|
0x84, 0x8e, 0x3a, 0xb7, 0, 0xaa, 0x55, 0xff, 0xff, 0x49, 0x92, 0x24, 0x49, 0x92, 0x24,
|
|
0xf5, 0x94, 0x6f, 0x32, 0x57, 0xb7, 0x8b, 0, 0xff, 0xff, 0x49, 0x92, 0x24, 0x49, 0x92,
|
|
0x24, 0x2c, 0x3a, 0x49, 0x19, 0x28, 0xf7, 0xd7, 0xbe, 0xff, 0xff, 0x49, 0x92, 0x24,
|
|
0x49, 0x92, 0x24, 0x16, 0x95, 0xae, 0x42, 0xfc, 0, 0xaa, 0x55, 0xff, 0xff, 0x49, 0x92,
|
|
0x24, 0x49, 0x92, 0x24, 0xd8, 0xad, 0xae, 0x42, 0xaf, 0x0a, 0xaa, 0x55,
|
|
];
|
|
let r = dds_buffer_to_image("".into(), &buffer, CompressedImageFormats::BC, true);
|
|
assert!(r.is_ok());
|
|
if let Ok(r) = r {
|
|
fake_wgpu_create_texture_with_data(&r.texture_descriptor, &r.data);
|
|
}
|
|
}
|
|
}
|