naga/back/glsl/mod.rs
1/*!
2Backend for [GLSL][glsl] (OpenGL Shading Language).
3
4The main structure is [`Writer`], it maintains internal state that is used
5to output a [`Module`](crate::Module) into glsl
6
7# Supported versions
8### Core
9- 330
10- 400
11- 410
12- 420
13- 430
14- 450
15
16### ES
17- 300
18- 310
19
20[glsl]: https://www.khronos.org/registry/OpenGL/index_gl.php
21*/
22
23// GLSL is mostly a superset of C but it also removes some parts of it this is a list of relevant
24// aspects for this backend.
25//
26// The most notable change is the introduction of the version preprocessor directive that must
27// always be the first line of a glsl file and is written as
28// `#version number profile`
29// `number` is the version itself (i.e. 300) and `profile` is the
30// shader profile we only support "core" and "es", the former is used in desktop applications and
31// the later is used in embedded contexts, mobile devices and browsers. Each one as it's own
32// versions (at the time of writing this the latest version for "core" is 460 and for "es" is 320)
33//
34// Other important preprocessor addition is the extension directive which is written as
35// `#extension name: behaviour`
36// Extensions provide increased features in a plugin fashion but they aren't required to be
37// supported hence why they are called extensions, that's why `behaviour` is used it specifies
38// whether the extension is strictly required or if it should only be enabled if needed. In our case
39// when we use extensions we set behaviour to `require` always.
40//
41// The only thing that glsl removes that makes a difference are pointers.
42//
43// Additions that are relevant for the backend are the discard keyword, the introduction of
44// vector, matrices, samplers, image types and functions that provide common shader operations
45
46pub use features::Features;
47pub use writer::Writer;
48
49use alloc::{
50 borrow::ToOwned,
51 format,
52 string::{String, ToString},
53 vec,
54 vec::Vec,
55};
56use core::{
57 cmp::Ordering,
58 fmt::{self, Error as FmtError, Write},
59 mem,
60};
61
62use hashbrown::hash_map;
63use thiserror::Error;
64
65use crate::{
66 back::{self, Baked},
67 common,
68 proc::{self, NameKey},
69 valid, Handle, ShaderStage, TypeInner,
70};
71use conv::*;
72use features::FeaturesManager;
73
74/// Contains simple 1:1 conversion functions.
75mod conv;
76/// Contains the features related code and the features querying method
77mod features;
78/// Contains a constant with a slice of all the reserved keywords RESERVED_KEYWORDS
79mod keywords;
80/// Contains the [`Writer`] type.
81mod writer;
82
83/// List of supported `core` GLSL versions.
84pub const SUPPORTED_CORE_VERSIONS: &[u16] = &[140, 150, 330, 400, 410, 420, 430, 440, 450, 460];
85/// List of supported `es` GLSL versions.
86pub const SUPPORTED_ES_VERSIONS: &[u16] = &[300, 310, 320];
87
88/// The suffix of the variable that will hold the calculated clamped level
89/// of detail for bounds checking in `ImageLoad`
90const CLAMPED_LOD_SUFFIX: &str = "_clamped_lod";
91
92pub(crate) const MODF_FUNCTION: &str = "naga_modf";
93pub(crate) const FREXP_FUNCTION: &str = "naga_frexp";
94
95// Must match code in glsl_built_in
96pub const FIRST_INSTANCE_BINDING: &str = "naga_vs_first_instance";
97
98#[cfg(feature = "deserialize")]
99#[derive(serde::Deserialize)]
100struct BindingMapSerialization {
101 resource_binding: crate::ResourceBinding,
102 bind_target: u8,
103}
104
105#[cfg(feature = "deserialize")]
106fn deserialize_binding_map<'de, D>(deserializer: D) -> Result<BindingMap, D::Error>
107where
108 D: serde::Deserializer<'de>,
109{
110 use serde::Deserialize;
111
112 let vec = Vec::<BindingMapSerialization>::deserialize(deserializer)?;
113 let mut map = BindingMap::default();
114 for item in vec {
115 map.insert(item.resource_binding, item.bind_target);
116 }
117 Ok(map)
118}
119
120/// Mapping between resources and bindings.
121pub type BindingMap = alloc::collections::BTreeMap<crate::ResourceBinding, u8>;
122
123impl crate::AtomicFunction {
124 const fn to_glsl(self) -> &'static str {
125 match self {
126 Self::Add | Self::Subtract => "Add",
127 Self::And => "And",
128 Self::InclusiveOr => "Or",
129 Self::ExclusiveOr => "Xor",
130 Self::Min => "Min",
131 Self::Max => "Max",
132 Self::Exchange { compare: None } => "Exchange",
133 Self::Exchange { compare: Some(_) } => "", //TODO
134 }
135 }
136}
137
138impl crate::AddressSpace {
139 /// Whether a variable with this address space can be initialized
140 const fn initializable(&self) -> bool {
141 match *self {
142 crate::AddressSpace::Function | crate::AddressSpace::Private => true,
143 crate::AddressSpace::WorkGroup
144 | crate::AddressSpace::Uniform
145 | crate::AddressSpace::Storage { .. }
146 | crate::AddressSpace::Handle
147 | crate::AddressSpace::Immediate
148 | crate::AddressSpace::TaskPayload => false,
149
150 crate::AddressSpace::RayPayload | crate::AddressSpace::IncomingRayPayload => {
151 unreachable!()
152 }
153 }
154 }
155}
156
157/// A GLSL version.
158#[derive(Debug, Copy, Clone, PartialEq)]
159#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
160#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
161pub enum Version {
162 /// `core` GLSL.
163 Desktop(u16),
164 /// `es` GLSL.
165 Embedded { version: u16, is_webgl: bool },
166}
167
168impl Version {
169 /// Create a new gles version
170 pub const fn new_gles(version: u16) -> Self {
171 Self::Embedded {
172 version,
173 is_webgl: false,
174 }
175 }
176
177 /// Returns true if self is `Version::Embedded` (i.e. is a es version)
178 const fn is_es(&self) -> bool {
179 match *self {
180 Version::Desktop(_) => false,
181 Version::Embedded { .. } => true,
182 }
183 }
184
185 /// Returns true if targeting WebGL
186 const fn is_webgl(&self) -> bool {
187 match *self {
188 Version::Desktop(_) => false,
189 Version::Embedded { is_webgl, .. } => is_webgl,
190 }
191 }
192
193 /// Checks the list of currently supported versions and returns true if it contains the
194 /// specified version
195 ///
196 /// # Notes
197 /// As an invalid version number will never be added to the supported version list
198 /// so this also checks for version validity
199 fn is_supported(&self) -> bool {
200 match *self {
201 Version::Desktop(v) => SUPPORTED_CORE_VERSIONS.contains(&v),
202 Version::Embedded { version: v, .. } => SUPPORTED_ES_VERSIONS.contains(&v),
203 }
204 }
205
206 fn supports_io_locations(&self) -> bool {
207 *self >= Version::Desktop(330) || *self >= Version::new_gles(300)
208 }
209
210 /// Checks if the version supports all of the explicit layouts:
211 /// - `location=` qualifiers for bindings
212 /// - `binding=` qualifiers for resources
213 ///
214 /// Note: `location=` for vertex inputs and fragment outputs is supported
215 /// unconditionally for GLES 300.
216 fn supports_explicit_locations(&self) -> bool {
217 *self >= Version::Desktop(420) || *self >= Version::new_gles(310)
218 }
219
220 fn supports_early_depth_test(&self) -> bool {
221 *self >= Version::Desktop(130) || *self >= Version::new_gles(310)
222 }
223
224 fn supports_std140_layout(&self) -> bool {
225 *self >= Version::Desktop(140) || *self >= Version::new_gles(300)
226 }
227
228 fn supports_std430_layout(&self) -> bool {
229 *self >= Version::Desktop(430) || *self >= Version::new_gles(310)
230 }
231
232 fn supports_fma_function(&self) -> bool {
233 *self >= Version::Desktop(400) || *self >= Version::new_gles(320)
234 }
235
236 fn supports_integer_functions(&self) -> bool {
237 *self >= Version::Desktop(400) || *self >= Version::new_gles(310)
238 }
239
240 fn supports_frexp_function(&self) -> bool {
241 *self >= Version::Desktop(400) || *self >= Version::new_gles(310)
242 }
243
244 fn supports_derivative_control(&self) -> bool {
245 *self >= Version::Desktop(450)
246 }
247
248 // For supports_pack_unpack_4x8, supports_pack_unpack_snorm_2x16, supports_pack_unpack_unorm_2x16
249 // see:
250 // https://registry.khronos.org/OpenGL-Refpages/gl4/html/unpackUnorm.xhtml
251 // https://registry.khronos.org/OpenGL-Refpages/es3/html/unpackUnorm.xhtml
252 // https://registry.khronos.org/OpenGL-Refpages/gl4/html/packUnorm.xhtml
253 // https://registry.khronos.org/OpenGL-Refpages/es3/html/packUnorm.xhtml
254 fn supports_pack_unpack_4x8(&self) -> bool {
255 *self >= Version::Desktop(400) || *self >= Version::new_gles(310)
256 }
257 fn supports_pack_unpack_snorm_2x16(&self) -> bool {
258 *self >= Version::Desktop(420) || *self >= Version::new_gles(300)
259 }
260 fn supports_pack_unpack_unorm_2x16(&self) -> bool {
261 *self >= Version::Desktop(400) || *self >= Version::new_gles(300)
262 }
263
264 // https://registry.khronos.org/OpenGL-Refpages/gl4/html/unpackHalf2x16.xhtml
265 // https://registry.khronos.org/OpenGL-Refpages/gl4/html/packHalf2x16.xhtml
266 // https://registry.khronos.org/OpenGL-Refpages/es3/html/unpackHalf2x16.xhtml
267 // https://registry.khronos.org/OpenGL-Refpages/es3/html/packHalf2x16.xhtml
268 fn supports_pack_unpack_half_2x16(&self) -> bool {
269 *self >= Version::Desktop(420) || *self >= Version::new_gles(300)
270 }
271}
272
273impl PartialOrd for Version {
274 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
275 match (*self, *other) {
276 (Version::Desktop(x), Version::Desktop(y)) => Some(x.cmp(&y)),
277 (Version::Embedded { version: x, .. }, Version::Embedded { version: y, .. }) => {
278 Some(x.cmp(&y))
279 }
280 _ => None,
281 }
282 }
283}
284
285impl fmt::Display for Version {
286 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287 match *self {
288 Version::Desktop(v) => write!(f, "{v} core"),
289 Version::Embedded { version: v, .. } => write!(f, "{v} es"),
290 }
291 }
292}
293
294bitflags::bitflags! {
295 /// Configuration flags for the [`Writer`].
296 #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
297 #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
298 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
299 pub struct WriterFlags: u32 {
300 /// Flip output Y and extend Z from (0, 1) to (-1, 1).
301 const ADJUST_COORDINATE_SPACE = 0x1;
302 /// Supports GL_EXT_texture_shadow_lod on the host, which provides
303 /// additional functions on shadows and arrays of shadows.
304 const TEXTURE_SHADOW_LOD = 0x2;
305 /// Supports ARB_shader_draw_parameters on the host, which provides
306 /// support for `gl_BaseInstanceARB`, `gl_BaseVertexARB`, `gl_DrawIDARB`, and `gl_DrawID`.
307 const DRAW_PARAMETERS = 0x4;
308 /// Include unused global variables, constants and functions. By default the output will exclude
309 /// global variables that are not used in the specified entrypoint (including indirect use),
310 /// all constant declarations, and functions that use excluded global variables.
311 const INCLUDE_UNUSED_ITEMS = 0x10;
312 /// Emit `PointSize` output builtin to vertex shaders, which is
313 /// required for drawing with `PointList` topology.
314 ///
315 /// https://registry.khronos.org/OpenGL/specs/es/3.2/GLSL_ES_Specification_3.20.html#built-in-language-variables
316 /// The variable gl_PointSize is intended for a shader to write the size of the point to be rasterized. It is measured in pixels.
317 /// If gl_PointSize is not written to, its value is undefined in subsequent pipe stages.
318 const FORCE_POINT_SIZE = 0x20;
319 }
320}
321
322/// Configuration used in the [`Writer`].
323#[derive(Debug, Clone)]
324#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
325#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
326#[cfg_attr(feature = "deserialize", serde(default))]
327pub struct Options {
328 /// The GLSL version to be used.
329 pub version: Version,
330 /// Configuration flags for the [`Writer`].
331 pub writer_flags: WriterFlags,
332 /// Map of resources association to binding locations.
333 #[cfg_attr(
334 feature = "deserialize",
335 serde(deserialize_with = "deserialize_binding_map")
336 )]
337 pub binding_map: BindingMap,
338 /// Should workgroup variables be zero initialized (by polyfilling)?
339 pub zero_initialize_workgroup_memory: bool,
340}
341
342impl Default for Options {
343 fn default() -> Self {
344 Options {
345 version: Version::new_gles(310),
346 writer_flags: WriterFlags::ADJUST_COORDINATE_SPACE,
347 binding_map: BindingMap::default(),
348 zero_initialize_workgroup_memory: true,
349 }
350 }
351}
352
353/// A subset of options meant to be changed per pipeline.
354#[derive(Debug, Clone)]
355#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
356#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
357pub struct PipelineOptions {
358 /// The stage of the entry point.
359 pub shader_stage: ShaderStage,
360 /// The name of the entry point.
361 ///
362 /// If no entry point that matches is found while creating a [`Writer`], an
363 /// error will be thrown.
364 pub entry_point: String,
365 /// How many views to render to, if doing multiview rendering.
366 pub multiview: Option<core::num::NonZeroU32>,
367}
368
369#[derive(Debug)]
370pub struct VaryingLocation {
371 /// The location of the global.
372 /// This corresponds to `layout(location = ..)` in GLSL.
373 pub location: u32,
374 /// The index which can be used for dual source blending.
375 /// This corresponds to `layout(index = ..)` in GLSL.
376 pub index: u32,
377}
378
379/// Reflection info for texture mappings and uniforms.
380#[derive(Debug)]
381pub struct ReflectionInfo {
382 /// Mapping between texture names and variables/samplers.
383 pub texture_mapping: crate::FastHashMap<String, TextureMapping>,
384 /// Mapping between uniform variables and names.
385 pub uniforms: crate::FastHashMap<Handle<crate::GlobalVariable>, String>,
386 /// Mapping between names and attribute locations.
387 pub varying: crate::FastHashMap<String, VaryingLocation>,
388 /// List of immediate data items in the shader.
389 pub immediates_items: Vec<ImmediateItem>,
390 /// Number of user-defined clip planes. Only applicable to vertex shaders.
391 pub clip_distance_count: u32,
392}
393
394/// Mapping between a texture and its sampler, if it exists.
395///
396/// GLSL pre-Vulkan has no concept of separate textures and samplers. Instead, everything is a
397/// `gsamplerN` where `g` is the scalar type and `N` is the dimension. But naga uses separate textures
398/// and samplers in the IR, so the backend produces a [`FastHashMap`](crate::FastHashMap) with the texture name
399/// as a key and a [`TextureMapping`] as a value. This way, the user knows where to bind.
400///
401/// [`Storage`](crate::ImageClass::Storage) images produce `gimageN` and don't have an associated sampler,
402/// so the [`sampler`](Self::sampler) field will be [`None`].
403#[derive(Debug, Clone)]
404pub struct TextureMapping {
405 /// Handle to the image global variable.
406 pub texture: Handle<crate::GlobalVariable>,
407 /// Handle to the associated sampler global variable, if it exists.
408 pub sampler: Option<Handle<crate::GlobalVariable>>,
409}
410
411/// All information to bind a single uniform value to the shader.
412///
413/// Immediates are emulated using traditional uniforms in OpenGL.
414///
415/// These are composed of a set of primitives (scalar, vector, matrix) that
416/// are given names. Because they are not backed by the concept of a buffer,
417/// we must do the work of calculating the offset of each primitive in the
418/// immediate data block.
419#[derive(Debug, Clone)]
420pub struct ImmediateItem {
421 /// GL uniform name for the item. This name is the same as if you were
422 /// to access it directly from a GLSL shader.
423 ///
424 /// The with the following example, the following names will be generated,
425 /// one name per GLSL uniform.
426 ///
427 /// ```glsl
428 /// struct InnerStruct {
429 /// value: f32,
430 /// }
431 ///
432 /// struct ImmediateData {
433 /// InnerStruct inner;
434 /// vec4 array[2];
435 /// }
436 ///
437 /// uniform ImmediateData _immediates_binding_cs;
438 /// ```
439 ///
440 /// ```text
441 /// - _immediates_binding_cs.inner.value
442 /// - _immediates_binding_cs.array[0]
443 /// - _immediates_binding_cs.array[1]
444 /// ```
445 ///
446 pub access_path: String,
447 /// Type of the uniform. This will only ever be a scalar, vector, or matrix.
448 pub ty: Handle<crate::Type>,
449 /// The offset in the immediate data memory block this uniform maps to.
450 ///
451 /// The size of the uniform can be derived from the type.
452 pub offset: u32,
453}
454
455/// Helper structure that generates a number
456#[derive(Default)]
457struct IdGenerator(u32);
458
459impl IdGenerator {
460 /// Generates a number that's guaranteed to be unique for this `IdGenerator`
461 const fn generate(&mut self) -> u32 {
462 // It's just an increasing number but it does the job
463 let ret = self.0;
464 self.0 += 1;
465 ret
466 }
467}
468
469/// Assorted options needed for generating varyings.
470#[derive(Clone, Copy)]
471struct VaryingOptions {
472 output: bool,
473 targeting_webgl: bool,
474 draw_parameters: bool,
475}
476
477impl VaryingOptions {
478 const fn from_writer_options(options: &Options, output: bool) -> Self {
479 Self {
480 output,
481 targeting_webgl: options.version.is_webgl(),
482 draw_parameters: options.writer_flags.contains(WriterFlags::DRAW_PARAMETERS),
483 }
484 }
485}
486
487/// Helper wrapper used to get a name for a varying
488///
489/// Varying have different naming schemes depending on their binding:
490/// - Varyings with builtin bindings get their name from [`glsl_built_in`].
491/// - Varyings with location bindings are named `_S_location_X` where `S` is a
492/// prefix identifying which pipeline stage the varying connects, and `X` is
493/// the location.
494struct VaryingName<'a> {
495 binding: &'a crate::Binding,
496 stage: ShaderStage,
497 options: VaryingOptions,
498}
499impl fmt::Display for VaryingName<'_> {
500 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
501 match *self.binding {
502 crate::Binding::Location {
503 blend_src: Some(1), ..
504 } => {
505 write!(f, "_fs2p_location1",)
506 }
507 crate::Binding::Location { location, .. } => {
508 let prefix = match (self.stage, self.options.output) {
509 (ShaderStage::Compute, _) => unreachable!(),
510 // pipeline to vertex
511 (ShaderStage::Vertex, false) => "p2vs",
512 // vertex to fragment
513 (ShaderStage::Vertex, true) | (ShaderStage::Fragment, false) => "vs2fs",
514 // fragment to pipeline
515 (ShaderStage::Fragment, true) => "fs2p",
516 (
517 ShaderStage::Task
518 | ShaderStage::Mesh
519 | ShaderStage::RayGeneration
520 | ShaderStage::AnyHit
521 | ShaderStage::ClosestHit
522 | ShaderStage::Miss,
523 _,
524 ) => unreachable!(),
525 };
526 write!(f, "_{prefix}_location{location}",)
527 }
528 crate::Binding::BuiltIn(built_in) => {
529 write!(f, "{}", glsl_built_in(built_in, self.options))
530 }
531 }
532 }
533}
534
535impl ShaderStage {
536 const fn to_str(self) -> &'static str {
537 match self {
538 ShaderStage::Compute => "cs",
539 ShaderStage::Fragment => "fs",
540 ShaderStage::Vertex => "vs",
541 ShaderStage::Task
542 | ShaderStage::Mesh
543 | ShaderStage::RayGeneration
544 | ShaderStage::AnyHit
545 | ShaderStage::ClosestHit
546 | ShaderStage::Miss => unreachable!(),
547 }
548 }
549}
550
551/// Shorthand result used internally by the backend
552type BackendResult<T = ()> = Result<T, Error>;
553
554/// A GLSL compilation error.
555#[derive(Debug, Error)]
556pub enum Error {
557 /// A error occurred while writing to the output.
558 #[error("Format error")]
559 FmtError(#[from] FmtError),
560 /// The specified [`Version`] doesn't have all required [`Features`].
561 ///
562 /// Contains the missing [`Features`].
563 #[error("The selected version doesn't support {0:?}")]
564 MissingFeatures(Features),
565 /// [`AddressSpace::Immediate`](crate::AddressSpace::Immediate) was used more than
566 /// once in the entry point, which isn't supported.
567 #[error("Multiple immediates aren't supported")]
568 MultipleImmediateData,
569 /// The specified [`Version`] isn't supported.
570 #[error("The specified version isn't supported")]
571 VersionNotSupported,
572 /// The entry point couldn't be found.
573 #[error("The requested entry point couldn't be found")]
574 EntryPointNotFound,
575 /// A call was made to an unsupported external.
576 #[error("A call was made to an unsupported external: {0}")]
577 UnsupportedExternal(String),
578 /// A scalar with an unsupported width was requested.
579 #[error("A scalar with an unsupported width was requested: {0:?}")]
580 UnsupportedScalar(crate::Scalar),
581 /// A image was used with multiple samplers, which isn't supported.
582 #[error("A image was used with multiple samplers")]
583 ImageMultipleSamplers,
584 #[error("{0}")]
585 Custom(String),
586 #[error("overrides should not be present at this stage")]
587 Override,
588 /// [`crate::Sampling::First`] is unsupported.
589 #[error("`{:?}` sampling is unsupported", crate::Sampling::First)]
590 FirstSamplingNotSupported,
591 #[error(transparent)]
592 ResolveArraySizeError(#[from] proc::ResolveArraySizeError),
593}
594
595/// Binary operation with a different logic on the GLSL side.
596enum BinaryOperation {
597 /// Vector comparison should use the function like `greaterThan()`, etc.
598 VectorCompare,
599 /// Vector component wise operation; used to polyfill unsupported ops like `|` and `&` for `bvecN`'s
600 VectorComponentWise,
601 /// GLSL `%` is SPIR-V `OpUMod/OpSMod` and `mod()` is `OpFMod`, but [`BinaryOperator::Modulo`](crate::BinaryOperator::Modulo) is `OpFRem`.
602 Modulo,
603 /// Any plain operation. No additional logic required.
604 Other,
605}
606
607fn is_value_init_supported(module: &crate::Module, ty: Handle<crate::Type>) -> bool {
608 match module.types[ty].inner {
609 TypeInner::Scalar { .. } | TypeInner::Vector { .. } | TypeInner::Matrix { .. } => true,
610 TypeInner::Array { base, size, .. } => {
611 size != crate::ArraySize::Dynamic && is_value_init_supported(module, base)
612 }
613 TypeInner::Struct { ref members, .. } => members
614 .iter()
615 .all(|member| is_value_init_supported(module, member.ty)),
616 _ => false,
617 }
618}
619
620pub fn supported_capabilities() -> valid::Capabilities {
621 use valid::Capabilities as Caps;
622
623 // Lots of these aren't supported on GLES in general, but naga is able to write them without panicking.
624
625 Caps::IMMEDIATES
626 | Caps::FLOAT64
627 | Caps::PRIMITIVE_INDEX
628 | Caps::CLIP_DISTANCE
629 | Caps::MULTIVIEW
630 | Caps::EARLY_DEPTH_TEST
631 | Caps::MULTISAMPLED_SHADING
632 | Caps::DUAL_SOURCE_BLENDING
633 | Caps::CUBE_ARRAY_TEXTURES
634 | Caps::SHADER_INT64
635 | Caps::SHADER_INT64_ATOMIC_ALL_OPS
636 | Caps::TEXTURE_ATOMIC
637 | Caps::TEXTURE_INT64_ATOMIC
638 | Caps::SUBGROUP
639 | Caps::SUBGROUP_BARRIER
640 | Caps::SHADER_FLOAT16
641 | Caps::SHADER_FLOAT16_IN_FLOAT32
642 | Caps::SHADER_BARYCENTRICS
643 | Caps::DRAW_INDEX
644}