gfx_hal/pso/
specialization.rs

1//! Pipeline specialization types.
2
3use std::{borrow::Cow, ops::Range, slice};
4
5/// Description of a specialization constant for the pipeline.
6#[derive(Debug, Clone, Hash, PartialEq)]
7pub struct SpecializationConstant {
8    /// Constant identifier in shader source.
9    pub id: u32,
10    /// Value to override specialization constant.
11    pub range: Range<u16>,
12}
13
14/// Information required for pipeline specialization.
15///
16/// Specialization allows for easy configuration of multiple similar pipelines.
17/// For example, there may be a boolean exposed to the shader that switches
18/// the [specularity](https://en.wikipedia.org/wiki/Specularity) on/off,
19/// provided via a [specialization constant][SpecializationConstant].
20///
21/// That would produce separate PSO's for the "on" and "off" states
22/// but they share most of the internal stuff and are fast to produce.
23/// More importantly, they are fast to execute, since the driver
24/// can optimize out the branch on that other PSO creation.
25#[derive(Debug, Clone)]
26pub struct Specialization<'a> {
27    /// Array of descriptors of specialization constants to override.
28    pub constants: Cow<'a, [SpecializationConstant]>,
29    /// Raw data of the specialization constants
30    pub data: Cow<'a, [u8]>,
31}
32
33impl Specialization<'_> {
34    /// Empty specialization instance.
35    pub const EMPTY: Self = Specialization {
36        constants: Cow::Borrowed(&[]),
37        data: Cow::Borrowed(&[]),
38    };
39}
40
41impl Default for Specialization<'_> {
42    fn default() -> Self {
43        Specialization::EMPTY
44    }
45}
46
47#[doc(hidden)]
48#[derive(Debug, Default)]
49pub struct SpecializationStorage {
50    constants: Vec<SpecializationConstant>,
51    data: Vec<u8>,
52}
53
54/// List of specialization constants.
55#[doc(hidden)]
56pub trait SpecConstList: Sized {
57    fn fold(self, storage: &mut SpecializationStorage);
58}
59
60impl<T> From<T> for Specialization<'_>
61where
62    T: SpecConstList,
63{
64    fn from(list: T) -> Self {
65        let mut storage = SpecializationStorage::default();
66        list.fold(&mut storage);
67        Specialization {
68            data: Cow::Owned(storage.data),
69            constants: Cow::Owned(storage.constants),
70        }
71    }
72}
73
74#[doc(hidden)]
75#[derive(Debug)]
76pub struct SpecConstListNil;
77
78#[doc(hidden)]
79#[derive(Debug)]
80pub struct SpecConstListCons<H, T> {
81    pub head: (u32, H),
82    pub tail: T,
83}
84
85impl SpecConstList for SpecConstListNil {
86    fn fold(self, _storage: &mut SpecializationStorage) {}
87}
88
89impl<H, T> SpecConstList for SpecConstListCons<H, T>
90where
91    T: SpecConstList,
92{
93    fn fold(self, storage: &mut SpecializationStorage) {
94        let size = std::mem::size_of::<H>();
95        assert!(storage.data.len() + size <= u16::MAX as usize);
96        let offset = storage.data.len() as u16;
97        storage.data.extend_from_slice(unsafe {
98            // Inspecting bytes is always safe.
99            let head_ptr: *const H = &self.head.1;
100            slice::from_raw_parts(head_ptr as *const u8, size)
101        });
102        storage.constants.push(SpecializationConstant {
103            id: self.head.0,
104            range: offset..offset + size as u16,
105        });
106        self.tail.fold(storage)
107    }
108}
109
110/// Macro for specifying list of specialization constants for `EntryPoint`.
111#[macro_export]
112macro_rules! spec_const_list {
113    (@ $(,)?) => {
114        $crate::pso::SpecConstListNil
115    };
116
117    (@ $head_id:expr => $head_constant:expr $(,$tail_id:expr => $tail_constant:expr)* $(,)?) => {
118        $crate::pso::SpecConstListCons {
119            head: ($head_id, $head_constant),
120            tail: $crate::spec_const_list!(@ $($tail_id => $tail_constant),*),
121        }
122    };
123
124    ($($id:expr => $constant:expr),* $(,)?) => {
125        $crate::spec_const_list!(@ $($id => $constant),*).into()
126    };
127
128    ($($constant:expr),* $(,)?) => {
129        {
130            let mut counter = 0;
131            $crate::spec_const_list!(@ $({ counter += 1; counter - 1 } => $constant),*).into()
132        }
133    };
134}