#![allow(
clippy::match_like_matches_macro,
clippy::redundant_pattern_matching,
clippy::needless_lifetimes,
clippy::new_without_default,
clippy::single_match,
clippy::vec_init_then_push,
clippy::missing_safety_doc,
)]
#![warn(
trivial_casts,
trivial_numeric_casts,
unused_extern_crates,
clippy::pattern_type_mismatch,
)]
pub use naga::{StorageAccess, VectorSize};
pub type Transform = mint::RowMatrix3x4<f32>;
pub const IDENTITY_TRANSFORM: Transform = mint::RowMatrix3x4 {
x: mint::Vector4 {
x: 1.0,
y: 0.0,
z: 0.0,
w: 0.0,
},
y: mint::Vector4 {
x: 0.0,
y: 1.0,
z: 0.0,
w: 0.0,
},
z: mint::Vector4 {
x: 0.0,
y: 0.0,
z: 1.0,
w: 0.0,
},
};
pub mod derive;
#[cfg_attr(
all(not(vulkan), not(gles), any(target_os = "ios", target_os = "macos")),
path = "metal/mod.rs"
)]
#[cfg_attr(
all(
not(gles),
any(
vulkan,
windows,
target_os = "linux",
target_os = "android",
target_os = "freebsd"
)
),
path = "vulkan/mod.rs"
)]
#[cfg_attr(any(gles, target_arch = "wasm32"), path = "gles/mod.rs")]
mod hal;
mod shader;
mod traits;
pub mod util;
pub mod limits {
pub const PLAIN_DATA_SIZE: u32 = 256;
pub const RESOURCES_IN_GROUP: u32 = 8;
pub const STORAGE_BUFFER_ALIGNMENT: u64 = 256;
pub const ACCELERATION_STRUCTURE_SCRATCH_ALIGNMENT: u64 = 256;
}
pub use hal::*;
use std::{fmt, num::NonZeroU32};
#[derive(Clone, Debug, Default)]
pub struct ContextDesc {
pub validation: bool,
pub capture: bool,
pub overlay: bool,
}
#[derive(Debug)]
pub enum NotSupportedError {
#[cfg(all(
not(gles),
any(
vulkan,
windows,
target_os = "linux",
target_os = "android",
target_os = "freebsd"
)
))]
VulkanLoadingError(ash::LoadingError),
#[cfg(all(
not(gles),
any(
vulkan,
windows,
target_os = "linux",
target_os = "android",
target_os = "freebsd"
)
))]
VulkanError(ash::vk::Result),
#[cfg(gles)]
GLESLoadingError(egl::LoadError<libloading::Error>),
#[cfg(gles)]
GLESError(egl::Error),
NoSupportedDeviceFound,
PlatformNotSupported,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Capabilities {
pub ray_query: ShaderVisibility,
}
#[derive(Clone, Debug, Default)]
pub struct DeviceInformation {
pub is_software_emulated: bool,
pub device_name: String,
pub driver_name: String,
pub driver_info: String,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Memory {
Device,
Shared,
Upload,
}
impl Memory {
pub fn is_host_visible(&self) -> bool {
match *self {
Self::Device => false,
Self::Shared | Self::Upload => true,
}
}
}
#[derive(Debug)]
pub struct BufferDesc<'a> {
pub name: &'a str,
pub size: u64,
pub memory: Memory,
}
#[derive(Clone, Copy, Debug)]
pub struct BufferPiece {
pub buffer: Buffer,
pub offset: u64,
}
impl From<Buffer> for BufferPiece {
fn from(buffer: Buffer) -> Self {
Self { buffer, offset: 0 }
}
}
impl BufferPiece {
pub fn data(&self) -> *mut u8 {
let base = self.buffer.data();
assert!(!base.is_null());
unsafe { base.offset(self.offset as isize) }
}
}
impl Buffer {
pub fn at(self, offset: u64) -> BufferPiece {
BufferPiece {
buffer: self,
offset,
}
}
}
pub type ResourceIndex = u32;
pub struct ResourceArray<T, const N: ResourceIndex> {
data: Vec<T>,
free_list: Vec<ResourceIndex>,
}
impl<T, const N: ResourceIndex> ResourceArray<T, N> {
pub fn new() -> Self {
Self {
data: Vec::with_capacity(N as usize),
free_list: Vec::new(),
}
}
pub fn alloc(&mut self, value: T) -> ResourceIndex {
if let Some(index) = self.free_list.pop() {
self.data[index as usize] = value;
index
} else {
let index = self.data.len() as u32;
assert!(index < N);
self.data.push(value);
index
}
}
pub fn free(&mut self, index: ResourceIndex) {
self.free_list.push(index);
}
pub fn clear(&mut self) {
self.data.clear();
self.free_list.clear();
}
}
impl<T, const N: ResourceIndex> std::ops::Index<ResourceIndex> for ResourceArray<T, N> {
type Output = T;
fn index(&self, index: ResourceIndex) -> &T {
&self.data[index as usize]
}
}
impl<T, const N: ResourceIndex> std::ops::IndexMut<ResourceIndex> for ResourceArray<T, N> {
fn index_mut(&mut self, index: ResourceIndex) -> &mut T {
&mut self.data[index as usize]
}
}
pub type BufferArray<const N: ResourceIndex> = ResourceArray<BufferPiece, N>;
pub type TextureArray<const N: ResourceIndex> = ResourceArray<TextureView, N>;
#[derive(Clone, Copy, Debug)]
pub struct TexturePiece {
pub texture: Texture,
pub mip_level: u32,
pub array_layer: u32,
pub origin: [u32; 3],
}
impl From<Texture> for TexturePiece {
fn from(texture: Texture) -> Self {
Self {
texture,
mip_level: 0,
array_layer: 0,
origin: [0; 3],
}
}
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
pub enum TextureFormat {
R8Unorm,
Rg8Unorm,
Rg8Snorm,
Rgba8Unorm,
Rgba8UnormSrgb,
Bgra8Unorm,
Bgra8UnormSrgb,
Rgba8Snorm,
R16Float,
Rgba16Float,
R32Float,
Rg32Float,
Rgba32Float,
R32Uint,
Rg32Uint,
Rgba32Uint,
Depth32Float,
Bc1Unorm,
Bc1UnormSrgb,
Bc2Unorm,
Bc2UnormSrgb,
Bc3Unorm,
Bc3UnormSrgb,
Bc4Unorm,
Bc4Snorm,
Bc5Unorm,
Bc5Snorm,
}
#[derive(Clone, Copy, Debug)]
pub struct TexelBlockInfo {
pub dimensions: (u8, u8),
pub size: u8,
}
bitflags::bitflags! {
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
pub struct TexelAspects: u8 {
const COLOR = 0x1;
const DEPTH = 0x2;
const STENCIL = 0x4;
}
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum TextureDimension {
D1,
D2,
D3,
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum ViewDimension {
D1,
D1Array,
D2,
D2Array,
Cube,
CubeArray,
D3,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct Extent {
pub width: u32,
pub height: u32,
pub depth: u32,
}
impl Default for Extent {
fn default() -> Self {
Self {
width: 1,
height: 1,
depth: 1,
}
}
}
impl fmt::Display for Extent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}x{}x{}", self.width, self.height, self.depth)
}
}
impl Extent {
pub fn max_mip_levels(&self) -> u32 {
self.width
.max(self.height)
.max(self.depth)
.next_power_of_two()
.trailing_zeros()
}
pub fn at_mip_level(&self, level: u32) -> Self {
Self {
width: (self.width >> level).max(1),
height: (self.height >> level).max(1),
depth: (self.depth >> level).max(1),
}
}
}
bitflags::bitflags! {
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
pub struct TextureUsage: u32 {
const COPY = 1 << 0;
const TARGET = 1 << 1;
const RESOURCE = 1 << 2;
const STORAGE = 1 << 3;
}
}
#[derive(Debug)]
pub struct TextureDesc<'a> {
pub name: &'a str,
pub format: TextureFormat,
pub size: Extent,
pub array_layer_count: u32,
pub mip_level_count: u32,
pub dimension: TextureDimension,
pub usage: TextureUsage,
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct TextureSubresources {
pub base_mip_level: u32,
pub mip_level_count: Option<NonZeroU32>,
pub base_array_layer: u32,
pub array_layer_count: Option<NonZeroU32>,
}
#[derive(Debug)]
pub struct TextureViewDesc<'a> {
pub name: &'a str,
pub format: TextureFormat,
pub dimension: ViewDimension,
pub subresources: &'a TextureSubresources,
}
bitflags::bitflags! {
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
pub struct ShaderVisibility: u32 {
const COMPUTE = 1 << 0;
const VERTEX = 1 << 1;
const FRAGMENT = 1 << 2;
}
}
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
pub enum AddressMode {
#[default]
ClampToEdge,
Repeat,
MirrorRepeat,
ClampToBorder,
}
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
pub enum FilterMode {
#[default]
Nearest,
Linear,
}
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
pub enum CompareFunction {
Never,
Less,
Equal,
LessEqual,
Greater,
NotEqual,
GreaterEqual,
#[default]
Always,
}
#[derive(Clone, Copy, Debug)]
pub enum TextureColor {
TransparentBlack,
OpaqueBlack,
White,
}
#[derive(Debug, Default)]
pub struct SamplerDesc<'a> {
pub name: &'a str,
pub address_modes: [AddressMode; 3],
pub mag_filter: FilterMode,
pub min_filter: FilterMode,
pub mipmap_filter: FilterMode,
pub lod_min_clamp: f32,
pub lod_max_clamp: Option<f32>,
pub compare: Option<CompareFunction>,
pub anisotropy_clamp: u32,
pub border_color: Option<TextureColor>,
}
#[derive(Debug)]
pub enum AccelerationStructureType {
TopLevel,
BottomLevel,
}
#[derive(Debug)]
pub struct AccelerationStructureDesc<'a> {
pub name: &'a str,
pub ty: AccelerationStructureType,
pub size: u64,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
pub enum VertexFormat {
F32,
F32Vec2,
F32Vec3,
F32Vec4,
U32,
U32Vec2,
U32Vec3,
U32Vec4,
I32,
I32Vec2,
I32Vec3,
I32Vec4,
}
#[derive(Clone, Debug)]
pub struct AccelerationStructureMesh {
pub vertex_data: BufferPiece,
pub vertex_format: VertexFormat,
pub vertex_stride: u32,
pub vertex_count: u32,
pub index_data: BufferPiece,
pub index_type: Option<IndexType>,
pub triangle_count: u32,
pub transform_data: BufferPiece,
pub is_opaque: bool,
}
#[derive(Clone, Debug)]
pub struct AccelerationStructureInstance {
pub acceleration_structure_index: u32,
pub transform: Transform,
pub mask: u32,
pub custom_index: u32,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct AccelerationStructureSizes {
pub data: u64,
pub scratch: u64,
}
pub struct Shader {
module: naga::Module,
info: naga::valid::ModuleInfo,
}
#[derive(Clone, Copy)]
pub struct ShaderFunction<'a> {
pub shader: &'a Shader,
pub entry_point: &'a str,
}
impl ShaderFunction<'_> {
fn entry_point_index(&self) -> usize {
self.shader
.module
.entry_points
.iter()
.position(|ep| ep.name == self.entry_point)
.expect("Entry point not found in the shader")
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ShaderBinding {
Texture,
TextureArray { count: u32 },
Sampler,
Buffer,
BufferArray { count: u32 },
AccelerationStructure,
Plain { size: u32 },
}
pub trait ShaderBindable: Clone + Copy + derive::HasShaderBinding {
fn bind_to(&self, context: &mut PipelineContext, index: u32);
}
#[derive(Debug)]
struct ShaderDataInfo {
visibility: ShaderVisibility,
binding_access: Box<[StorageAccess]>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ShaderDataLayout {
pub bindings: Vec<(&'static str, ShaderBinding)>,
}
impl ShaderDataLayout {
pub const EMPTY: &'static Self = &Self {
bindings: Vec::new(),
};
fn to_info(&self) -> ShaderDataInfo {
ShaderDataInfo {
visibility: ShaderVisibility::empty(),
binding_access: vec![StorageAccess::empty(); self.bindings.len()].into_boxed_slice(),
}
}
}
pub trait ShaderData {
fn layout() -> ShaderDataLayout;
fn fill(&self, context: PipelineContext);
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct VertexAttribute {
pub offset: u32,
pub format: VertexFormat,
}
struct VertexAttributeMapping {
buffer_index: usize,
attribute_index: usize,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct VertexLayout {
pub attributes: Vec<(&'static str, VertexAttribute)>,
pub stride: u32,
}
pub trait Vertex {
fn layout() -> VertexLayout;
}
#[derive(Clone, Debug, PartialEq)]
pub struct VertexFetchState<'a> {
pub layout: &'a VertexLayout,
pub instanced: bool,
}
pub struct ShaderDesc<'a> {
pub source: &'a str,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub enum CommandType {
Transfer,
Compute,
#[default]
General,
}
pub struct CommandEncoderDesc<'a> {
pub name: &'a str,
pub buffer_count: u32,
}
pub struct ComputePipelineDesc<'a> {
pub name: &'a str,
pub data_layouts: &'a [&'a ShaderDataLayout],
pub compute: ShaderFunction<'a>,
}
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
pub enum PrimitiveTopology {
PointList,
LineList,
LineStrip,
#[default]
TriangleList,
TriangleStrip,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub enum FrontFace {
#[default]
Ccw,
Cw,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Face {
Front,
Back,
}
#[derive(Clone, Debug, Default)]
pub struct PrimitiveState {
pub topology: PrimitiveTopology,
pub front_face: FrontFace,
pub cull_mode: Option<Face>,
pub unclipped_depth: bool,
pub wireframe: bool,
}
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
pub enum StencilOperation {
#[default]
Keep,
Zero,
Replace,
Invert,
IncrementClamp,
DecrementClamp,
IncrementWrap,
DecrementWrap,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct StencilFaceState {
pub compare: CompareFunction,
pub fail_op: StencilOperation,
pub depth_fail_op: StencilOperation,
pub pass_op: StencilOperation,
}
impl StencilFaceState {
pub const IGNORE: Self = StencilFaceState {
compare: CompareFunction::Always,
fail_op: StencilOperation::Keep,
depth_fail_op: StencilOperation::Keep,
pass_op: StencilOperation::Keep,
};
}
impl Default for StencilFaceState {
fn default() -> Self {
Self::IGNORE
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct StencilState {
pub front: StencilFaceState,
pub back: StencilFaceState,
pub read_mask: u32,
pub write_mask: u32,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct DepthBiasState {
pub constant: i32,
pub slope_scale: f32,
pub clamp: f32,
}
#[derive(Clone, Debug)]
pub struct DepthStencilState {
pub format: TextureFormat,
pub depth_write_enabled: bool,
pub depth_compare: CompareFunction,
pub stencil: StencilState,
pub bias: DepthBiasState,
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum BlendFactor {
Zero,
One,
Src,
OneMinusSrc,
SrcAlpha,
OneMinusSrcAlpha,
Dst,
OneMinusDst,
DstAlpha,
OneMinusDstAlpha,
SrcAlphaSaturated,
Constant,
OneMinusConstant,
}
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
pub enum BlendOperation {
#[default]
Add,
Subtract,
ReverseSubtract,
Min,
Max,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct BlendComponent {
pub src_factor: BlendFactor,
pub dst_factor: BlendFactor,
pub operation: BlendOperation,
}
impl BlendComponent {
pub const REPLACE: Self = Self {
src_factor: BlendFactor::One,
dst_factor: BlendFactor::Zero,
operation: BlendOperation::Add,
};
pub const OVER: Self = Self {
src_factor: BlendFactor::One,
dst_factor: BlendFactor::OneMinusSrcAlpha,
operation: BlendOperation::Add,
};
pub const ADDITIVE: Self = Self {
src_factor: BlendFactor::One,
dst_factor: BlendFactor::One,
operation: BlendOperation::Add,
};
}
impl Default for BlendComponent {
fn default() -> Self {
Self::REPLACE
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct BlendState {
pub color: BlendComponent,
pub alpha: BlendComponent,
}
impl BlendState {
pub const REPLACE: Self = Self {
color: BlendComponent::REPLACE,
alpha: BlendComponent::REPLACE,
};
pub const ALPHA_BLENDING: Self = Self {
color: BlendComponent {
src_factor: BlendFactor::SrcAlpha,
dst_factor: BlendFactor::OneMinusSrcAlpha,
operation: BlendOperation::Add,
},
alpha: BlendComponent::OVER,
};
pub const PREMULTIPLIED_ALPHA_BLENDING: Self = Self {
color: BlendComponent::OVER,
alpha: BlendComponent::OVER,
};
pub const ADDITIVE: Self = Self {
color: BlendComponent::ADDITIVE,
alpha: BlendComponent::ADDITIVE,
};
}
bitflags::bitflags! {
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct ColorWrites: u32 {
const RED = 1 << 0;
const GREEN = 1 << 1;
const BLUE = 1 << 2;
const ALPHA = 1 << 3;
const COLOR = Self::RED.bits() | Self::GREEN.bits() | Self::BLUE.bits();
const ALL = Self::RED.bits() | Self::GREEN.bits() | Self::BLUE.bits() | Self::ALPHA.bits();
}
}
impl Default for ColorWrites {
fn default() -> Self {
Self::ALL
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ColorTargetState {
pub format: TextureFormat,
pub blend: Option<BlendState>,
pub write_mask: ColorWrites,
}
impl From<TextureFormat> for ColorTargetState {
fn from(format: TextureFormat) -> Self {
Self {
format,
blend: None,
write_mask: ColorWrites::ALL,
}
}
}
pub struct RenderPipelineDesc<'a> {
pub name: &'a str,
pub data_layouts: &'a [&'a ShaderDataLayout],
pub vertex: ShaderFunction<'a>,
pub vertex_fetches: &'a [VertexFetchState<'a>],
pub primitive: PrimitiveState,
pub depth_stencil: Option<DepthStencilState>,
pub fragment: ShaderFunction<'a>,
pub color_targets: &'a [ColorTargetState],
}
#[derive(Clone, Copy, Debug)]
pub enum InitOp {
Load,
Clear(TextureColor),
}
#[derive(Clone, Copy, Debug)]
pub enum FinishOp {
Store,
Discard,
ResolveTo(TextureView),
Ignore,
}
#[derive(Debug)]
pub struct RenderTarget {
pub view: TextureView,
pub init_op: InitOp,
pub finish_op: FinishOp,
}
#[derive(Debug)]
pub struct RenderTargetSet<'a> {
pub colors: &'a [RenderTarget],
pub depth_stencil: Option<RenderTarget>,
}
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
pub enum DisplaySync {
#[default]
Block,
Recent,
Tear,
}
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
pub enum ColorSpace {
#[default]
Linear,
Srgb,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct SurfaceConfig {
pub size: Extent,
pub usage: TextureUsage,
pub display_sync: DisplaySync,
pub color_space: ColorSpace,
pub transparent: bool,
pub allow_exclusive_full_screen: bool,
}
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
pub enum AlphaMode {
#[default]
Ignored,
PreMultiplied,
PostMultiplied,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SurfaceInfo {
pub format: TextureFormat,
pub alpha: AlphaMode,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum IndexType {
U16,
U32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ScissorRect {
pub x: i32,
pub y: i32,
pub w: u32,
pub h: u32,
}