Skip to main content

blade_graphics/
lib.rs

1#![allow(
2    // We don't use syntax sugar where it's not necessary.
3    clippy::match_like_matches_macro,
4    // Redundant matching is more explicit.
5    clippy::redundant_pattern_matching,
6    // Explicit lifetimes are often easier to reason about.
7    clippy::needless_lifetimes,
8    // No need for defaults in the internal types.
9    clippy::new_without_default,
10    // Matches are good and extendable, no need to make an exception here.
11    clippy::single_match,
12    // Push commands are more regular than macros.
13    clippy::vec_init_then_push,
14    // This is the land of unsafe.
15    clippy::missing_safety_doc,
16    // Let me decide when it's too many.
17    clippy::too_many_arguments,
18)]
19#![warn(
20    trivial_numeric_casts,
21    unused_extern_crates,
22    //TODO: re-enable. Currently doesn't like "mem::size_of" on newer Rust
23    //unused_qualifications,
24    // We don't match on a reference, unless required.
25    clippy::pattern_type_mismatch,
26)]
27
28pub use naga::{StorageAccess, VectorSize, back::PipelineConstants};
29pub type Transform = mint::RowMatrix3x4<f32>;
30
31pub const IDENTITY_TRANSFORM: Transform = mint::RowMatrix3x4 {
32    x: mint::Vector4 {
33        x: 1.0,
34        y: 0.0,
35        z: 0.0,
36        w: 0.0,
37    },
38    y: mint::Vector4 {
39        x: 0.0,
40        y: 1.0,
41        z: 0.0,
42        w: 0.0,
43    },
44    z: mint::Vector4 {
45        x: 0.0,
46        y: 0.0,
47        z: 1.0,
48        w: 0.0,
49    },
50};
51
52pub mod derive;
53#[cfg_attr(
54    all(not(vulkan), not(gles), any(target_os = "ios", target_os = "macos")),
55    path = "metal/mod.rs"
56)]
57#[cfg_attr(
58    all(
59        not(gles),
60        any(
61            vulkan,
62            windows,
63            target_os = "linux",
64            target_os = "android",
65            target_os = "freebsd"
66        )
67    ),
68    path = "vulkan/mod.rs"
69)]
70#[cfg_attr(any(gles, target_arch = "wasm32"), path = "gles/mod.rs")]
71mod hal;
72mod shader;
73pub mod traits;
74pub mod util;
75pub mod limits {
76    /// Max number of passes inside a command encoder.
77    pub const PASS_COUNT: usize = 100;
78    /// Max plain data size for a pipeline.
79    pub const PLAIN_DATA_SIZE: u32 = 256;
80    /// Max number of resources in a bind group.
81    pub const RESOURCES_IN_GROUP: u32 = 8;
82    /// Min storage buffer alignment.
83    pub const STORAGE_BUFFER_ALIGNMENT: u64 = 16;
84    /// Min acceleration structure scratch buffer alignment.
85    pub const ACCELERATION_STRUCTURE_SCRATCH_ALIGNMENT: u64 = 256;
86}
87
88pub use hal::*;
89
90#[cfg(target_arch = "wasm32")]
91pub const CANVAS_ID: &str = "blade";
92
93use std::{fmt, num::NonZeroU32};
94
95/// Error from the underlying graphics platform during initialization.
96#[derive(Debug)]
97pub struct PlatformError(String);
98
99impl PlatformError {
100    pub(crate) fn loading(err: impl fmt::Debug) -> Self {
101        Self(format!("failed to load: {:?}", err))
102    }
103    pub(crate) fn init(err: impl fmt::Debug) -> Self {
104        Self(format!("failed to initialize: {:?}", err))
105    }
106}
107
108impl fmt::Display for PlatformError {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        f.write_str(&self.0)
111    }
112}
113
114impl std::error::Error for PlatformError {}
115
116#[cfg(not(any(
117    vulkan,
118    windows,
119    target_os = "linux",
120    target_os = "android",
121    target_os = "freebsd"
122)))]
123pub mod openxr {
124    #[derive(Clone, Debug)]
125    pub enum Instance {}
126    #[derive(Clone, Debug)]
127    pub enum SystemId {}
128}
129
130#[derive(Clone)]
131pub struct XrDesc {
132    pub instance: openxr::Instance,
133    pub system_id: openxr::SystemId,
134}
135
136impl fmt::Debug for XrDesc {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        f.debug_struct("XrDesc")
139            .field("system_id", &self.system_id)
140            .finish()
141    }
142}
143
144#[derive(Clone, Debug, Default)]
145pub struct ContextDesc {
146    /// Ability to present contents to a window.
147    pub presentation: bool,
148    /// Enable VR/AR.
149    pub xr: Option<XrDesc>,
150    /// Enable ray tracing support (acceleration structures and ray queries).
151    pub ray_tracing: bool,
152    /// Enable validation of the GAPI, shaders,
153    /// and insert crash markers into command buffers.
154    pub validation: bool,
155    /// Enable GPU timing of all passes.
156    pub timing: bool,
157    /// Enable capture support with GAPI tools.
158    pub capture: bool,
159    /// Enable GAPI overlay.
160    pub overlay: bool,
161    /// Force selection of a specific Device ID.
162    pub device_id: Option<u32>,
163}
164
165#[derive(Debug)]
166pub enum NotSupportedError {
167    Platform(PlatformError),
168    NoSupportedDeviceFound,
169    PlatformNotSupported,
170}
171
172impl fmt::Display for NotSupportedError {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        match *self {
175            Self::Platform(ref e) => write!(f, "platform error: {}", e),
176            Self::NoSupportedDeviceFound => f.write_str("no supported device found"),
177            Self::PlatformNotSupported => f.write_str("platform not supported"),
178        }
179    }
180}
181
182impl std::error::Error for NotSupportedError {}
183
184impl From<PlatformError> for NotSupportedError {
185    fn from(error: PlatformError) -> Self {
186        Self::Platform(error)
187    }
188}
189
190/// Error indicating a GPU device failure.
191#[derive(Clone, Debug, PartialEq, Eq)]
192pub enum DeviceError {
193    /// The GPU device has been lost and can no longer be used.
194    DeviceLost,
195    /// The GPU ran out of memory.
196    OutOfMemory,
197}
198
199impl fmt::Display for DeviceError {
200    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201        match *self {
202            Self::DeviceLost => f.write_str("device lost"),
203            Self::OutOfMemory => f.write_str("out of memory"),
204        }
205    }
206}
207
208impl std::error::Error for DeviceError {}
209
210/// GPU memory usage statistics.
211#[derive(Clone, Copy, Debug, Default)]
212pub struct MemoryStats {
213    /// Total memory budget across all device-local heaps (bytes).
214    /// Zero if the backend doesn't support memory budget queries.
215    pub budget: u64,
216    /// Current memory usage across all device-local heaps (bytes).
217    /// Zero if the backend doesn't support memory budget queries.
218    pub usage: u64,
219}
220
221/// Cooperative matrix support information.
222///
223/// Each field is a tile size (8 or 16), or 0 if that configuration
224/// is not supported. Naga supports square tiles only (8×8 and 16×16).
225#[derive(Clone, Copy, Debug, Default, PartialEq)]
226pub struct CooperativeMatrix {
227    /// Tile size for all-f32 operations.
228    pub f32_tile: u32,
229    /// Tile size for f16-input, f32-accumulator operations.
230    pub f16_tile: u32,
231}
232
233impl CooperativeMatrix {
234    /// Returns true if any cooperative matrix configuration is supported.
235    pub fn is_supported(&self) -> bool {
236        self.f32_tile > 0 || self.f16_tile > 0
237    }
238}
239
240#[derive(Clone, Debug, Default, PartialEq)]
241pub struct Capabilities {
242    /// Support binding arrays of handles.
243    pub binding_array: bool,
244    /// Which shader stages support ray queries.
245    pub ray_query: ShaderVisibility,
246    /// Bit mask of supported MSAA sample counts.
247    pub sample_count_mask: u32,
248    /// Support for dual-source blending.
249    pub dual_source_blending: bool,
250    /// Support for 16-bit floating-point types in shaders.
251    pub shader_float16: bool,
252    /// Cooperative matrix support.
253    pub cooperative_matrix: CooperativeMatrix,
254}
255
256#[derive(Clone, Debug, Default)]
257pub struct DeviceInformation {
258    /// If this is something like llvmpipe, not a real GPU
259    pub is_software_emulated: bool,
260    /// The name of the GPU device
261    pub device_name: String,
262    /// The driver used to talk to the GPU
263    pub driver_name: String,
264    /// Further information about the driver
265    pub driver_info: String,
266}
267
268impl Context {
269    pub fn create_surface_configured<
270        I: raw_window_handle::HasWindowHandle + raw_window_handle::HasDisplayHandle,
271    >(
272        &self,
273        window: &I,
274        config: SurfaceConfig,
275    ) -> Result<Surface, NotSupportedError> {
276        let mut surface = self.create_surface(window)?;
277        self.reconfigure_surface(&mut surface, config);
278        Ok(surface)
279    }
280}
281
282#[derive(Clone, Copy, Debug, PartialEq)]
283pub enum Memory {
284    /// Device-local memory. Fast for GPU operations.
285    Device,
286    /// Shared CPU-GPU memory. Not so fast for GPU.
287    Shared,
288    /// Upload memory. Can only be transferred on GPU.
289    Upload,
290    /// External memory
291    External(ExternalMemorySource),
292}
293
294/// If the source contains None it will export it otherwise it will import the value in Some
295//TODO add D3D11, D3D12 and metal support
296#[derive(Clone, Copy, Debug, PartialEq, Hash)]
297pub enum ExternalMemorySource {
298    #[cfg(target_os = "windows")]
299    Win32(Option<isize>),
300
301    #[cfg(target_os = "windows")]
302    Win32KMT(Option<isize>),
303
304    #[cfg(not(target_os = "windows"))]
305    Fd(Option<i32>),
306
307    #[cfg(target_os = "linux")]
308    Dma(Option<i32>),
309
310    /// Is a pointer cast to usize, reason being it otherwise can't be used as sync and send, you should manage memory access to this yourself
311    HostAllocation(usize),
312}
313
314impl Memory {
315    pub fn is_host_visible(&self) -> bool {
316        match *self {
317            Self::Shared
318            | Self::Upload
319            | Self::External(ExternalMemorySource::HostAllocation(_)) => true,
320            Self::Device | Self::External(_) => false,
321        }
322    }
323}
324
325#[derive(Debug)]
326pub struct BufferDesc<'a> {
327    pub name: &'a str,
328    pub size: u64,
329    pub memory: Memory,
330}
331
332#[derive(Clone, Copy, Debug)]
333pub struct BufferPiece {
334    pub buffer: Buffer,
335    pub offset: u64,
336}
337
338impl From<Buffer> for BufferPiece {
339    fn from(buffer: Buffer) -> Self {
340        Self { buffer, offset: 0 }
341    }
342}
343
344impl BufferPiece {
345    pub fn data(&self) -> *mut u8 {
346        let base = self.buffer.data();
347        assert!(!base.is_null());
348        debug_assert!(
349            self.offset <= self.buffer.size(),
350            "BufferPiece offset {} exceeds buffer size {}",
351            self.offset,
352            self.buffer.size(),
353        );
354        unsafe { base.offset(self.offset as isize) }
355    }
356}
357
358impl Buffer {
359    pub fn at(self, offset: u64) -> BufferPiece {
360        BufferPiece {
361            buffer: self,
362            offset,
363        }
364    }
365}
366
367pub type ResourceIndex = u32;
368/// An array of resources to be used with shader bindings.
369/// The generic argument tells the maximum number of resources.
370pub struct ResourceArray<T, const N: ResourceIndex> {
371    data: Vec<T>,
372    free_list: Vec<ResourceIndex>,
373}
374impl<T, const N: ResourceIndex> ResourceArray<T, N> {
375    pub fn new() -> Self {
376        Self {
377            data: Vec::with_capacity(N as usize),
378            free_list: Vec::new(),
379        }
380    }
381    pub fn alloc(&mut self, value: T) -> ResourceIndex {
382        if let Some(index) = self.free_list.pop() {
383            self.data[index as usize] = value;
384            index
385        } else {
386            let index = self.data.len() as u32;
387            assert!(index < N);
388            self.data.push(value);
389            index
390        }
391    }
392    pub fn free(&mut self, index: ResourceIndex) {
393        self.free_list.push(index);
394    }
395    pub fn clear(&mut self) {
396        self.data.clear();
397        self.free_list.clear();
398    }
399}
400impl<T, const N: ResourceIndex> std::ops::Index<ResourceIndex> for ResourceArray<T, N> {
401    type Output = T;
402    fn index(&self, index: ResourceIndex) -> &T {
403        &self.data[index as usize]
404    }
405}
406impl<T, const N: ResourceIndex> std::ops::IndexMut<ResourceIndex> for ResourceArray<T, N> {
407    fn index_mut(&mut self, index: ResourceIndex) -> &mut T {
408        &mut self.data[index as usize]
409    }
410}
411pub type BufferArray<const N: ResourceIndex> = ResourceArray<BufferPiece, N>;
412pub type TextureArray<const N: ResourceIndex> = ResourceArray<TextureView, N>;
413pub type AccelerationStructureArray<const N: ResourceIndex> =
414    ResourceArray<AccelerationStructure, N>;
415
416#[derive(Clone, Copy, Debug)]
417pub struct TexturePiece {
418    pub texture: Texture,
419    pub mip_level: u32,
420    pub array_layer: u32,
421    pub origin: [u32; 3],
422}
423
424impl From<Texture> for TexturePiece {
425    fn from(texture: Texture) -> Self {
426        Self {
427            texture,
428            mip_level: 0,
429            array_layer: 0,
430            origin: [0; 3],
431        }
432    }
433}
434
435#[non_exhaustive]
436#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
437pub enum TextureFormat {
438    // color
439    R8Unorm,
440    Rg8Unorm,
441    Rg8Snorm,
442    Rgba8Unorm,
443    Rgba8UnormSrgb,
444    Bgra8Unorm,
445    Bgra8UnormSrgb,
446    Rgba8Snorm,
447    R16Float,
448    Rg16Float,
449    Rgba16Float,
450    R32Float,
451    Rg32Float,
452    Rgba32Float,
453    R32Uint,
454    Rg32Uint,
455    Rgba32Uint,
456    // depth and stencil
457    Depth32Float,
458    Depth32FloatStencil8Uint,
459    Stencil8Uint,
460    // S3TC block compression
461    Bc1Unorm,
462    Bc1UnormSrgb,
463    Bc2Unorm,
464    Bc2UnormSrgb,
465    Bc3Unorm,
466    Bc3UnormSrgb,
467    Bc4Unorm,
468    Bc4Snorm,
469    Bc5Unorm,
470    Bc5Snorm,
471    Bc6hUfloat,
472    Bc6hFloat,
473    Bc7Unorm,
474    Bc7UnormSrgb,
475    // packed 32-bit
476    Rgb10a2Unorm,
477    Rg11b10Ufloat,
478    Rgb9e5Ufloat,
479}
480
481#[derive(Clone, Copy, Debug)]
482pub struct TexelBlockInfo {
483    pub dimensions: (u8, u8),
484    pub size: u8,
485}
486
487bitflags::bitflags! {
488    #[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
489    pub struct TexelAspects: u8 {
490        const COLOR = 0x1;
491        const DEPTH = 0x2;
492        const STENCIL = 0x4;
493    }
494}
495
496/// Dimensionality of a texture.
497#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
498pub enum TextureDimension {
499    /// 1D texture
500    D1,
501    /// 2D texture
502    D2,
503    /// 3D texture
504    D3,
505}
506
507#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
508pub enum ViewDimension {
509    D1,
510    D1Array,
511    D2,
512    D2Array,
513    Cube,
514    CubeArray,
515    D3,
516}
517
518#[repr(C)]
519#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
520pub struct Extent {
521    pub width: u32,
522    pub height: u32,
523    pub depth: u32,
524}
525impl Default for Extent {
526    fn default() -> Self {
527        Self {
528            width: 1,
529            height: 1,
530            depth: 1,
531        }
532    }
533}
534impl fmt::Display for Extent {
535    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
536        write!(f, "{}x{}x{}", self.width, self.height, self.depth)
537    }
538}
539
540impl Extent {
541    pub fn max_mip_levels(&self) -> u32 {
542        self.width
543            .max(self.height)
544            .max(self.depth)
545            .next_power_of_two()
546            .trailing_zeros()
547    }
548    pub fn at_mip_level(&self, level: u32) -> Self {
549        Self {
550            width: (self.width >> level).max(1),
551            height: (self.height >> level).max(1),
552            depth: (self.depth >> level).max(1),
553        }
554    }
555}
556
557bitflags::bitflags! {
558    #[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
559    pub struct TextureUsage: u32 {
560        const COPY = 1 << 0;
561        const TARGET = 1 << 1;
562        const RESOURCE = 1 << 2;
563        const STORAGE = 1 << 3;
564    }
565}
566
567#[derive(Debug)]
568pub struct TextureDesc<'a> {
569    pub name: &'a str,
570    pub format: TextureFormat,
571    pub size: Extent,
572    pub array_layer_count: u32,
573    pub mip_level_count: u32,
574    pub sample_count: u32,
575    pub dimension: TextureDimension,
576    pub usage: TextureUsage,
577    pub external: Option<ExternalMemorySource>,
578}
579
580#[derive(Clone, Debug, Default, Eq, PartialEq)]
581pub struct TextureSubresources {
582    pub base_mip_level: u32,
583    pub mip_level_count: Option<NonZeroU32>,
584    pub base_array_layer: u32,
585    pub array_layer_count: Option<NonZeroU32>,
586}
587
588#[derive(Debug)]
589pub struct TextureViewDesc<'a> {
590    pub name: &'a str,
591    pub format: TextureFormat,
592    pub dimension: ViewDimension,
593    pub subresources: &'a TextureSubresources,
594}
595
596bitflags::bitflags! {
597    #[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
598    pub struct ShaderVisibility: u32 {
599        const COMPUTE = 1 << 0;
600        const VERTEX = 1 << 1;
601        const FRAGMENT = 1 << 2;
602    }
603}
604
605/// How edges should be handled in texture addressing.
606#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
607pub enum AddressMode {
608    /// Clamp the value to the edge of the texture.
609    #[default]
610    ClampToEdge,
611    /// Repeat the texture in a tiling fashion.
612    Repeat,
613    /// Repeat the texture, mirroring it every repeat.
614    MirrorRepeat,
615    /// Clamp the value to the border of the texture.
616    ClampToBorder,
617}
618
619/// Texel mixing mode when sampling between texels.
620#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
621pub enum FilterMode {
622    /// Nearest neighbor sampling.
623    #[default]
624    Nearest,
625    /// Linear Interpolation
626    Linear,
627}
628
629/// Comparison function used for depth and stencil operations.
630#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
631pub enum CompareFunction {
632    /// Function never passes
633    Never,
634    /// Function passes if new value less than existing value
635    Less,
636    /// Function passes if new value is equal to existing value. When using
637    /// this compare function, make sure to mark your Vertex Shader's `@builtin(position)`
638    /// output as `@invariant` to prevent artifacting.
639    Equal,
640    /// Function passes if new value is less than or equal to existing value
641    LessEqual,
642    /// Function passes if new value is greater than existing value
643    Greater,
644    /// Function passes if new value is not equal to existing value. When using
645    /// this compare function, make sure to mark your Vertex Shader's `@builtin(position)`
646    /// output as `@invariant` to prevent artifacting.
647    NotEqual,
648    /// Function passes if new value is greater than or equal to existing value
649    GreaterEqual,
650    /// Function always passes
651    #[default]
652    Always,
653}
654
655#[derive(Clone, Copy, Debug, PartialEq)]
656pub enum TextureColor {
657    TransparentBlack,
658    OpaqueBlack,
659    White,
660}
661
662#[derive(Debug, Default)]
663pub struct SamplerDesc<'a> {
664    pub name: &'a str,
665    pub address_modes: [AddressMode; 3],
666    pub mag_filter: FilterMode,
667    pub min_filter: FilterMode,
668    pub mipmap_filter: FilterMode,
669    pub lod_min_clamp: f32,
670    pub lod_max_clamp: Option<f32>,
671    pub compare: Option<CompareFunction>,
672    pub anisotropy_clamp: u32,
673    pub border_color: Option<TextureColor>,
674}
675
676#[derive(Debug)]
677pub enum AccelerationStructureType {
678    TopLevel,
679    BottomLevel,
680}
681
682#[derive(Debug)]
683pub struct AccelerationStructureDesc<'a> {
684    pub name: &'a str,
685    pub ty: AccelerationStructureType,
686    pub size: u64,
687}
688
689#[non_exhaustive]
690#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
691pub enum VertexFormat {
692    F32,
693    F32Vec2,
694    F32Vec3,
695    F32Vec4,
696    U32,
697    U32Vec2,
698    U32Vec3,
699    U32Vec4,
700    I32,
701    I32Vec2,
702    I32Vec3,
703    I32Vec4,
704}
705
706#[derive(Clone, Debug)]
707pub struct AccelerationStructureMesh {
708    pub vertex_data: BufferPiece,
709    pub vertex_format: VertexFormat,
710    pub vertex_stride: u32,
711    pub vertex_count: u32,
712    pub index_data: BufferPiece,
713    pub index_type: Option<IndexType>,
714    pub triangle_count: u32,
715    pub transform_data: BufferPiece,
716    pub is_opaque: bool,
717}
718
719#[derive(Clone, Debug)]
720pub struct AccelerationStructureInstance {
721    pub acceleration_structure_index: u32,
722    pub transform: Transform,
723    pub mask: u32,
724    pub custom_index: u32,
725}
726
727impl Default for AccelerationStructureInstance {
728    fn default() -> Self {
729        Self {
730            acceleration_structure_index: 0,
731            transform: IDENTITY_TRANSFORM,
732            mask: 0xFF,
733            custom_index: 0,
734        }
735    }
736}
737
738#[derive(Clone, Copy, Debug, Default, PartialEq)]
739pub struct AccelerationStructureSizes {
740    /// Size of the permanent GPU data
741    pub data: u64,
742    /// Size of the scratch space
743    pub scratch: u64,
744}
745
746pub struct Shader {
747    module: naga::Module,
748    info: naga::valid::ModuleInfo,
749    source: String,
750}
751
752#[derive(Clone, Copy)]
753pub struct ShaderFunction<'a> {
754    pub shader: &'a Shader,
755    pub entry_point: &'a str,
756    pub constants: &'a PipelineConstants,
757}
758
759impl ShaderFunction<'_> {
760    fn entry_point_index(&self) -> usize {
761        self.shader
762            .module
763            .entry_points
764            .iter()
765            .position(|ep| ep.name == self.entry_point)
766            .expect("Entry point not found in the shader")
767    }
768}
769
770#[derive(Clone, Copy, Debug, PartialEq)]
771pub enum ShaderBinding {
772    Texture,
773    TextureArray { count: u32 },
774    Sampler,
775    Buffer,
776    BufferArray { count: u32 },
777    AccelerationStructure,
778    AccelerationStructureArray { count: u32 },
779    Plain { size: u32 },
780}
781
782pub trait ShaderBindable: Clone + Copy + derive::HasShaderBinding {
783    fn bind_to(&self, context: &mut PipelineContext, index: u32);
784}
785
786#[derive(Debug)]
787struct ShaderDataInfo {
788    visibility: ShaderVisibility,
789    binding_access: Box<[StorageAccess]>,
790}
791
792#[derive(Clone, Debug, Default, PartialEq)]
793pub struct ShaderDataLayout {
794    pub bindings: Vec<(&'static str, ShaderBinding)>,
795}
796impl ShaderDataLayout {
797    pub const EMPTY: &'static Self = &Self {
798        bindings: Vec::new(),
799    };
800
801    fn to_info(&self) -> ShaderDataInfo {
802        ShaderDataInfo {
803            visibility: ShaderVisibility::empty(),
804            binding_access: vec![StorageAccess::empty(); self.bindings.len()].into_boxed_slice(),
805        }
806    }
807}
808
809pub trait ShaderData {
810    fn layout() -> ShaderDataLayout;
811    fn fill(&self, context: PipelineContext);
812}
813
814#[derive(Copy, Clone, Debug, PartialEq)]
815pub struct VertexAttribute {
816    pub offset: u32,
817    pub format: VertexFormat,
818}
819
820struct VertexAttributeMapping {
821    buffer_index: usize,
822    attribute_index: usize,
823}
824
825#[derive(Clone, Debug, Default, PartialEq)]
826pub struct VertexLayout {
827    pub attributes: Vec<(&'static str, VertexAttribute)>,
828    pub stride: u32,
829}
830
831pub trait Vertex {
832    fn layout() -> VertexLayout;
833}
834
835#[derive(Clone, Debug, PartialEq)]
836pub struct VertexFetchState<'a> {
837    pub layout: &'a VertexLayout,
838    pub instanced: bool,
839}
840
841pub struct ShaderDesc<'a> {
842    /// WGSL source code. Used for parsing when `naga_module` is `None`,
843    /// or as debug info (SPIR-V source maps, shader dumps) when a module is provided.
844    pub source: &'a str,
845    /// Optional pre-built Naga IR module. When provided, `source` is skipped
846    /// for parsing and only used as debug info (if non-empty).
847    pub naga_module: Option<naga::Module>,
848}
849
850#[derive(Clone, Debug, Default, PartialEq)]
851pub enum CommandType {
852    Transfer,
853    Compute,
854    #[default]
855    General,
856}
857
858pub struct CommandEncoderDesc<'a> {
859    pub name: &'a str,
860    /// Number of buffers that this encoder needs to keep alive.
861    /// For example, one buffer is being run on GPU while the
862    /// other is being actively encoded, which makes 2.
863    pub buffer_count: u32,
864}
865
866pub struct ComputePipelineDesc<'a> {
867    pub name: &'a str,
868    pub data_layouts: &'a [&'a ShaderDataLayout],
869    pub compute: ShaderFunction<'a>,
870}
871
872/// Primitive type the input mesh is composed of.
873#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
874pub enum PrimitiveTopology {
875    /// Vertex data is a list of points. Each vertex is a new point.
876    PointList,
877    /// Vertex data is a list of lines. Each pair of vertices composes a new line.
878    ///
879    /// Vertices `0 1 2 3` create two lines `0 1` and `2 3`
880    LineList,
881    /// Vertex data is a strip of lines. Each set of two adjacent vertices form a line.
882    ///
883    /// Vertices `0 1 2 3` create three lines `0 1`, `1 2`, and `2 3`.
884    LineStrip,
885    /// Vertex data is a list of triangles. Each set of 3 vertices composes a new triangle.
886    ///
887    /// Vertices `0 1 2 3 4 5` create two triangles `0 1 2` and `3 4 5`
888    #[default]
889    TriangleList,
890    /// Vertex data is a triangle strip. Each set of three adjacent vertices form a triangle.
891    ///
892    /// Vertices `0 1 2 3 4 5` creates four triangles `0 1 2`, `2 1 3`, `2 3 4`, and `4 3 5`
893    TriangleStrip,
894}
895
896/// Vertex winding order which classifies the "front" face of a triangle.
897#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
898pub enum FrontFace {
899    /// Triangles with vertices in counter clockwise order are considered the front face.
900    ///
901    /// This is the default with right handed coordinate spaces.
902    #[default]
903    Ccw,
904    /// Triangles with vertices in clockwise order are considered the front face.
905    ///
906    /// This is the default with left handed coordinate spaces.
907    Cw,
908}
909
910/// Face of a vertex.
911#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
912pub enum Face {
913    /// Front face
914    Front,
915    /// Back face
916    Back,
917}
918
919#[derive(Clone, Debug, Default)]
920pub struct PrimitiveState {
921    /// The primitive topology used to interpret vertices.
922    pub topology: PrimitiveTopology,
923    /// The face to consider the front for the purpose of culling and stencil operations.
924    pub front_face: FrontFace,
925    /// The face culling mode.
926    pub cull_mode: Option<Face>,
927    /// If set to true, the polygon depth is not clipped to 0-1 before rasterization.
928    pub unclipped_depth: bool,
929    /// If true, only the primitive edges are rasterized..
930    pub wireframe: bool,
931}
932
933/// Operation to perform on the stencil value.
934#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
935pub enum StencilOperation {
936    /// Keep stencil value unchanged.
937    #[default]
938    Keep,
939    /// Set stencil value to zero.
940    Zero,
941    /// Replace stencil value with value provided.
942    Replace,
943    /// Bitwise inverts stencil value.
944    Invert,
945    /// Increments stencil value by one, clamping on overflow.
946    IncrementClamp,
947    /// Decrements stencil value by one, clamping on underflow.
948    DecrementClamp,
949    /// Increments stencil value by one, wrapping on overflow.
950    IncrementWrap,
951    /// Decrements stencil value by one, wrapping on underflow.
952    DecrementWrap,
953}
954
955/// Describes stencil state in a render pipeline.
956///
957/// If you are not using stencil state, set this to [`StencilFaceState::IGNORE`].
958#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
959pub struct StencilFaceState {
960    /// Comparison function that determines if the fail_op or pass_op is used on the stencil buffer.
961    pub compare: CompareFunction,
962    /// Operation that is preformed when stencil test fails.
963    pub fail_op: StencilOperation,
964    /// Operation that is performed when depth test fails but stencil test succeeds.
965    pub depth_fail_op: StencilOperation,
966    /// Operation that is performed when stencil test success.
967    pub pass_op: StencilOperation,
968}
969
970impl StencilFaceState {
971    /// Ignore the stencil state for the face.
972    pub const IGNORE: Self = StencilFaceState {
973        compare: CompareFunction::Always,
974        fail_op: StencilOperation::Keep,
975        depth_fail_op: StencilOperation::Keep,
976        pass_op: StencilOperation::Keep,
977    };
978}
979
980impl Default for StencilFaceState {
981    fn default() -> Self {
982        Self::IGNORE
983    }
984}
985
986/// State of the stencil operation (fixed-pipeline stage).
987///
988/// For use in [`DepthStencilState`].
989#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
990pub struct StencilState {
991    /// Front face mode.
992    pub front: StencilFaceState,
993    /// Back face mode.
994    pub back: StencilFaceState,
995    /// Stencil values are AND'd with this mask when reading and writing from the stencil buffer. Only low 8 bits are used.
996    pub read_mask: u32,
997    /// Stencil values are AND'd with this mask when writing to the stencil buffer. Only low 8 bits are used.
998    pub write_mask: u32,
999}
1000
1001#[derive(Clone, Copy, Debug, Default, PartialEq)]
1002pub struct DepthBiasState {
1003    /// Constant depth biasing factor, in basic units of the depth format.
1004    pub constant: i32,
1005    /// Slope depth biasing factor.
1006    pub slope_scale: f32,
1007    /// Depth bias clamp value (absolute).
1008    pub clamp: f32,
1009}
1010
1011/// Describes the depth/stencil state in a render pipeline.
1012#[derive(Clone, Debug)]
1013pub struct DepthStencilState {
1014    /// Format of the depth/stencil texture view.
1015    pub format: TextureFormat,
1016    /// If disabled, depth will not be written to.
1017    pub depth_write_enabled: bool,
1018    /// Comparison function used to compare depth values in the depth test.
1019    pub depth_compare: CompareFunction,
1020    /// Stencil state.
1021    pub stencil: StencilState,
1022    /// Depth bias state.
1023    pub bias: DepthBiasState,
1024}
1025
1026/// Alpha blend factor.
1027#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
1028pub enum BlendFactor {
1029    /// 0.0
1030    Zero,
1031    /// 1.0
1032    One,
1033    /// S.component
1034    Src,
1035    /// 1.0 - S.component
1036    OneMinusSrc,
1037    /// S.alpha
1038    SrcAlpha,
1039    /// 1.0 - S.alpha
1040    OneMinusSrcAlpha,
1041    /// D.component
1042    Dst,
1043    /// 1.0 - D.component
1044    OneMinusDst,
1045    /// D.alpha
1046    DstAlpha,
1047    /// 1.0 - D.alpha
1048    OneMinusDstAlpha,
1049    /// min(S.alpha, 1.0 - D.alpha)
1050    SrcAlphaSaturated,
1051    /// Constant
1052    Constant,
1053    /// 1.0 - Constant
1054    OneMinusConstant,
1055    /// S1.component
1056    Src1,
1057    /// 1.0 - S1.component
1058    OneMinusSrc1,
1059    /// S1.alpha
1060    Src1Alpha,
1061    /// 1.0 - S1.alpha
1062    OneMinusSrc1Alpha,
1063}
1064
1065impl BlendFactor {
1066    pub const fn uses_dual_source(&self) -> bool {
1067        matches!(
1068            self,
1069            BlendFactor::Src1
1070                | BlendFactor::OneMinusSrc1
1071                | BlendFactor::Src1Alpha
1072                | BlendFactor::OneMinusSrc1Alpha
1073        )
1074    }
1075}
1076
1077/// Alpha blend operation.
1078#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
1079pub enum BlendOperation {
1080    /// Src + Dst
1081    #[default]
1082    Add,
1083    /// Src - Dst
1084    Subtract,
1085    /// Dst - Src
1086    ReverseSubtract,
1087    /// min(Src, Dst)
1088    Min,
1089    /// max(Src, Dst)
1090    Max,
1091}
1092
1093/// Describes a blend component of a [`BlendState`].
1094#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1095pub struct BlendComponent {
1096    /// Multiplier for the source, which is produced by the fragment shader.
1097    pub src_factor: BlendFactor,
1098    /// Multiplier for the destination, which is stored in the target.
1099    pub dst_factor: BlendFactor,
1100    /// The binary operation applied to the source and destination,
1101    /// multiplied by their respective factors.
1102    pub operation: BlendOperation,
1103}
1104
1105impl BlendComponent {
1106    /// Default blending state that replaces destination with the source.
1107    pub const REPLACE: Self = Self {
1108        src_factor: BlendFactor::One,
1109        dst_factor: BlendFactor::Zero,
1110        operation: BlendOperation::Add,
1111    };
1112
1113    /// Blend state of (1 * src) + ((1 - src_alpha) * dst)
1114    pub const OVER: Self = Self {
1115        src_factor: BlendFactor::One,
1116        dst_factor: BlendFactor::OneMinusSrcAlpha,
1117        operation: BlendOperation::Add,
1118    };
1119
1120    /// Blend state of src + dst
1121    pub const ADDITIVE: Self = Self {
1122        src_factor: BlendFactor::One,
1123        dst_factor: BlendFactor::One,
1124        operation: BlendOperation::Add,
1125    };
1126
1127    pub const fn uses_dual_source(&self) -> bool {
1128        self.src_factor.uses_dual_source() || self.dst_factor.uses_dual_source()
1129    }
1130}
1131
1132impl Default for BlendComponent {
1133    fn default() -> Self {
1134        Self::REPLACE
1135    }
1136}
1137
1138/// Describe the blend state of a render pipeline,
1139/// within [`ColorTargetState`].
1140#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1141pub struct BlendState {
1142    /// Color equation.
1143    pub color: BlendComponent,
1144    /// Alpha equation.
1145    pub alpha: BlendComponent,
1146}
1147
1148impl BlendState {
1149    /// Blend mode that does no color blending, just overwrites the output with the contents of the shader.
1150    pub const REPLACE: Self = Self {
1151        color: BlendComponent::REPLACE,
1152        alpha: BlendComponent::REPLACE,
1153    };
1154
1155    /// Blend mode that does standard alpha blending with non-premultiplied alpha.
1156    pub const ALPHA_BLENDING: Self = Self {
1157        color: BlendComponent {
1158            src_factor: BlendFactor::SrcAlpha,
1159            dst_factor: BlendFactor::OneMinusSrcAlpha,
1160            operation: BlendOperation::Add,
1161        },
1162        alpha: BlendComponent::OVER,
1163    };
1164
1165    /// Blend mode that does standard alpha blending with premultiplied alpha.
1166    pub const PREMULTIPLIED_ALPHA_BLENDING: Self = Self {
1167        color: BlendComponent::OVER,
1168        alpha: BlendComponent::OVER,
1169    };
1170
1171    /// Blend mode that just adds the value.
1172    pub const ADDITIVE: Self = Self {
1173        color: BlendComponent::ADDITIVE,
1174        alpha: BlendComponent::ADDITIVE,
1175    };
1176
1177    pub const fn uses_dual_source(&self) -> bool {
1178        self.color.uses_dual_source() || self.alpha.uses_dual_source()
1179    }
1180}
1181
1182bitflags::bitflags! {
1183    /// Color write mask. Disabled color channels will not be written to.
1184    #[repr(transparent)]
1185    #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
1186    pub struct ColorWrites: u32 {
1187        /// Enable red channel writes
1188        const RED = 1 << 0;
1189        /// Enable green channel writes
1190        const GREEN = 1 << 1;
1191        /// Enable blue channel writes
1192        const BLUE = 1 << 2;
1193        /// Enable alpha channel writes
1194        const ALPHA = 1 << 3;
1195        /// Enable red, green, and blue channel writes
1196        const COLOR = Self::RED.bits() | Self::GREEN.bits() | Self::BLUE.bits();
1197        /// Enable writes to all channels.
1198        const ALL = Self::RED.bits() | Self::GREEN.bits() | Self::BLUE.bits() | Self::ALPHA.bits();
1199    }
1200}
1201
1202impl Default for ColorWrites {
1203    fn default() -> Self {
1204        Self::ALL
1205    }
1206}
1207
1208/// Describes the color state of a render pipeline.
1209#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1210pub struct ColorTargetState {
1211    /// The [`TextureFormat`] of the image that this pipeline will render to.
1212    pub format: TextureFormat,
1213    /// The blending that is used for this pipeline.
1214    pub blend: Option<BlendState>,
1215    /// Mask which enables/disables writes to different color/alpha channel.
1216    pub write_mask: ColorWrites,
1217}
1218
1219impl From<TextureFormat> for ColorTargetState {
1220    fn from(format: TextureFormat) -> Self {
1221        Self {
1222            format,
1223            blend: None,
1224            write_mask: ColorWrites::ALL,
1225        }
1226    }
1227}
1228
1229pub struct RenderPipelineDesc<'a> {
1230    pub name: &'a str,
1231    pub data_layouts: &'a [&'a ShaderDataLayout],
1232    pub vertex: ShaderFunction<'a>,
1233    pub vertex_fetches: &'a [VertexFetchState<'a>],
1234    pub primitive: PrimitiveState,
1235    pub depth_stencil: Option<DepthStencilState>,
1236    pub fragment: Option<ShaderFunction<'a>>,
1237    pub color_targets: &'a [ColorTargetState],
1238    pub multisample_state: MultisampleState,
1239}
1240
1241#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1242pub struct MultisampleState {
1243    pub sample_count: u32,
1244    pub sample_mask: u64,
1245    pub alpha_to_coverage: bool,
1246}
1247
1248impl Default for MultisampleState {
1249    fn default() -> Self {
1250        Self {
1251            sample_count: 1,
1252            sample_mask: !0,
1253            alpha_to_coverage: false,
1254        }
1255    }
1256}
1257
1258#[derive(Clone, Copy, Debug)]
1259pub enum InitOp {
1260    Load,
1261    Clear(TextureColor),
1262    DontCare,
1263}
1264
1265#[derive(Clone, Copy, Debug)]
1266pub enum FinishOp {
1267    Store,
1268    Discard,
1269    /// The texture specified here will be stored but it is undefined what
1270    /// happens to the original render target
1271    ResolveTo(TextureView),
1272    Ignore,
1273}
1274
1275#[derive(Debug)]
1276pub struct RenderTarget {
1277    pub view: TextureView,
1278    pub init_op: InitOp,
1279    pub finish_op: FinishOp,
1280}
1281
1282#[derive(Debug)]
1283pub struct RenderTargetSet<'a> {
1284    pub colors: &'a [RenderTarget],
1285    pub depth_stencil: Option<RenderTarget>,
1286}
1287
1288/// Mechanism used to acquire frames and display them on screen.
1289#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
1290pub enum DisplaySync {
1291    /// Block until the oldest frame is released.
1292    #[default]
1293    Block,
1294    /// Display the most recently presented frame.
1295    /// Falls back to `Tear` if unsupported.
1296    Recent,
1297    /// Tear the currently displayed frame when presenting a new one.
1298    Tear,
1299}
1300
1301#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
1302pub enum ColorSpace {
1303    #[default]
1304    Linear,
1305    Srgb,
1306}
1307
1308#[derive(Clone, Copy, Debug, Default, PartialEq)]
1309pub struct SurfaceConfig {
1310    pub size: Extent,
1311    pub usage: TextureUsage,
1312    pub display_sync: DisplaySync,
1313    /// The color space that render output colors are expected to be in.
1314    ///
1315    /// This will affect the surface format returned by the `Context`.
1316    ///
1317    /// For example, if the display expects sRGB space and we render
1318    /// in `ColorSpace::Linear` space, the returned format will be sRGB.
1319    pub color_space: ColorSpace,
1320    pub transparent: bool,
1321    pub allow_exclusive_full_screen: bool,
1322}
1323
1324#[derive(Clone, Copy, Debug, PartialEq)]
1325pub struct XrSurfaceConfig {
1326    pub size: Extent,
1327    pub usage: TextureUsage,
1328    pub color_space: ColorSpace,
1329    pub view_count: u32,
1330}
1331
1332impl Default for XrSurfaceConfig {
1333    fn default() -> Self {
1334        Self {
1335            size: Extent::default(),
1336            usage: TextureUsage::TARGET,
1337            color_space: ColorSpace::Linear,
1338            view_count: 2,
1339        }
1340    }
1341}
1342
1343#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
1344pub enum AlphaMode {
1345    #[default]
1346    Ignored,
1347    PreMultiplied,
1348    PostMultiplied,
1349}
1350
1351#[derive(Clone, Copy, Debug, PartialEq)]
1352pub struct SurfaceInfo {
1353    pub format: TextureFormat,
1354    pub alpha: AlphaMode,
1355}
1356
1357#[derive(Clone, Copy, Debug, PartialEq)]
1358pub enum IndexType {
1359    U16,
1360    U32,
1361}
1362
1363#[derive(Clone, Debug, PartialEq)]
1364pub struct ScissorRect {
1365    pub x: i32,
1366    pub y: i32,
1367    pub w: u32,
1368    pub h: u32,
1369}
1370
1371#[derive(Clone, Debug, PartialEq)]
1372pub struct Viewport {
1373    pub x: f32,
1374    pub y: f32,
1375    pub w: f32,
1376    pub h: f32,
1377    pub depth: std::ops::Range<f32>,
1378}
1379
1380pub type Timings = Vec<(String, std::time::Duration)>;