use crate::{
buffer::Offset as RawOffset,
device,
format,
pso::{Comparison, Rect},
};
use std::{f32, hash, ops::Range};
pub type Size = u32;
pub type NumSamples = u8;
pub type Layer = u16;
pub type Level = u8;
pub const MAX_LEVEL: Level = 15;
pub type TexelCoordinate = i32;
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Extent {
pub width: Size,
pub height: Size,
pub depth: Size,
}
impl Extent {
pub fn is_empty(&self) -> bool {
self.width == 0 || self.height == 0 || self.depth == 0
}
pub fn at_level(&self, level: Level) -> Self {
Extent {
width: 1.max(self.width >> level),
height: 1.max(self.height >> level),
depth: 1.max(self.depth >> level),
}
}
pub fn rect(&self) -> Rect {
Rect {
x: 0,
y: 0,
w: self.width as i16,
h: self.height as i16,
}
}
}
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Offset {
pub x: TexelCoordinate,
pub y: TexelCoordinate,
pub z: TexelCoordinate,
}
impl Offset {
pub const ZERO: Self = Offset { x: 0, y: 0, z: 0 };
pub fn into_bounds(self, extent: &Extent) -> Range<Offset> {
let end = Offset {
x: self.x + extent.width as i32,
y: self.y + extent.height as i32,
z: self.z + extent.depth as i32,
};
self .. end
}
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Tiling {
Optimal = 0,
Linear = 1,
}
#[derive(Clone, Debug, PartialEq)]
pub enum CreationError {
OutOfMemory(device::OutOfMemory),
Format(format::Format),
Kind,
Samples(NumSamples),
Size(Size),
Data(usize),
Usage(Usage),
}
impl From<device::OutOfMemory> for CreationError {
fn from(error: device::OutOfMemory) -> Self {
CreationError::OutOfMemory(error)
}
}
impl std::fmt::Display for CreationError {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CreationError::OutOfMemory(err) => write!(fmt, "Failed to create image: {}", err),
CreationError::Format(format) => write!(fmt, "Failed to create image: Unsupported format: {:?}", format),
CreationError::Kind => write!(fmt, "Failed to create image: Specified kind doesn't support particular operation"), CreationError::Samples(samples) => write!(fmt, "Failed to create image: Specified format doesn't support specified sampling {}", samples),
CreationError::Size(size) => write!(fmt, "Failed to create image: Unsupported size in one of the dimensions {}", size),
CreationError::Data(data) => write!(fmt, "Failed to create image: The given data has a different size {{{}}} than the target image slice", data), CreationError::Usage(usage) => write!(fmt, "Failed to create image: Unsupported usage: {:?}", usage),
}
}
}
impl std::error::Error for CreationError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
CreationError::OutOfMemory(err) => Some(err),
_ => None,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ViewCreationError {
Usage(Usage),
Level(Level),
Layer(LayerError),
BadFormat(format::Format),
BadKind(ViewKind),
OutOfMemory(device::OutOfMemory),
Unsupported,
}
impl From<device::OutOfMemory> for ViewCreationError {
fn from(error: device::OutOfMemory) -> Self {
ViewCreationError::OutOfMemory(error)
}
}
impl std::fmt::Display for ViewCreationError {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ViewCreationError::Usage(usage) => write!(fmt, "Failed to create image view: Specified usage flags are not present in the image {:?}", usage),
ViewCreationError::Level(level) => write!(fmt, "Failed to create image view: Selected level doesn't exist in the image {}", level),
ViewCreationError::Layer(err) => write!(fmt, "Failed to create image view: {}", err),
ViewCreationError::BadFormat(format) => write!(fmt, "Failed to create image view: Incompatible format {:?}", format),
ViewCreationError::BadKind(kind) => write!(fmt, "Failed to create image view: Incompatible kind {:?}", kind),
ViewCreationError::OutOfMemory(err) => write!(fmt, "Failed to create image view: {}", err),
ViewCreationError::Unsupported => write!(fmt, "Failed to create image view: Implementation specific error occurred"),
}
}
}
impl std::error::Error for ViewCreationError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
ViewCreationError::OutOfMemory(err) => Some(err),
_ => None,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum LayerError {
NotExpected(Kind),
OutOfBounds(Range<Layer>),
}
impl std::fmt::Display for LayerError {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LayerError::NotExpected(kind) => {
write!(fmt, "Kind {{{:?}}} does not support arrays", kind)
}
LayerError::OutOfBounds(layers) => write!(
fmt,
"Out of bounds layers {} .. {}",
layers.start, layers.end
),
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Filter {
Nearest,
Linear,
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)]
pub enum CubeFace {
PosX,
NegX,
PosY,
NegY,
PosZ,
NegZ,
}
pub const CUBE_FACES: [CubeFace; 6] = [
CubeFace::PosX,
CubeFace::NegX,
CubeFace::PosY,
CubeFace::NegY,
CubeFace::PosZ,
CubeFace::NegZ,
];
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Kind {
D1(Size, Layer),
D2(Size, Size, Layer, NumSamples),
D3(Size, Size, Size),
}
impl Kind {
pub fn extent(&self) -> Extent {
match *self {
Kind::D1(width, _) => Extent {
width,
height: 1,
depth: 1,
},
Kind::D2(width, height, _, _) => Extent {
width,
height,
depth: 1,
},
Kind::D3(width, height, depth) => Extent {
width,
height,
depth,
},
}
}
pub fn level_extent(&self, level: Level) -> Extent {
use std::cmp::{max, min};
let map = |val| max(min(val, 1), val >> min(level, MAX_LEVEL));
match *self {
Kind::D1(w, _) => Extent {
width: map(w),
height: 1,
depth: 1,
},
Kind::D2(w, h, _, _) => Extent {
width: map(w),
height: map(h),
depth: 1,
},
Kind::D3(w, h, d) => Extent {
width: map(w),
height: map(h),
depth: map(d),
},
}
}
pub fn num_levels(&self) -> Level {
use std::cmp::max;
match *self {
Kind::D2(_, _, _, s) if s > 1 => {
1
}
_ => {
let extent = self.extent();
let dominant = max(max(extent.width, extent.height), extent.depth);
(1 ..).find(|level| dominant >> level == 0).unwrap()
}
}
}
pub fn num_layers(&self) -> Layer {
match *self {
Kind::D1(_, a) | Kind::D2(_, _, a, _) => a,
Kind::D3(..) => 1,
}
}
pub fn num_samples(&self) -> NumSamples {
match *self {
Kind::D1(..) => 1,
Kind::D2(_, _, _, s) => s,
Kind::D3(..) => 1,
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ViewKind {
D1,
D1Array,
D2,
D2Array,
D3,
Cube,
CubeArray,
}
bitflags!(
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ViewCapabilities: u32 {
const MUTABLE_FORMAT = 0x0000_0008;
const KIND_CUBE = 0x0000_0010;
const KIND_2D_ARRAY = 0x0000_0020;
}
);
bitflags!(
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Usage: u32 {
const TRANSFER_SRC = 0x1;
const TRANSFER_DST = 0x2;
const SAMPLED = 0x4;
const STORAGE = 0x8;
const COLOR_ATTACHMENT = 0x10;
const DEPTH_STENCIL_ATTACHMENT = 0x20;
const TRANSIENT_ATTACHMENT = 0x40;
const INPUT_ATTACHMENT = 0x80;
}
);
impl Usage {
pub fn can_transfer(&self) -> bool {
self.intersects(Usage::TRANSFER_SRC | Usage::TRANSFER_DST)
}
pub fn can_target(&self) -> bool {
self.intersects(Usage::COLOR_ATTACHMENT | Usage::DEPTH_STENCIL_ATTACHMENT)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum WrapMode {
Tile,
Mirror,
Clamp,
Border,
MirrorClamp,
}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Lod(pub f32);
impl Lod {
pub const RANGE: Range<Self> = Lod(f32::MIN) .. Lod(f32::MAX);
}
impl Eq for Lod {}
impl hash::Hash for Lod {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.0.to_bits().hash(state)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PackedColor(pub u32);
impl From<[f32; 4]> for PackedColor {
fn from(c: [f32; 4]) -> PackedColor {
PackedColor(
c.iter()
.rev()
.fold(0, |u, &c| (u << 8) + (c * 255.0) as u32),
)
}
}
impl Into<[f32; 4]> for PackedColor {
fn into(self) -> [f32; 4] {
let mut out = [0.0; 4];
for (i, channel) in out.iter_mut().enumerate() {
let byte = (self.0 >> (i << 3)) & 0xFF;
*channel = byte as f32 / 255.0;
}
out
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SamplerDesc {
pub min_filter: Filter,
pub mag_filter: Filter,
pub mip_filter: Filter,
pub wrap_mode: (WrapMode, WrapMode, WrapMode),
pub lod_bias: Lod,
pub lod_range: Range<Lod>,
pub comparison: Option<Comparison>,
pub border: PackedColor,
pub normalized: bool,
pub anisotropy_clamp: Option<u8>,
}
impl SamplerDesc {
pub fn new(filter: Filter, wrap: WrapMode) -> Self {
SamplerDesc {
min_filter: filter,
mag_filter: filter,
mip_filter: filter,
wrap_mode: (wrap, wrap, wrap),
lod_bias: Lod(0.0),
lod_range: Lod::RANGE.clone(),
comparison: None,
border: PackedColor(0),
normalized: true,
anisotropy_clamp: None,
}
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Layout {
General,
ColorAttachmentOptimal,
DepthStencilAttachmentOptimal,
DepthStencilReadOnlyOptimal,
ShaderReadOnlyOptimal,
TransferSrcOptimal,
TransferDstOptimal,
Undefined,
Preinitialized,
Present,
}
bitflags!(
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Access: u32 {
const INPUT_ATTACHMENT_READ = 0x10;
const SHADER_READ = 0x20;
const SHADER_WRITE = 0x40;
const COLOR_ATTACHMENT_READ = 0x80;
const COLOR_ATTACHMENT_WRITE = 0x100;
const DEPTH_STENCIL_ATTACHMENT_READ = 0x200;
const DEPTH_STENCIL_ATTACHMENT_WRITE = 0x400;
const TRANSFER_READ = 0x800;
const TRANSFER_WRITE = 0x1000;
const HOST_READ = 0x2000;
const HOST_WRITE = 0x4000;
const MEMORY_READ = 0x8000;
const MEMORY_WRITE = 0x10000;
}
);
pub type State = (Access, Layout);
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Subresource {
pub aspects: format::Aspects,
pub level: Level,
pub layer: Layer,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SubresourceLayers {
pub aspects: format::Aspects,
pub level: Level,
pub layers: Range<Layer>,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SubresourceRange {
pub aspects: format::Aspects,
pub levels: Range<Level>,
pub layers: Range<Layer>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FormatProperties {
pub max_extent: Extent,
pub max_levels: Level,
pub max_layers: Layer,
pub sample_count_mask: NumSamples,
pub max_resource_size: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SubresourceFootprint {
pub slice: Range<RawOffset>,
pub row_pitch: RawOffset,
pub array_pitch: RawOffset,
pub depth_pitch: RawOffset,
}