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