sierra/
lib.rs

1//! Sierra is Vulkan-lite API, focused on ease of use
2//! while maintaining high level of control.
3//!
4//! While resembles Vulkan in most ways,\
5//! sierra does both memory and descriptor allocation automatically.
6//! Additionally sierra tracks resources usage to free them once no references left.
7//!
8//! Sierra provides rich proc-macro system for declarative descriptor sets and render passes.
9
10// Someday this will be uncommented.
11// #![warn(missing_docs)]
12
13#![warn(missing_debug_implementations)]
14#![warn(missing_copy_implementations)]
15
16use std::{
17    cmp::{Ord, Ordering},
18    convert::TryFrom,
19    error::Error,
20    fmt::Debug,
21};
22
23#[cfg(feature = "tracing")]
24#[macro_export]
25macro_rules! trace {
26    ($($tokens:tt)*) => {
27        tracing::trace!($($tokens)*)
28    };
29}
30
31#[cfg(feature = "tracing")]
32#[macro_export]
33macro_rules! debug {
34    ($($tokens:tt)*) => {
35        tracing::debug!($($tokens)*)
36    };
37}
38
39#[cfg(feature = "tracing")]
40#[macro_export]
41macro_rules! info {
42    ($($tokens:tt)*) => {
43        tracing::info!($($tokens)*)
44    };
45}
46
47#[cfg(feature = "tracing")]
48#[macro_export]
49macro_rules! warn {
50    ($($tokens:tt)*) => {
51        tracing::warn!($($tokens)*)
52    };
53}
54
55#[cfg(feature = "tracing")]
56#[macro_export]
57macro_rules! error {
58    ($($tokens:tt)*) => {
59        tracing::error!($($tokens)*)
60    };
61}
62
63#[cfg(not(feature = "tracing"))]
64#[macro_export]
65macro_rules! trace {
66    ($($e:expr),*) => {{ $(let _ = &$e;)* }};
67}
68
69#[cfg(not(feature = "tracing"))]
70#[macro_export]
71macro_rules! debug {
72    ($($e:expr),*) => {{ $( let _ = &$e;)* }};
73}
74
75#[cfg(not(feature = "tracing"))]
76#[macro_export]
77macro_rules! info {
78    ($($e:expr),*) => {{ $(let _ = &$e;)* }};
79}
80
81#[cfg(not(feature = "tracing"))]
82#[macro_export]
83macro_rules! warn {
84    ($($e:expr),*) => {{ $(let _ = &$e;)* }};
85}
86
87#[cfg(not(feature = "tracing"))]
88#[macro_export]
89macro_rules! error {
90    ($($e:expr),*) => {{ $(let _ = &$e;)* }};
91}
92
93pub mod backend;
94
95mod accel;
96mod access;
97mod buffer;
98mod cache;
99mod descriptor;
100mod dimensions;
101mod encode;
102mod fence;
103mod format;
104mod framebuffer;
105mod image;
106mod memory;
107mod physical;
108mod pipeline;
109mod queue;
110mod render_pass;
111mod repr;
112mod sampler;
113mod semaphore;
114mod shader;
115mod stage;
116mod surface;
117mod view;
118
119pub use self::{
120    accel::*,
121    access::*,
122    backend::{Device, Graphics},
123    buffer::*,
124    cache::*,
125    descriptor::*,
126    dimensions::*,
127    encode::*,
128    fence::*,
129    format::*,
130    framebuffer::*,
131    image::*,
132    memory::*,
133    physical::*,
134    pipeline::*,
135    queue::*,
136    render_pass::*,
137    repr::*,
138    sampler::*,
139    semaphore::*,
140    shader::*,
141    stage::*,
142    surface::*,
143    view::*,
144};
145
146pub use sierra_proc::{
147    binding_flags, format, graphics_pipeline_desc, shader_stages, swizzle, Descriptors, Pass,
148    PipelineInput, ShaderRepr,
149};
150
151/// Re-exporting for code-gen.
152#[doc(hidden)]
153pub use {arrayvec, bytemuck, scoped_arena, smallvec};
154
155/// Error that may occur when allocation fails because of either
156/// device memory is exhausted.
157///
158/// Deallocation of device memory or other resources may increase chance
159/// that operation would succeed.
160#[derive(Clone, Copy, Debug, thiserror::Error, PartialEq, Eq)]
161#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
162#[error("Out of device memory")]
163pub struct OutOfMemory;
164
165/// Error that may occur during execution on the device
166/// and then signalled on command submission or waiting operations.
167///
168/// This error is unrecoverable lost `Device` state cannot be changed to not-lost.
169/// It must be recreated.
170///
171/// Any mapped memory allocated from lost device is still valid for access, but its content is undefined.
172///
173/// If this error is returned by `PhysicalDevice::create_device` function
174/// then physical device is lost and cannot be used.
175/// This may indicate that device was physically disconnected or developed a fault.
176#[derive(Clone, Copy, Debug, thiserror::Error, PartialEq, Eq)]
177#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
178#[error("Device lost")]
179pub struct DeviceLost;
180
181/// Device address is `u64` value pointing into device resource.
182#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
183#[repr(transparent)]
184pub struct DeviceAddress(pub std::num::NonZeroU64);
185
186impl DeviceAddress {
187    pub fn offset(&mut self, offset: u64) -> DeviceAddress {
188        let value = self.0.get().checked_add(offset).unwrap();
189
190        DeviceAddress(unsafe { std::num::NonZeroU64::new_unchecked(value) })
191    }
192}
193
194#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
195#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
196pub enum IndexType {
197    U16,
198    U32,
199}
200
201impl IndexType {
202    pub fn size(&self) -> u8 {
203        match self {
204            IndexType::U16 => 2,
205            IndexType::U32 => 4,
206        }
207    }
208}
209
210#[derive(Debug, thiserror::Error)]
211pub enum CreateDeviceError<E: Error + 'static> {
212    #[error(transparent)]
213    OutOfMemory {
214        #[from]
215        source: OutOfMemory,
216    },
217
218    #[error("Non-existed families are requested")]
219    BadFamiliesRequested,
220
221    #[error(transparent)]
222    CannotFindRequeredQueues { source: E },
223
224    /// Implementation specific error.
225    #[error("Failed to load functions")]
226    FunctionLoadFailed,
227}
228
229/// Possible error which can be returned from `create_buffer_*`.
230#[derive(Clone, Copy, Debug, thiserror::Error, PartialEq, Eq)]
231pub enum CreateBufferError {
232    #[error(transparent)]
233    OutOfMemory {
234        #[from]
235        source: OutOfMemory,
236    },
237
238    #[error("Buffer usage {usage:?} is unsupported")]
239    UnsupportedUsage { usage: BufferUsage },
240}
241
242/// Possible error that may occur during memory mapping.
243#[derive(Clone, Copy, Debug, thiserror::Error)]
244pub enum MapError {
245    /// Device memory is exhausted.
246    #[error(transparent)]
247    OutOfMemory {
248        #[from]
249        source: OutOfMemory,
250    },
251
252    /// Memory is not host-visible.
253    #[error("Memory is not host-visible")]
254    NonHostVisible,
255
256    /// Memory is already mapped
257    #[error("Memory is already mapped")]
258    AlreadyMapped,
259
260    /// Map failed for implementation specific reason
261    #[error("Map failed for implementation specific reason")]
262    MapFailed,
263}
264
265#[doc(hidden)]
266pub trait OrdArith<T>: Copy {
267    fn cmp(self, rhs: T) -> Ordering;
268}
269
270impl<T> OrdArith<T> for T
271where
272    T: Ord + Copy,
273{
274    fn cmp(self, rhs: T) -> Ordering {
275        <T as Ord>::cmp(&self, &rhs)
276    }
277}
278
279impl OrdArith<u32> for usize {
280    fn cmp(self, rhs: u32) -> Ordering {
281        match u32::try_from(self) {
282            Ok(lhs) => Ord::cmp(&lhs, &rhs),
283            Err(_) => Ordering::Greater,
284        }
285    }
286}
287
288impl OrdArith<u64> for usize {
289    fn cmp(self, rhs: u64) -> Ordering {
290        match u64::try_from(self) {
291            Ok(lhs) => Ord::cmp(&lhs, &rhs),
292            Err(_) => Ordering::Greater,
293        }
294    }
295}
296
297impl OrdArith<u128> for usize {
298    fn cmp(self, rhs: u128) -> Ordering {
299        match u128::try_from(self) {
300            Ok(lhs) => Ord::cmp(&lhs, &rhs),
301            Err(_) => Ordering::Greater,
302        }
303    }
304}
305
306impl OrdArith<usize> for u32 {
307    fn cmp(self, rhs: usize) -> Ordering {
308        match u32::try_from(rhs) {
309            Ok(rhs) => Ord::cmp(&self, &rhs),
310            Err(_) => Ordering::Less,
311        }
312    }
313}
314
315impl OrdArith<usize> for u64 {
316    fn cmp(self, rhs: usize) -> Ordering {
317        match u64::try_from(rhs) {
318            Ok(rhs) => Ord::cmp(&self, &rhs),
319            Err(_) => Ordering::Less,
320        }
321    }
322}
323
324impl OrdArith<usize> for u128 {
325    fn cmp(self, rhs: usize) -> Ordering {
326        match u128::try_from(rhs) {
327            Ok(rhs) => Ord::cmp(&self, &rhs),
328            Err(_) => Ordering::Less,
329        }
330    }
331}
332
333impl OrdArith<u32> for u64 {
334    fn cmp(self, rhs: u32) -> Ordering {
335        Ord::cmp(&self, &u64::from(rhs))
336    }
337}
338
339impl OrdArith<u32> for u128 {
340    fn cmp(self, rhs: u32) -> Ordering {
341        Ord::cmp(&self, &u128::from(rhs))
342    }
343}
344
345impl OrdArith<u64> for u128 {
346    fn cmp(self, rhs: u64) -> Ordering {
347        Ord::cmp(&self, &u128::from(rhs))
348    }
349}
350
351#[doc(hidden)]
352pub fn arith_cmp<T>(lhs: impl OrdArith<T>, rhs: T) -> Ordering {
353    lhs.cmp(rhs)
354}
355
356#[doc(hidden)]
357pub fn arith_eq<T>(lhs: impl OrdArith<T>, rhs: T) -> bool {
358    lhs.cmp(rhs) == Ordering::Equal
359}
360
361#[doc(hidden)]
362pub fn arith_ne<T>(lhs: impl OrdArith<T>, rhs: T) -> bool {
363    lhs.cmp(rhs) != Ordering::Equal
364}
365
366#[doc(hidden)]
367pub fn arith_lt<T>(lhs: impl OrdArith<T>, rhs: T) -> bool {
368    lhs.cmp(rhs) == Ordering::Less
369}
370
371#[doc(hidden)]
372pub fn arith_gt<T>(lhs: impl OrdArith<T>, rhs: T) -> bool {
373    lhs.cmp(rhs) == Ordering::Greater
374}
375
376#[doc(hidden)]
377pub fn arith_le<T>(lhs: impl OrdArith<T>, rhs: T) -> bool {
378    lhs.cmp(rhs) != Ordering::Greater
379}
380
381#[doc(hidden)]
382pub fn arith_ge<T>(lhs: impl OrdArith<T>, rhs: T) -> bool {
383    lhs.cmp(rhs) != Ordering::Less
384}
385
386/// Handles host OOM the same way global allocator does.
387/// This function should be called on host OOM error returned from Vulkan API.
388#[track_caller]
389pub fn out_of_host_memory() -> ! {
390    use std::alloc::{handle_alloc_error, Layout};
391
392    handle_alloc_error(unsafe { Layout::from_size_align_unchecked(1, 1) })
393}
394
395/// Handles host OOM the same way global allocator does.
396/// This function should be called on host OOM error returned from Vulkan API.
397pub fn host_memory_space_overflow() -> ! {
398    panic!("Memory address space overflow")
399}
400
401fn assert_object<T: Debug + Send + Sync + 'static>() {}
402fn assert_error<T: Error + Send + Sync + 'static>() {}
403
404/// Returns minimal aligned integer not smaller than value.
405pub fn align_up(align_mask: u64, value: u64) -> Option<u64> {
406    Some(value.checked_add(align_mask)? & !align_mask)
407}
408
409/// Returns maximal aligned integer not greater than value.
410pub fn align_down(align_mask: u64, value: u64) -> u64 {
411    value & !align_mask
412}
413
414#[macro_export]
415macro_rules! descriptor_set_layout_bindings {
416    ($($ty:ident $(($count:expr))? $(@$binding:literal)? for $($stages:ident),+ $($(| $flags:ident)+)?),*) => {
417        {
418            let mut binding = 0;
419            vec![
420                $({
421                    $(binding = $binding + 1)?;
422                    $crate::DescriptorSetLayoutBinding {
423                        binding: binding - 1,
424                        ty: $crate::DescriptorType::$ty,
425                        count: 1 $(- 1 + $count)?,
426                        stages: $($crate::ShaderStageFlags::$stages)|+,
427                        flags: $crate::DescriptorBindingFlags::empty() $(| $crate::DescriptorBindingFlags::$flags)*,
428                    }
429                },)*
430            ]
431        }
432    };
433}
434
435#[macro_export]
436macro_rules! descriptor_set_layout {
437    ($(|$flags:ident) *$($ty:ident $(($count:expr))? $(@$binding:literal)? for $($stages:ident)+ $($(| $bflags:ident)+)?),*) => {
438        $crate::DescriptorSetLayoutInfo {
439            flags: $crate::DescriptorSetLayoutFlags::empty() $(| $crate::DescriptorSetLayoutFlags::$flags)*,
440            bindings: descriptor_set_layout_bindings!($($ty $(@$binding)? $(* $count)? for $($stages)+ $($(| $bflags)+)?)*),
441        }
442    }
443}
444
445mod sealed {
446    #[doc(hidden)]
447    pub trait Sealed {}
448}
449
450trait IteratorExt: Iterator {
451    fn filter_min_by_key<B, F>(self, f: F) -> Option<Self::Item>
452    where
453        Self: Sized,
454        B: Ord,
455        F: FnMut(&Self::Item) -> Option<B>,
456    {
457        #[inline]
458        fn key<T, B>(mut f: impl FnMut(&T) -> Option<B>) -> impl FnMut(T) -> Option<(B, T)> {
459            move |x| Some((f(&x)?, x))
460        }
461
462        #[inline]
463        fn compare<T, B: Ord>((x_p, _): &(B, T), (y_p, _): &(B, T)) -> Ordering {
464            x_p.cmp(y_p)
465        }
466
467        let (_, x) = self.filter_map(key(f)).min_by(compare)?;
468        Some(x)
469    }
470}
471
472impl<T> IteratorExt for T where T: Iterator {}