use crate::error::Error;
use crate::format::{D3DFormat, DxgiFormat};
use crate::surface::SubresourceId;
use crate::Dds;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GpuFormat {
pub dxgi_name: &'static str,
pub wgpu_name: &'static str,
pub vulkan_name: &'static str,
pub block_bytes: u32,
pub block_width: u32,
pub block_height: u32,
pub compressed: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UploadPath {
Compressed,
DecodedRgba8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UploadPlan {
pub id: SubresourceId,
pub path: UploadPath,
pub format: GpuFormat,
pub width: u32,
pub height: u32,
pub depth: u32,
pub data_offset: usize,
pub data_len: usize,
pub bytes_per_row: u32,
pub rows_per_image: u32,
}
impl Dds {
pub fn gpu_format(&self) -> Result<GpuFormat, Error> {
if let Some(dxgi) = self.get_dxgi_format() {
return gpu_format_from_dxgi(dxgi);
}
if let Some(d3d) = self.get_d3d_format() {
return gpu_format_from_d3d(d3d);
}
Err(Error::UnsupportedFormat)
}
pub fn upload_plan_compressed(&self, id: SubresourceId) -> Result<UploadPlan, Error> {
let format = self.gpu_format()?;
let surf = self.surface(id)?;
let range = self.subresource_range(id)?;
let (bytes_per_row, rows_per_image) =
compressed_pitches(format, surf.width, surf.height)?;
Ok(UploadPlan {
id,
path: UploadPath::Compressed,
format,
width: surf.width,
height: surf.height,
depth: surf.depth,
data_offset: range.start,
data_len: range.end - range.start,
bytes_per_row,
rows_per_image,
})
}
pub fn upload_plan_decoded_rgba8(&self, id: SubresourceId) -> Result<UploadPlan, Error> {
let _ = self.decode_content()?;
let surf = self.surface(id)?;
let pixels = (surf.width as usize)
.checked_mul(surf.height as usize)
.and_then(|n| n.checked_mul(surf.depth as usize))
.and_then(|n| n.checked_mul(4))
.ok_or(Error::OutOfBounds)?;
Ok(UploadPlan {
id,
path: UploadPath::DecodedRgba8,
format: GPU_RGBA8_UNORM,
width: surf.width,
height: surf.height,
depth: surf.depth,
data_offset: 0,
data_len: pixels,
bytes_per_row: surf
.width
.checked_mul(4)
.ok_or(Error::OutOfBounds)?,
rows_per_image: surf.height,
})
}
}
fn compressed_pitches(format: GpuFormat, width: u32, height: u32) -> Result<(u32, u32), Error> {
if width == 0 || height == 0 {
return Err(Error::InvalidField("zero image dimension".into()));
}
let blocks_x = width.div_ceil(format.block_width);
let blocks_y = height.div_ceil(format.block_height);
let bytes_per_row = blocks_x
.checked_mul(format.block_bytes)
.ok_or(Error::OutOfBounds)?;
Ok((bytes_per_row, blocks_y))
}
const GPU_RGBA8_UNORM: GpuFormat = GpuFormat {
dxgi_name: "R8G8B8A8_UNorm",
wgpu_name: "Rgba8Unorm",
vulkan_name: "VK_FORMAT_R8G8B8A8_UNORM",
block_bytes: 4,
block_width: 1,
block_height: 1,
compressed: false,
};
fn bc(dxgi: &'static str, wgpu: &'static str, vk: &'static str, block_bytes: u32) -> GpuFormat {
GpuFormat {
dxgi_name: dxgi,
wgpu_name: wgpu,
vulkan_name: vk,
block_bytes,
block_width: 4,
block_height: 4,
compressed: true,
}
}
fn gpu_format_from_dxgi(format: DxgiFormat) -> Result<GpuFormat, Error> {
use DxgiFormat::*;
Ok(match format {
BC1_Typeless | BC1_UNorm => bc(
"BC1_UNorm",
"Bc1RgbaUnorm",
"VK_FORMAT_BC1_RGBA_UNORM_BLOCK",
8,
),
BC1_UNorm_sRGB => bc(
"BC1_UNorm_sRGB",
"Bc1RgbaUnormSrgb",
"VK_FORMAT_BC1_RGBA_SRGB_BLOCK",
8,
),
BC2_Typeless | BC2_UNorm => bc(
"BC2_UNorm",
"Bc2RgbaUnorm",
"VK_FORMAT_BC2_UNORM_BLOCK",
16,
),
BC2_UNorm_sRGB => bc(
"BC2_UNorm_sRGB",
"Bc2RgbaUnormSrgb",
"VK_FORMAT_BC2_SRGB_BLOCK",
16,
),
BC3_Typeless | BC3_UNorm => bc(
"BC3_UNorm",
"Bc3RgbaUnorm",
"VK_FORMAT_BC3_UNORM_BLOCK",
16,
),
BC3_UNorm_sRGB => bc(
"BC3_UNorm_sRGB",
"Bc3RgbaUnormSrgb",
"VK_FORMAT_BC3_SRGB_BLOCK",
16,
),
BC4_Typeless | BC4_UNorm => {
bc("BC4_UNorm", "Bc4RUnorm", "VK_FORMAT_BC4_UNORM_BLOCK", 8)
}
BC4_SNorm => bc("BC4_SNorm", "Bc4RSnorm", "VK_FORMAT_BC4_SNORM_BLOCK", 8),
BC5_Typeless | BC5_UNorm => {
bc("BC5_UNorm", "Bc5RgUnorm", "VK_FORMAT_BC5_UNORM_BLOCK", 16)
}
BC5_SNorm => bc("BC5_SNorm", "Bc5RgSnorm", "VK_FORMAT_BC5_SNORM_BLOCK", 16),
BC7_Typeless | BC7_UNorm => bc(
"BC7_UNorm",
"Bc7RgbaUnorm",
"VK_FORMAT_BC7_UNORM_BLOCK",
16,
),
BC7_UNorm_sRGB => bc(
"BC7_UNorm_sRGB",
"Bc7RgbaUnormSrgb",
"VK_FORMAT_BC7_SRGB_BLOCK",
16,
),
R8G8B8A8_Typeless | R8G8B8A8_UNorm | R8G8B8A8_UInt => GPU_RGBA8_UNORM,
R8G8B8A8_UNorm_sRGB => GpuFormat {
dxgi_name: "R8G8B8A8_UNorm_sRGB",
wgpu_name: "Rgba8UnormSrgb",
vulkan_name: "VK_FORMAT_R8G8B8A8_SRGB",
block_bytes: 4,
block_width: 1,
block_height: 1,
compressed: false,
},
B8G8R8A8_Typeless | B8G8R8A8_UNorm => GpuFormat {
dxgi_name: "B8G8R8A8_UNorm",
wgpu_name: "Bgra8Unorm",
vulkan_name: "VK_FORMAT_B8G8R8A8_UNORM",
block_bytes: 4,
block_width: 1,
block_height: 1,
compressed: false,
},
B8G8R8A8_UNorm_sRGB => GpuFormat {
dxgi_name: "B8G8R8A8_UNorm_sRGB",
wgpu_name: "Bgra8UnormSrgb",
vulkan_name: "VK_FORMAT_B8G8R8A8_SRGB",
block_bytes: 4,
block_width: 1,
block_height: 1,
compressed: false,
},
_ => return Err(Error::UnsupportedFormat),
})
}
fn gpu_format_from_d3d(format: D3DFormat) -> Result<GpuFormat, Error> {
use D3DFormat::*;
Ok(match format {
DXT1 => bc(
"BC1_UNorm",
"Bc1RgbaUnorm",
"VK_FORMAT_BC1_RGBA_UNORM_BLOCK",
8,
),
DXT2 | DXT3 => bc(
"BC2_UNorm",
"Bc2RgbaUnorm",
"VK_FORMAT_BC2_UNORM_BLOCK",
16,
),
DXT4 | DXT5 => bc(
"BC3_UNorm",
"Bc3RgbaUnorm",
"VK_FORMAT_BC3_UNORM_BLOCK",
16,
),
_ => return Err(Error::UnsupportedFormat),
})
}