naga_types/
glsl.rs

1use core::{cmp::Ordering, fmt};
2
3// Must match code in glsl_built_in
4pub const FIRST_INSTANCE_BINDING: &str = "naga_vs_first_instance";
5
6/// List of supported `core` GLSL versions.
7pub const SUPPORTED_CORE_VERSIONS: &[u16] = &[140, 150, 330, 400, 410, 420, 430, 440, 450, 460];
8/// List of supported `es` GLSL versions.
9pub const SUPPORTED_ES_VERSIONS: &[u16] = &[300, 310, 320];
10
11/// A GLSL version.
12#[derive(Debug, Copy, Clone, PartialEq)]
13#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
14#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
15pub enum Version {
16    /// `core` GLSL.
17    Desktop(u16),
18    /// `es` GLSL.
19    Embedded { version: u16, is_webgl: bool },
20}
21
22impl Version {
23    /// Create a new gles version
24    pub const fn new_gles(version: u16) -> Self {
25        Self::Embedded {
26            version,
27            is_webgl: false,
28        }
29    }
30
31    /// Returns true if self is `Version::Embedded` (i.e. is a es version)
32    pub const fn is_es(&self) -> bool {
33        match *self {
34            Version::Desktop(_) => false,
35            Version::Embedded { .. } => true,
36        }
37    }
38
39    /// Returns true if targeting WebGL
40    pub const fn is_webgl(&self) -> bool {
41        match *self {
42            Version::Desktop(_) => false,
43            Version::Embedded { is_webgl, .. } => is_webgl,
44        }
45    }
46
47    /// Checks the list of currently supported versions and returns true if it contains the
48    /// specified version
49    ///
50    /// # Notes
51    /// As an invalid version number will never be added to the supported version list
52    /// so this also checks for version validity
53    pub fn is_supported(&self) -> bool {
54        match *self {
55            Version::Desktop(v) => SUPPORTED_CORE_VERSIONS.contains(&v),
56            Version::Embedded { version: v, .. } => SUPPORTED_ES_VERSIONS.contains(&v),
57        }
58    }
59
60    pub fn supports_io_locations(&self) -> bool {
61        *self >= Version::Desktop(330) || *self >= Version::new_gles(300)
62    }
63
64    /// Checks if the version supports all of the explicit layouts:
65    /// - `location=` qualifiers for bindings
66    /// - `binding=` qualifiers for resources
67    ///
68    /// Note: `location=` for vertex inputs and fragment outputs is supported
69    /// unconditionally for GLES 300.
70    pub fn supports_explicit_locations(&self) -> bool {
71        *self >= Version::Desktop(420) || *self >= Version::new_gles(310)
72    }
73
74    pub fn supports_early_depth_test(&self) -> bool {
75        *self >= Version::Desktop(130) || *self >= Version::new_gles(310)
76    }
77
78    pub fn supports_std140_layout(&self) -> bool {
79        *self >= Version::Desktop(140) || *self >= Version::new_gles(300)
80    }
81
82    pub fn supports_std430_layout(&self) -> bool {
83        // std430 is available from 400 via GL_ARB_shader_storage_buffer_object.
84        *self >= Version::Desktop(400) || *self >= Version::new_gles(310)
85    }
86
87    pub fn supports_fma_function(&self) -> bool {
88        *self >= Version::Desktop(400) || *self >= Version::new_gles(320)
89    }
90
91    pub fn supports_integer_functions(&self) -> bool {
92        *self >= Version::Desktop(400) || *self >= Version::new_gles(310)
93    }
94
95    pub fn supports_frexp_function(&self) -> bool {
96        *self >= Version::Desktop(400) || *self >= Version::new_gles(310)
97    }
98
99    pub fn supports_derivative_control(&self) -> bool {
100        *self >= Version::Desktop(450)
101    }
102
103    // For supports_pack_unpack_4x8, supports_pack_unpack_snorm_2x16, supports_pack_unpack_unorm_2x16
104    // see:
105    // https://registry.khronos.org/OpenGL-Refpages/gl4/html/unpackUnorm.xhtml
106    // https://registry.khronos.org/OpenGL-Refpages/es3/html/unpackUnorm.xhtml
107    // https://registry.khronos.org/OpenGL-Refpages/gl4/html/packUnorm.xhtml
108    // https://registry.khronos.org/OpenGL-Refpages/es3/html/packUnorm.xhtml
109    pub fn supports_pack_unpack_4x8(&self) -> bool {
110        *self >= Version::Desktop(400) || *self >= Version::new_gles(310)
111    }
112    pub fn supports_pack_unpack_snorm_2x16(&self) -> bool {
113        *self >= Version::Desktop(420) || *self >= Version::new_gles(300)
114    }
115    pub fn supports_pack_unpack_unorm_2x16(&self) -> bool {
116        *self >= Version::Desktop(400) || *self >= Version::new_gles(300)
117    }
118
119    // https://registry.khronos.org/OpenGL-Refpages/gl4/html/unpackHalf2x16.xhtml
120    // https://registry.khronos.org/OpenGL-Refpages/gl4/html/packHalf2x16.xhtml
121    // https://registry.khronos.org/OpenGL-Refpages/es3/html/unpackHalf2x16.xhtml
122    // https://registry.khronos.org/OpenGL-Refpages/es3/html/packHalf2x16.xhtml
123    pub fn supports_pack_unpack_half_2x16(&self) -> bool {
124        *self >= Version::Desktop(420) || *self >= Version::new_gles(300)
125    }
126}
127
128impl PartialOrd for Version {
129    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
130        match (*self, *other) {
131            (Version::Desktop(x), Version::Desktop(y)) => Some(x.cmp(&y)),
132            (Version::Embedded { version: x, .. }, Version::Embedded { version: y, .. }) => {
133                Some(x.cmp(&y))
134            }
135            _ => None,
136        }
137    }
138}
139
140impl fmt::Display for Version {
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        match *self {
143            Version::Desktop(v) => write!(f, "{v} core"),
144            Version::Embedded { version: v, .. } => write!(f, "{v} es"),
145        }
146    }
147}
148
149/// Mapping between resources and bindings.
150pub type BindingMap = alloc::collections::BTreeMap<crate::ResourceBinding, u8>;
151
152/// Separate type from `naga::ScalarKind` so that naga can easily add impl's
153#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
154#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
155#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
156pub enum GlslScalarKind {
157    Sint,
158    Uint,
159    Float,
160}
161
162/// Separate type from `naga::VectorSize` so that naga can easily add impl's
163#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
164#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
165#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
166pub enum GlslVectorSize {
167    Bi = 2,
168    Tri = 3,
169    Quad = 4,
170}
171impl GlslVectorSize {
172    pub fn alignment(&self) -> u32 {
173        match self {
174            Self::Bi => 2,
175            Self::Tri | Self::Quad => 4,
176        }
177    }
178}
179
180/// Separate type from `naga::Scalar` so that naga can easily add impl's
181#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
182#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
183#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
184pub struct GlslScalar {
185    pub kind: GlslScalarKind,
186    pub width: u8,
187}
188
189impl GlslScalar {
190    pub const F32: Self = Self {
191        kind: GlslScalarKind::Float,
192        width: 4,
193    };
194    pub const I32: Self = Self {
195        kind: GlslScalarKind::Sint,
196        width: 4,
197    };
198    pub const U32: Self = Self {
199        kind: GlslScalarKind::Uint,
200        width: 4,
201    };
202}
203
204/// A subset of `naga::TypeInner` so that uniforms can be analyzed without pulling in the entire naga IR.
205#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
206#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
207#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
208pub enum GlslUniformType {
209    Scalar(GlslScalar),
210    Vector {
211        size: GlslVectorSize,
212        scalar: GlslScalar,
213    },
214    Matrix {
215        columns: GlslVectorSize,
216        rows: GlslVectorSize,
217        scalar: GlslScalar,
218    },
219}
220impl GlslUniformType {
221    pub fn size(&self) -> u32 {
222        match self {
223            Self::Scalar(scalar) => scalar.width as u32,
224            Self::Vector { size, scalar } => *size as u32 * scalar.width as u32,
225            // matrices are treated as arrays of aligned columns
226            Self::Matrix {
227                columns,
228                rows,
229                scalar,
230            } => rows.alignment() * scalar.width as u32 * *columns as u32,
231        }
232    }
233}