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, thiserror::Error)]
pub enum CreationError {
#[error(transparent)]
OutOfMemory(#[from] device::OutOfMemory),
#[error("Unsupported format: {0:?}")]
Format(format::Format),
#[error("Specified kind doesn't support particular operation")]
Kind,
#[error("Specified format doesn't support specified sampling {0:}")]
Samples(NumSamples),
#[error("Unsupported size in one of the dimensions {0:}")]
Size(Size),
#[error("The given data has a different size ({0:}) than the target image slice")]
Data(usize),
#[error("Unsupported usage: {0:?}")]
Usage(Usage),
}
#[derive(Clone, Debug, PartialEq, thiserror::Error)]
pub enum ViewCreationError {
#[error(transparent)]
OutOfMemory(#[from] device::OutOfMemory),
#[error("Specified usage flags are not present in the image {0:?}")]
Usage(Usage),
#[error("Selected level doesn't exist in the image {0:}")]
Level(Level),
#[error(transparent)]
Layer(#[from] LayerError),
#[error("Incompatible format: {0:?}")]
BadFormat(format::Format),
#[error("Incompatible kind: {0:?}")]
BadKind(ViewKind),
#[error("Implementation specific error occurred")]
Unsupported,
}
#[derive(Clone, Debug, PartialEq, thiserror::Error)]
pub enum LayerError {
#[error("Source image doesn't support view kind {0:?}")]
NotExpected(Kind),
#[error("Selected layers are out of bounds")]
OutOfBounds,
}
#[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 compute_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))]
#[derive(Default)]
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, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ReductionMode {
WeightedAverage,
Minimum,
Maximum,
}
#[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, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum BorderColor {
TransparentBlack,
OpaqueBlack,
OpaqueWhite,
}
impl Into<[f32; 4]> for BorderColor {
fn into(self) -> [f32; 4] {
match self {
BorderColor::TransparentBlack => [0.0, 0.0, 0.0, 0.0],
BorderColor::OpaqueBlack => [0.0, 0.0, 0.0, 1.0],
BorderColor::OpaqueWhite => [1.0, 1.0, 1.0, 1.0],
}
}
}
#[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 reduction_mode: ReductionMode,
pub wrap_mode: (WrapMode, WrapMode, WrapMode),
pub lod_bias: Lod,
pub lod_range: Range<Lod>,
pub comparison: Option<Comparison>,
pub border: BorderColor,
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,
reduction_mode: ReductionMode::WeightedAverage,
wrap_mode: (wrap, wrap, wrap),
lod_bias: Lod(0.0),
lod_range: Lod::RANGE.clone(),
comparison: None,
border: BorderColor::TransparentBlack,
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,
}
impl Default for Layout {
fn default() -> Self {
Self::General
}
}
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, Default, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SubresourceRange {
pub aspects: format::Aspects,
pub level_start: Level,
pub level_count: Option<Level>,
pub layer_start: Layer,
pub layer_count: Option<Layer>,
}
impl From<SubresourceLayers> for SubresourceRange {
fn from(sub: SubresourceLayers) -> Self {
SubresourceRange {
aspects: sub.aspects,
level_start: sub.level,
level_count: Some(1),
layer_start: sub.layers.start,
layer_count: Some(sub.layers.end - sub.layers.start),
}
}
}
impl SubresourceRange {
pub fn resolve_level_count(&self, total: Level) -> Level {
self.level_count.unwrap_or(total - self.level_start)
}
pub fn resolve_layer_count(&self, total: Layer) -> Layer {
self.layer_count.unwrap_or(total - self.layer_start)
}
}
#[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,
}
#[derive(Debug)]
pub enum TileKind {
Volume,
Flat(NumSamples),
}
pub fn get_tile_size(tile_kind: TileKind, texel_bits: u16) -> (u16, u16, u16) {
match tile_kind {
TileKind::Flat(samples) => {
let sizes = match texel_bits {
8 => (256, 256, 1),
16 => (256, 128, 1),
32 => (128, 128, 1),
64 => (128, 64, 1),
128 => (64, 64, 1),
_ => unimplemented!(),
};
match samples {
1 => sizes,
2 => (sizes.0 / 2, sizes.1, 1),
4 => (sizes.0 / 2, sizes.1 / 2, 1),
8 => (sizes.0 / 4, sizes.1 / 2, 1),
16 => (sizes.0 / 4, sizes.1 / 4, 1),
_ => unimplemented!(),
}
}
TileKind::Volume => match texel_bits {
8 => (64, 32, 32),
16 => (32, 32, 32),
32 => (32, 32, 16),
64 => (32, 16, 16),
128 => (16, 16, 16),
_ => unimplemented!(),
},
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FramebufferAttachment {
pub usage: Usage,
pub view_caps: ViewCapabilities,
pub format: format::Format,
}