use crate::{BaseBufferHandle, BufferHandle};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum Layouts {
Undefined,
RenderTarget,
Transfer,
Present,
Read,
General,
ShaderBindingTable,
Indirect,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FilteringModes {
Closest,
Linear,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SamplingReductionModes {
WeightedAverage,
Min,
Max,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SamplerAddressingModes {
Repeat,
Mirror,
Clamp,
Border {},
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum UseCases {
STATIC,
DYNAMIC,
}
bitflags::bitflags! {
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Uses : u32 {
const Vertex = 1 << 0;
const Index = 1 << 1;
const Uniform = 1 << 2;
const Storage = 1 << 3;
const Indirect = 1 << 4;
const Image = 1 << 5;
const RenderTarget = 1 << 6;
const InputAttachment = 1 << 15;
const DepthStencil = 1 << 7;
const AccelerationStructure = 1 << 8;
const TransferSource = 1 << 9;
const TransferDestination = 1 << 10;
const ShaderBindingTable = 1 << 11;
const AccelerationStructureBuildScratch = 1 << 12;
const AccelerationStructureBuild = 1 << 13;
const Clear = 1 << 14;
const BlitSource = 1 << 9;
const BlitDestination = 1 << 10;
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Stages : u64 {
const NONE = 0b0;
const VERTEX = 1 << 1;
const INDEX = 1 << 2;
const TASK = 1 << 3;
const MESH = 1 << 4;
const FRAGMENT = 1 << 5;
const COMPUTE = 1 << 6;
const TRANSFER = 1 << 7;
const PRESENTATION = 1 << 8;
const HOST = 1 << 9;
const SHADER_WRITE = 1 << 10;
const RAYGEN = 1 << 11;
const CLOSEST_HIT = 1 << 12;
const ANY_HIT = 1 << 13;
const INTERSECTION = 1 << 14;
const MISS = 1 << 15;
const CALLABLE = 1 << 16;
const ACCELERATION_STRUCTURE_BUILD = 1 << 17;
const LAST = 1 << 63;
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Formats {
R8F,
R8UNORM,
R8SNORM,
R8sRGB,
R16F,
R16UNORM,
R16SNORM,
R16sRGB,
R32F,
R32UNORM,
R32SNORM,
R32sRGB,
RG8F,
RG8UNORM,
RG8SNORM,
RG8sRGB,
RG16F,
RG16UNORM,
RG16SNORM,
RG16sRGB,
RGB8F,
RGB8UNORM,
RGB8SNORM,
RGB8sRGB,
RGB16F,
RGB16UNORM,
RGB16SNORM,
RGB16sRGB,
RGBA8F,
RGBA8UNORM,
RGBA8SNORM,
RGBA8sRGB,
RGBA16F,
RGBA16UNORM,
RGBA16SNORM,
RGBA16sRGB,
RGBu11u11u10,
BGRAu8,
BGRAsRGB,
Depth32,
U32,
BC5,
BC5SNORM,
BC7,
BC7SRGB,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BcLayout {
pub blocks_w: u32,
pub blocks_h: u32,
pub bytes_per_block: u32,
pub bytes_per_row: u32,
pub bytes_per_image: u32,
}
pub fn bc_layout(width: u32, height: u32, bytes_per_block: u32) -> BcLayout {
let blocks_w = width.max(1).div_ceil(4).max(1);
let blocks_h = height.max(1).div_ceil(4).max(1);
let bytes_per_row = blocks_w * bytes_per_block;
let bytes_per_image = bytes_per_row * blocks_h;
BcLayout {
blocks_w,
blocks_h,
bytes_per_block,
bytes_per_row,
bytes_per_image,
}
}
impl Formats {
pub fn bc_bytes_per_block(&self) -> Option<u32> {
match self {
Formats::BC5 | Formats::BC5SNORM | Formats::BC7 | Formats::BC7SRGB => Some(16),
_ => None,
}
}
pub fn bc_layout(&self, width: u32, height: u32) -> Option<BcLayout> {
Some(bc_layout(width, height, self.bc_bytes_per_block()?))
}
pub fn compact_copy_layout(&self, width: u32, height: u32) -> (usize, usize, usize) {
if let Some(layout) = self.bc_layout(width, height) {
return (
layout.bytes_per_row as usize,
layout.blocks_h as usize,
layout.bytes_per_image as usize,
);
}
let bytes_per_row = width as usize * self.size();
let row_count = height as usize;
(bytes_per_row, row_count, bytes_per_row * row_count)
}
pub fn encoding(&self) -> Option<Encodings> {
match self {
Formats::R8F
| Formats::R16F
| Formats::R32F
| Formats::RG8F
| Formats::RG16F
| Formats::RGB8F
| Formats::RGB16F
| Formats::RGBA8F
| Formats::RGBA16F
| Formats::Depth32 => Some(Encodings::FloatingPoint),
Formats::R8UNORM
| Formats::R16UNORM
| Formats::R32UNORM
| Formats::RG8UNORM
| Formats::RG16UNORM
| Formats::RGB8UNORM
| Formats::RGB16UNORM
| Formats::RGBA8UNORM
| Formats::RGBA16UNORM
| Formats::RGBu11u11u10
| Formats::BGRAu8 => Some(Encodings::UnsignedNormalized),
Formats::R8SNORM
| Formats::R16SNORM
| Formats::R32SNORM
| Formats::RG8SNORM
| Formats::RG16SNORM
| Formats::RGB8SNORM
| Formats::RGB16SNORM
| Formats::RGBA8SNORM
| Formats::RGBA16SNORM => Some(Encodings::SignedNormalized),
Formats::R8sRGB
| Formats::R16sRGB
| Formats::R32sRGB
| Formats::RG8sRGB
| Formats::RG16sRGB
| Formats::RGB8sRGB
| Formats::RGB16sRGB
| Formats::RGBA8sRGB
| Formats::RGBA16sRGB
| Formats::BGRAsRGB => Some(Encodings::sRGB),
Formats::BC7SRGB => Some(Encodings::sRGB),
Formats::BC5SNORM => Some(Encodings::SignedNormalized),
Formats::U32 | Formats::BC5 | Formats::BC7 => None,
}
}
pub fn channel_bit_size(&self) -> ChannelBitSize {
match self {
Formats::R8F
| Formats::R8UNORM
| Formats::R8SNORM
| Formats::R8sRGB
| Formats::RG8F
| Formats::RG8UNORM
| Formats::RG8SNORM
| Formats::RG8sRGB
| Formats::RGB8F
| Formats::RGB8UNORM
| Formats::RGB8SNORM
| Formats::RGB8sRGB
| Formats::RGBA8F
| Formats::RGBA8UNORM
| Formats::RGBA8SNORM
| Formats::RGBA8sRGB
| Formats::BGRAu8
| Formats::BGRAsRGB => ChannelBitSize::Bits8,
Formats::R16F
| Formats::R16UNORM
| Formats::R16SNORM
| Formats::R16sRGB
| Formats::RG16F
| Formats::RG16UNORM
| Formats::RG16SNORM
| Formats::RG16sRGB
| Formats::RGB16F
| Formats::RGB16UNORM
| Formats::RGB16SNORM
| Formats::RGB16sRGB
| Formats::RGBA16F
| Formats::RGBA16UNORM
| Formats::RGBA16SNORM
| Formats::RGBA16sRGB => ChannelBitSize::Bits16,
Formats::R32F | Formats::R32UNORM | Formats::R32SNORM | Formats::R32sRGB | Formats::Depth32 | Formats::U32 => {
ChannelBitSize::Bits32
}
Formats::RGBu11u11u10 => ChannelBitSize::Bits11_11_10,
Formats::BC5 | Formats::BC5SNORM | Formats::BC7 | Formats::BC7SRGB => ChannelBitSize::Compressed,
}
}
pub fn channel_layout(&self) -> ChannelLayout {
match self {
Formats::R8F
| Formats::R8UNORM
| Formats::R8SNORM
| Formats::R8sRGB
| Formats::R16F
| Formats::R16UNORM
| Formats::R16SNORM
| Formats::R16sRGB
| Formats::R32F
| Formats::R32UNORM
| Formats::R32SNORM
| Formats::R32sRGB => ChannelLayout::R,
Formats::RG8F
| Formats::RG8UNORM
| Formats::RG8SNORM
| Formats::RG8sRGB
| Formats::RG16F
| Formats::RG16UNORM
| Formats::RG16SNORM
| Formats::RG16sRGB => ChannelLayout::RG,
Formats::RGB8F
| Formats::RGB8UNORM
| Formats::RGB8SNORM
| Formats::RGB8sRGB
| Formats::RGB16F
| Formats::RGB16UNORM
| Formats::RGB16SNORM
| Formats::RGB16sRGB
| Formats::RGBu11u11u10 => ChannelLayout::RGB,
Formats::RGBA8F
| Formats::RGBA8UNORM
| Formats::RGBA8SNORM
| Formats::RGBA8sRGB
| Formats::RGBA16F
| Formats::RGBA16UNORM
| Formats::RGBA16SNORM
| Formats::RGBA16sRGB => ChannelLayout::RGBA,
Formats::BGRAu8 | Formats::BGRAsRGB => ChannelLayout::BGRA,
Formats::Depth32 => ChannelLayout::Depth,
Formats::U32 => ChannelLayout::Packed,
Formats::BC5 | Formats::BC5SNORM | Formats::BC7 | Formats::BC7SRGB => ChannelLayout::BC,
}
}
}
pub trait Size {
fn size(&self) -> usize;
}
impl Size for Formats {
fn size(&self) -> usize {
match self {
Formats::R8F | Formats::R8UNORM | Formats::R8SNORM | Formats::R8sRGB => 1,
Formats::R16F | Formats::R16UNORM | Formats::R16SNORM | Formats::R16sRGB => 2,
Formats::R32F | Formats::R32UNORM | Formats::R32SNORM | Formats::R32sRGB => 4,
Formats::RG8F | Formats::RG8UNORM | Formats::RG8SNORM | Formats::RG8sRGB => 2,
Formats::RG16F | Formats::RG16UNORM | Formats::RG16SNORM | Formats::RG16sRGB => 4,
Formats::RGB8F | Formats::RGB8UNORM | Formats::RGB8SNORM | Formats::RGB8sRGB => 3,
Formats::RGB16F | Formats::RGB16UNORM | Formats::RGB16SNORM | Formats::RGB16sRGB => 6,
Formats::RGBA8F | Formats::RGBA8UNORM | Formats::RGBA8SNORM | Formats::RGBA8sRGB => 4,
Formats::RGBA16F | Formats::RGBA16UNORM | Formats::RGBA16SNORM | Formats::RGBA16sRGB => 8,
Formats::RGBu11u11u10 => 4,
Formats::BGRAu8 | Formats::BGRAsRGB => 4,
Formats::Depth32 => 4,
Formats::U32 => 4,
Formats::BC5 | Formats::BC5SNORM => 1,
Formats::BC7 | Formats::BC7SRGB => 1,
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum CompressionSchemes {
BC5,
BC7,
}
bitflags::bitflags! {
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct AccessPolicies : u8 {
const NONE = 0b00000000;
const READ = 0b00000001;
const WRITE = 0b00000010;
const READ_WRITE = Self::READ.bits() | Self::WRITE.bits();
}
}
#[derive(Hash, Clone, Copy, PartialEq, Eq)]
pub enum DataTypes {
Float,
Float2,
Float3,
Float4,
U8,
U16,
U32,
Int,
Int2,
Int3,
Int4,
UInt,
UInt2,
UInt3,
UInt4,
}
impl DataTypes {
pub fn size(self) -> usize {
match self {
DataTypes::Float => std::mem::size_of::<f32>(),
DataTypes::Float2 => std::mem::size_of::<f32>() * 2,
DataTypes::Float3 => std::mem::size_of::<f32>() * 3,
DataTypes::Float4 => std::mem::size_of::<f32>() * 4,
DataTypes::U8 => std::mem::size_of::<u8>(),
DataTypes::U16 => std::mem::size_of::<u16>(),
DataTypes::U32 => std::mem::size_of::<u32>(),
DataTypes::Int => std::mem::size_of::<i32>(),
DataTypes::Int2 => std::mem::size_of::<i32>() * 2,
DataTypes::Int3 => std::mem::size_of::<i32>() * 3,
DataTypes::Int4 => std::mem::size_of::<i32>() * 4,
DataTypes::UInt => std::mem::size_of::<u32>(),
DataTypes::UInt2 => std::mem::size_of::<u32>() * 2,
DataTypes::UInt3 => std::mem::size_of::<u32>() * 3,
DataTypes::UInt4 => std::mem::size_of::<u32>() * 4,
}
}
}
impl Size for DataTypes {
fn size(&self) -> usize {
(*self).size()
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct DeviceAccesses: u16 {
const CpuRead = 1 << 0;
const CpuWrite = 1 << 1;
const GpuRead = 1 << 2;
const GpuWrite = 1 << 3;
const DeviceOnly = 1 << 2 | 1 << 3;
const HostOnly = 1 << 0 | 1 << 1;
const HostToDevice = 1 << 1 | 1 << 2;
const DeviceToHost = 1 << 0 | 1 << 3;
}
}
#[derive(Clone, Copy, Debug)]
pub enum ShaderTypes {
Vertex,
Fragment,
Compute,
Task,
Mesh,
RayGen,
ClosestHit,
AnyHit,
Intersection,
Miss,
Callable,
}
impl From<ShaderTypes> for Stages {
fn from(ty: ShaderTypes) -> Self {
match ty {
ShaderTypes::Vertex => Self::VERTEX,
ShaderTypes::Fragment => Self::FRAGMENT,
ShaderTypes::Compute => Self::COMPUTE,
ShaderTypes::Task => Self::TASK,
ShaderTypes::Mesh => Self::MESH,
ShaderTypes::RayGen => Self::RAYGEN,
ShaderTypes::ClosestHit => Self::CLOSEST_HIT,
ShaderTypes::AnyHit => Self::ANY_HIT,
ShaderTypes::Intersection => Self::INTERSECTION,
ShaderTypes::Miss => Self::MISS,
ShaderTypes::Callable => Self::CALLABLE,
}
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Encodings {
FloatingPoint,
UnsignedNormalized,
SignedNormalized,
#[allow(non_camel_case_types)]
sRGB,
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum ChannelLayout {
R,
RG,
RGB,
RGBA,
BGRA,
Packed,
Depth,
BC,
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum ChannelBitSize {
Bits8,
Bits16,
Bits32,
Bits11_11_10,
Compressed,
}
pub struct BufferCopyDescriptor {
pub source_buffer: BaseBufferHandle,
pub source_offset: usize,
pub destination_buffer: BaseBufferHandle,
pub destination_offset: usize,
pub size: usize,
}
impl BufferCopyDescriptor {
pub fn new(
source_buffer: BaseBufferHandle,
source_offset: usize,
destination_buffer: BaseBufferHandle,
destination_offset: usize,
size: usize,
) -> Self {
Self {
source_buffer,
source_offset,
destination_buffer,
destination_offset,
size,
}
}
}
pub struct BufferImageCopyDescriptor {
pub source_buffer: BaseBufferHandle,
pub source_offset: usize,
pub source_bytes_per_row: usize,
pub source_bytes_per_image: usize,
pub destination_image: crate::BaseImageHandle,
}
impl BufferImageCopyDescriptor {
pub fn new(
source_buffer: BaseBufferHandle,
source_offset: usize,
source_bytes_per_row: usize,
source_bytes_per_image: usize,
destination_image: crate::BaseImageHandle,
) -> Self {
Self {
source_buffer,
source_offset,
source_bytes_per_row,
source_bytes_per_image,
destination_image,
}
}
}
#[derive(Clone, Copy)]
pub struct ImageBufferCopyDescriptor {
pub source: crate::ImageOrSwapchain,
pub destination_buffer: BaseBufferHandle,
pub destination_offset: usize,
pub destination_bytes_per_row: usize,
pub destination_bytes_per_image: usize,
}
impl ImageBufferCopyDescriptor {
pub fn new(
source_image: crate::BaseImageHandle,
destination_buffer: BaseBufferHandle,
destination_offset: usize,
destination_bytes_per_row: usize,
destination_bytes_per_image: usize,
) -> Self {
Self {
source: source_image.into(),
destination_buffer,
destination_offset,
destination_bytes_per_row,
destination_bytes_per_image,
}
}
pub fn swapchain(
source_swapchain: crate::SwapchainHandle,
destination_buffer: BaseBufferHandle,
destination_offset: usize,
destination_bytes_per_row: usize,
destination_bytes_per_image: usize,
) -> Self {
Self {
source: source_swapchain.into(),
destination_buffer,
destination_offset,
destination_bytes_per_row,
destination_bytes_per_image,
}
}
}
pub struct BufferDescriptor {
pub(super) buffer: BaseBufferHandle,
pub(super) offset: usize,
pub(super) index_type: Option<DataTypes>,
}
impl BufferDescriptor {
pub fn new<T: Copy, const N: usize>(buffer: BufferHandle<[T; N]>) -> Self {
Self {
buffer: buffer.into(),
offset: 0,
index_type: None,
}
}
pub fn offset(mut self, offset: usize) -> Self {
self.offset = offset;
self
}
pub fn index_type(mut self, index_type: DataTypes) -> Self {
self.index_type = Some(index_type);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bc_layout_uses_ceil_block_counts_and_keeps_small_mips_nonzero() {
let layout = bc_layout(5, 7, 16);
assert_eq!(layout.blocks_w, 2);
assert_eq!(layout.blocks_h, 2);
assert_eq!(layout.bytes_per_block, 16);
assert_eq!(layout.bytes_per_row, 32);
assert_eq!(layout.bytes_per_image, 64);
let small = bc_layout(1, 1, 8);
assert_eq!(small.blocks_w, 1);
assert_eq!(small.blocks_h, 1);
assert_eq!(small.bytes_per_row, 8);
assert_eq!(small.bytes_per_image, 8);
}
#[test]
fn format_bc_layout_uses_format_block_size() {
assert_eq!(Formats::BC5.bc_bytes_per_block(), Some(16));
assert_eq!(Formats::BC7.bc_layout(8, 4).unwrap().bytes_per_image, 32);
assert_eq!(Formats::RGBA8UNORM.bc_layout(8, 4), None);
}
#[test]
fn compact_copy_layout_preserves_texel_rows_and_bc_block_rows() {
let cases = [
(Formats::RGBA8UNORM, 5, 7, (20, 7, 140)),
(Formats::RGB16UNORM, 5, 7, (30, 7, 210)),
(Formats::RGBu11u11u10, 5, 7, (20, 7, 140)),
(Formats::Depth32, 5, 7, (20, 7, 140)),
(Formats::BC7, 5, 7, (32, 2, 64)),
(Formats::RGBA8UNORM, 0, 0, (0, 0, 0)),
(Formats::BC7, 0, 0, (16, 1, 16)),
];
for (format, width, height, expected) in cases {
assert_eq!(format.compact_copy_layout(width, height), expected);
}
}
#[test]
fn shader_stage_conversion_is_one_to_one() {
let cases = [
(ShaderTypes::Vertex, Stages::VERTEX),
(ShaderTypes::Fragment, Stages::FRAGMENT),
(ShaderTypes::Compute, Stages::COMPUTE),
(ShaderTypes::Task, Stages::TASK),
(ShaderTypes::Mesh, Stages::MESH),
(ShaderTypes::RayGen, Stages::RAYGEN),
(ShaderTypes::ClosestHit, Stages::CLOSEST_HIT),
(ShaderTypes::AnyHit, Stages::ANY_HIT),
(ShaderTypes::Intersection, Stages::INTERSECTION),
(ShaderTypes::Miss, Stages::MISS),
(ShaderTypes::Callable, Stages::CALLABLE),
];
for (shader, expected_stage) in cases {
assert_eq!(Stages::from(shader), expected_stage);
}
}
#[test]
fn primitive_data_type_sizes_match_gpu_scalar_widths() {
let cases = [
(DataTypes::Float, 4),
(DataTypes::Float2, 8),
(DataTypes::Float3, 12),
(DataTypes::Float4, 16),
(DataTypes::U8, 1),
(DataTypes::U16, 2),
(DataTypes::U32, 4),
(DataTypes::Int, 4),
(DataTypes::Int2, 8),
(DataTypes::Int3, 12),
(DataTypes::Int4, 16),
(DataTypes::UInt, 4),
(DataTypes::UInt2, 8),
(DataTypes::UInt3, 12),
(DataTypes::UInt4, 16),
];
for (data_type, expected_size) in cases {
assert_eq!(data_type.size(), expected_size);
assert_eq!(Size::size(&data_type), expected_size);
}
}
#[test]
fn access_and_use_aliases_preserve_backend_bit_contracts() {
assert_eq!(AccessPolicies::READ_WRITE, AccessPolicies::READ | AccessPolicies::WRITE);
assert_eq!(DeviceAccesses::DeviceOnly, DeviceAccesses::GpuRead | DeviceAccesses::GpuWrite);
assert_eq!(DeviceAccesses::HostOnly, DeviceAccesses::CpuRead | DeviceAccesses::CpuWrite);
assert_eq!(Uses::BlitSource, Uses::TransferSource);
assert_eq!(Uses::BlitDestination, Uses::TransferDestination);
}
}
impl<T: Copy> From<BufferHandle<T>> for BufferDescriptor {
fn from(val: BufferHandle<T>) -> Self {
BufferDescriptor {
buffer: val.into(),
offset: 0,
index_type: None,
}
}
}
pub struct BufferStridedRange {
pub(super) buffer_offset: BufferDescriptor,
pub(super) stride: usize,
pub(super) size: usize,
}
impl BufferStridedRange {
pub fn new(buffer: BaseBufferHandle, offset: usize, stride: usize, size: usize) -> Self {
Self {
buffer_offset: BufferDescriptor {
buffer,
offset,
index_type: None,
},
stride,
size,
}
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct WorkloadTypes: u16 {
const RASTER = 1 << 0;
const RAY_TRACING = 1 << 1;
const COMPUTE = 1 << 2;
const TRANSFER = 1 << 3;
const VIDEO = 1 << 4;
const IO = 1 << 5;
}
}