#![allow(
// We don't use syntax sugar where it's not necessary.
clippy::match_like_matches_macro,
// Redundant matching is more explicit.
clippy::redundant_pattern_matching,
// Explicit lifetimes are often easier to reason about.
clippy::needless_lifetimes,
// No need for defaults in the internal types.
clippy::new_without_default,
// Matches are good and extendable, no need to make an exception here.
clippy::single_match,
// Push commands are more regular than macros.
clippy::vec_init_then_push,
// "if panic" is a good uniform construct.
clippy::if_then_panic,
)]
#![warn(
trivial_casts,
trivial_numeric_casts,
unused_extern_crates,
unused_qualifications,
// We don't match on a reference, unless required.
clippy::pattern_type_mismatch,
)]
pub use naga::{StorageAccess, VectorSize};
#[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")
),
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 use hal::*;
use std::num::NonZeroU32;
#[derive(Debug)]
pub struct ContextDesc {
pub validation: bool,
pub capture: bool,
}
#[derive(Debug)]
pub struct NotSupportedError;
#[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,
}
}
}
#[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 {
Rgba8Unorm,
Rgba8UnormSrgb,
Bgra8UnormSrgb,
Depth32Float,
}
#[derive(Clone, Copy, Debug)]
pub struct TexelBlockInfo {
pub dimensions: (u8, u8),
pub size: u8,
}
bitflags::bitflags! {
pub struct TexelAspects: u8 {
const COLOR = 0 << 1;
const DEPTH = 1 << 1;
const STENCIL = 1 << 2;
}
}
#[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,
}
#[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 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! {
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 texture: Texture,
pub format: TextureFormat,
pub dimension: ViewDimension,
pub subresources: &'a TextureSubresources,
}
bitflags::bitflags! {
#[derive(Default)]
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>,
}
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,
Sampler,
Buffer,
Plain { size: u32 },
}
pub trait ShaderBindable: Clone + Copy {
fn bind_to(&self, context: &mut PipelineContext, index: u32);
}
pub trait HasShaderBinding: ShaderBindable {
const TYPE: ShaderBinding;
}
impl<T: bytemuck::Pod> HasShaderBinding for T {
const TYPE: ShaderBinding = ShaderBinding::Plain {
size: std::mem::size_of::<T>() as u32,
};
}
impl HasShaderBinding for TextureView {
const TYPE: ShaderBinding = ShaderBinding::Texture;
}
impl HasShaderBinding for Sampler {
const TYPE: ShaderBinding = ShaderBinding::Sampler;
}
impl HasShaderBinding for BufferPiece {
const TYPE: ShaderBinding = ShaderBinding::Buffer;
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ShaderDataLayout {
pub bindings: Vec<(&'static str, ShaderBinding)>,
}
impl ShaderDataLayout {
pub const EMPTY: &Self = &Self {
bindings: Vec::new(),
};
}
pub trait ShaderData {
fn layout() -> ShaderDataLayout;
fn fill(&self, context: PipelineContext);
}
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,
};
}
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,
};
}
bitflags::bitflags! {
#[repr(transparent)]
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 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(Debug)]
pub struct SurfaceConfig {
pub size: Extent,
pub usage: TextureUsage,
pub frame_count: u32,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum IndexType {
U16,
U32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ScissorRect {
pub x: u32,
pub y: u32,
pub w: u32,
pub h: u32,
}