1use core::fmt::Write;
2
3use super::{BackendResult, Error, Version, Writer};
4use crate::{
5 back::glsl::{Options, WriterFlags},
6 AddressSpace, Binding, Expression, Handle, ImageClass, ImageDimension, Interpolation,
7 SampleLevel, Sampling, Scalar, ScalarKind, ShaderStage, StorageFormat, Type, TypeInner,
8};
9
10bitflags::bitflags! {
11 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
13 pub struct Features: u32 {
14 const BUFFER_STORAGE = 1;
16 const ARRAY_OF_ARRAYS = 1 << 1;
17 const DOUBLE_TYPE = 1 << 2;
19 const FULL_IMAGE_FORMATS = 1 << 3;
21 const MULTISAMPLED_TEXTURES = 1 << 4;
22 const MULTISAMPLED_TEXTURE_ARRAYS = 1 << 5;
23 const CUBE_TEXTURES_ARRAY = 1 << 6;
24 const COMPUTE_SHADER = 1 << 7;
25 const IMAGE_LOAD_STORE = 1 << 8;
27 const CONSERVATIVE_DEPTH = 1 << 9;
28 const NOPERSPECTIVE_QUALIFIER = 1 << 11;
32 const SAMPLE_QUALIFIER = 1 << 12;
33 const CLIP_DISTANCE = 1 << 13;
34 const CULL_DISTANCE = 1 << 14;
35 const SAMPLE_VARIABLES = 1 << 15;
37 const DYNAMIC_ARRAY_SIZE = 1 << 16;
39 const MULTI_VIEW = 1 << 17;
40 const TEXTURE_SAMPLES = 1 << 18;
42 const TEXTURE_LEVELS = 1 << 19;
44 const IMAGE_SIZE = 1 << 20;
46 const DUAL_SOURCE_BLENDING = 1 << 21;
48 const INSTANCE_INDEX = 1 << 22;
52 const TEXTURE_SHADOW_LOD = 1 << 23;
54 const SUBGROUP_OPERATIONS = 1 << 24;
56 const TEXTURE_ATOMICS = 1 << 25;
58 const SHADER_BARYCENTRICS = 1 << 26;
60 const PRIMITIVE_INDEX = 1 << 27;
62 }
63}
64
65impl core::fmt::Display for Features {
66 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
67 bitflags::parser::to_writer(self, f)
69 }
70}
71
72pub(crate) struct FeaturesManager(Features);
77
78impl FeaturesManager {
79 pub const fn new() -> Self {
81 Self(Features::empty())
82 }
83
84 pub fn request(&mut self, features: Features) {
86 self.0 |= features
87 }
88
89 pub const fn contains(&mut self, features: Features) -> bool {
91 self.0.contains(features)
92 }
93
94 pub fn check_availability(&self, version: Version) -> BackendResult {
97 let mut missing = Features::empty();
99
100 macro_rules! check_feature {
102 ($feature:ident, $core:literal) => {
104 if self.0.contains(Features::$feature)
105 && (version < Version::Desktop($core) || version.is_es())
106 {
107 missing |= Features::$feature;
108 }
109 };
110 ($feature:ident, $core:literal, $es:literal) => {
112 if self.0.contains(Features::$feature)
113 && (version < Version::Desktop($core) || version < Version::new_gles($es))
114 {
115 missing |= Features::$feature;
116 }
117 };
118 }
119
120 check_feature!(COMPUTE_SHADER, 420, 310);
121 check_feature!(BUFFER_STORAGE, 400, 310);
122 check_feature!(DOUBLE_TYPE, 150);
123 check_feature!(CUBE_TEXTURES_ARRAY, 130, 310);
124 check_feature!(MULTISAMPLED_TEXTURES, 150, 300);
125 check_feature!(MULTISAMPLED_TEXTURE_ARRAYS, 150, 310);
126 check_feature!(ARRAY_OF_ARRAYS, 120, 310);
127 check_feature!(IMAGE_LOAD_STORE, 130, 310);
128 check_feature!(CONSERVATIVE_DEPTH, 130, 300);
129 check_feature!(NOPERSPECTIVE_QUALIFIER, 130);
130 check_feature!(SAMPLE_QUALIFIER, 400, 320);
131 check_feature!(CLIP_DISTANCE, 130, 300 );
132 check_feature!(CULL_DISTANCE, 450, 300 );
133 check_feature!(SAMPLE_VARIABLES, 400, 300);
134 check_feature!(DYNAMIC_ARRAY_SIZE, 400 , 310);
135 check_feature!(DUAL_SOURCE_BLENDING, 330, 300 );
136 check_feature!(SUBGROUP_OPERATIONS, 430, 310);
137 check_feature!(TEXTURE_ATOMICS, 420, 310);
138 match version {
139 Version::Embedded { is_webgl: true, .. } => check_feature!(MULTI_VIEW, 140, 300),
140 _ => check_feature!(MULTI_VIEW, 140, 310),
141 };
142 check_feature!(TEXTURE_SAMPLES, 150);
146 check_feature!(TEXTURE_LEVELS, 130);
147 check_feature!(IMAGE_SIZE, 430, 310);
148 check_feature!(TEXTURE_SHADOW_LOD, 200, 300);
149
150 if missing.is_empty() {
152 Ok(())
153 } else {
154 Err(Error::MissingFeatures(missing, version))
155 }
156 }
157
158 pub fn write(&self, options: &Options, mut out: impl Write) -> BackendResult {
164 if self.0.contains(Features::COMPUTE_SHADER) && !options.version.is_es() {
165 writeln!(out, "#extension GL_ARB_compute_shader : require")?;
167 }
168
169 if self.0.contains(Features::BUFFER_STORAGE) && !options.version.is_es() {
170 writeln!(
172 out,
173 "#extension GL_ARB_shader_storage_buffer_object : require"
174 )?;
175 }
176
177 if self.0.contains(Features::DOUBLE_TYPE) && options.version < Version::Desktop(400) {
178 writeln!(out, "#extension GL_ARB_gpu_shader_fp64 : require")?;
180 }
181
182 if self.0.contains(Features::CUBE_TEXTURES_ARRAY) {
183 if options.version.is_es() {
184 writeln!(out, "#extension GL_EXT_texture_cube_map_array : require")?;
186 } else if options.version < Version::Desktop(400) {
187 writeln!(out, "#extension GL_ARB_texture_cube_map_array : require")?;
189 }
190 }
191
192 if self.0.contains(Features::MULTISAMPLED_TEXTURE_ARRAYS) && options.version.is_es() {
193 writeln!(
195 out,
196 "#extension GL_OES_texture_storage_multisample_2d_array : require"
197 )?;
198 }
199
200 if self.0.contains(Features::ARRAY_OF_ARRAYS) && options.version < Version::Desktop(430) {
201 writeln!(out, "#extension ARB_arrays_of_arrays : require")?;
203 }
204
205 if self.0.contains(Features::IMAGE_LOAD_STORE) {
206 if self.0.contains(Features::FULL_IMAGE_FORMATS) && options.version.is_es() {
207 writeln!(out, "#extension GL_NV_image_formats : require")?;
209 }
210
211 if options.version < Version::Desktop(420) {
212 writeln!(out, "#extension GL_ARB_shader_image_load_store : require")?;
214 }
215 }
216
217 if self.0.contains(Features::CONSERVATIVE_DEPTH) {
218 if options.version.is_es() {
219 writeln!(out, "#extension GL_EXT_conservative_depth : require")?;
221 }
222
223 if options.version < Version::Desktop(420) {
224 writeln!(out, "#extension GL_ARB_conservative_depth : require")?;
226 }
227 }
228
229 if (self.0.contains(Features::CLIP_DISTANCE) || self.0.contains(Features::CULL_DISTANCE))
230 && options.version.is_es()
231 {
232 writeln!(out, "#extension GL_EXT_clip_cull_distance : require")?;
234 }
235
236 if self.0.contains(Features::SAMPLE_VARIABLES) && options.version.is_es() {
237 writeln!(out, "#extension GL_OES_sample_variables : require")?;
239 }
240
241 if self.0.contains(Features::MULTI_VIEW) {
242 if let Version::Embedded { is_webgl: true, .. } = options.version {
243 writeln!(out, "#extension GL_OVR_multiview2 : require")?;
245 } else {
246 writeln!(out, "#extension GL_EXT_multiview : require")?;
248 }
249 }
250
251 if self.0.contains(Features::TEXTURE_SAMPLES) {
252 writeln!(
254 out,
255 "#extension GL_ARB_shader_texture_image_samples : require"
256 )?;
257 }
258
259 if self.0.contains(Features::TEXTURE_LEVELS) && options.version < Version::Desktop(430) {
260 writeln!(out, "#extension GL_ARB_texture_query_levels : require")?;
262 }
263 if self.0.contains(Features::DUAL_SOURCE_BLENDING) && options.version.is_es() {
264 writeln!(out, "#extension GL_EXT_blend_func_extended : require")?;
266 }
267
268 if self.0.contains(Features::INSTANCE_INDEX) {
269 if options.writer_flags.contains(WriterFlags::DRAW_PARAMETERS) {
270 writeln!(out, "#extension GL_ARB_shader_draw_parameters : require")?;
272 }
273 }
274
275 if self.0.contains(Features::TEXTURE_SHADOW_LOD) {
276 writeln!(out, "#extension GL_EXT_texture_shadow_lod : require")?;
278 }
279
280 if self.0.contains(Features::SUBGROUP_OPERATIONS) {
281 writeln!(out, "#extension GL_KHR_shader_subgroup_basic : require")?;
283 writeln!(out, "#extension GL_KHR_shader_subgroup_vote : require")?;
284 writeln!(
285 out,
286 "#extension GL_KHR_shader_subgroup_arithmetic : require"
287 )?;
288 writeln!(out, "#extension GL_KHR_shader_subgroup_ballot : require")?;
289 writeln!(out, "#extension GL_KHR_shader_subgroup_shuffle : require")?;
290 writeln!(
291 out,
292 "#extension GL_KHR_shader_subgroup_shuffle_relative : require"
293 )?;
294 writeln!(out, "#extension GL_KHR_shader_subgroup_quad : require")?;
295 }
296
297 if self.0.contains(Features::TEXTURE_ATOMICS) {
298 writeln!(out, "#extension GL_OES_shader_image_atomic : require")?;
300 }
301
302 if self.0.contains(Features::SHADER_BARYCENTRICS) {
303 writeln!(
305 out,
306 "#extension GL_EXT_fragment_shader_barycentric : require"
307 )?;
308 }
309
310 if self.0.contains(Features::PRIMITIVE_INDEX) {
311 match options.version {
312 Version::Embedded { version, .. } if version < 320 => {
313 writeln!(out, "#extension GL_OES_geometry_shader : require")?;
314 }
315 Version::Desktop(version) if version < 150 => {
316 writeln!(out, "#extension GL_ARB_geometry_shader4 : require")?;
317 }
318 _ => (),
319 }
320 }
321
322 Ok(())
323 }
324}
325
326impl<W> Writer<'_, W> {
327 pub(super) fn collect_required_features(&mut self) -> BackendResult {
333 let ep_info = self.info.get_entry_point(self.entry_point_idx as usize);
334
335 if let Some(early_depth_test) = self.entry_point.early_depth_test {
336 match early_depth_test {
337 crate::EarlyDepthTest::Force => {
338 if self.options.version.supports_early_depth_test() {
339 self.features.request(Features::IMAGE_LOAD_STORE);
340 }
341 }
342 crate::EarlyDepthTest::Allow { .. } => {
343 self.features.request(Features::CONSERVATIVE_DEPTH);
344 }
345 }
346 }
347
348 for arg in self.entry_point.function.arguments.iter() {
349 self.varying_required_features(arg.binding.as_ref(), arg.ty);
350 }
351 if let Some(ref result) = self.entry_point.function.result {
352 self.varying_required_features(result.binding.as_ref(), result.ty);
353 }
354
355 if let ShaderStage::Compute = self.entry_point.stage {
356 self.features.request(Features::COMPUTE_SHADER)
357 }
358
359 if self.multiview.is_some() {
360 self.features.request(Features::MULTI_VIEW);
361 }
362
363 for (ty_handle, ty) in self.module.types.iter() {
364 match ty.inner {
365 TypeInner::Scalar(scalar)
366 | TypeInner::Vector { scalar, .. }
367 | TypeInner::Matrix { scalar, .. } => self.scalar_required_features(scalar),
368 TypeInner::Array { base, size, .. } => {
369 if let TypeInner::Array { .. } = self.module.types[base].inner {
370 self.features.request(Features::ARRAY_OF_ARRAYS)
371 }
372
373 if size == crate::ArraySize::Dynamic {
375 let mut is_used = false;
376
377 for (global_handle, global) in self.module.global_variables.iter() {
379 if ep_info[global_handle].is_empty() {
381 continue;
382 }
383
384 if global.ty == ty_handle {
386 is_used = true;
387 break;
388 }
389
390 if let TypeInner::Struct { ref members, .. } =
392 self.module.types[global.ty].inner
393 {
394 if let Some(last) = members.last() {
397 if last.ty == ty_handle {
398 is_used = true;
399 break;
400 }
401 }
402 }
403 }
404
405 if is_used {
407 self.features.request(Features::DYNAMIC_ARRAY_SIZE);
408 }
409 }
410 }
411 TypeInner::Image {
412 dim,
413 arrayed,
414 class,
415 } => {
416 if arrayed && dim == ImageDimension::Cube {
417 self.features.request(Features::CUBE_TEXTURES_ARRAY)
418 }
419
420 match class {
421 ImageClass::Sampled { multi: true, .. }
422 | ImageClass::Depth { multi: true } => {
423 self.features.request(Features::MULTISAMPLED_TEXTURES);
424 if arrayed {
425 self.features.request(Features::MULTISAMPLED_TEXTURE_ARRAYS);
426 }
427 }
428 ImageClass::Storage { format, .. } => {
429 self.features.request(Features::IMAGE_LOAD_STORE);
433 match format {
434 StorageFormat::R8Unorm
435 | StorageFormat::R8Snorm
436 | StorageFormat::R8Uint
437 | StorageFormat::R8Sint
438 | StorageFormat::R16Uint
439 | StorageFormat::R16Sint
440 | StorageFormat::R16Float
441 | StorageFormat::R16Unorm
442 | StorageFormat::R16Snorm
443 | StorageFormat::Rg8Unorm
444 | StorageFormat::Rg8Snorm
445 | StorageFormat::Rg8Uint
446 | StorageFormat::Rg8Sint
447 | StorageFormat::Rg16Uint
448 | StorageFormat::Rg16Sint
449 | StorageFormat::Rg16Float
450 | StorageFormat::Rg16Unorm
451 | StorageFormat::Rg16Snorm
452 | StorageFormat::Rgba16Unorm
453 | StorageFormat::Rgba16Snorm
454 | StorageFormat::Rgb10a2Uint
455 | StorageFormat::Rgb10a2Unorm
456 | StorageFormat::Rg11b10Ufloat
457 | StorageFormat::R64Uint
458 | StorageFormat::Rg32Uint
459 | StorageFormat::Rg32Sint
460 | StorageFormat::Rg32Float => {
461 self.features.request(Features::FULL_IMAGE_FORMATS)
462 }
463 _ => {}
464 }
465 }
466 ImageClass::Sampled { multi: false, .. }
467 | ImageClass::Depth { multi: false }
468 | ImageClass::External => {}
469 }
470 }
471 _ => {}
472 }
473 }
474
475 let mut immediates_used = false;
476
477 for (handle, global) in self.module.global_variables.iter() {
478 if ep_info[handle].is_empty() {
479 continue;
480 }
481 match global.space {
482 AddressSpace::WorkGroup => self.features.request(Features::COMPUTE_SHADER),
483 AddressSpace::Storage { .. } => self.features.request(Features::BUFFER_STORAGE),
484 AddressSpace::Immediate => {
485 if immediates_used {
486 return Err(Error::MultipleImmediateData);
487 }
488 immediates_used = true;
489 }
490 _ => {}
491 }
492 }
493
494 let &mut Self {
498 module,
499 info,
500 ref mut features,
501 entry_point,
502 entry_point_idx,
503 ref policies,
504 ..
505 } = self;
506
507 for (expressions, info) in module
510 .functions
511 .iter()
512 .map(|(h, f)| (&f.expressions, &info[h]))
513 .chain(core::iter::once((
514 &entry_point.function.expressions,
515 info.get_entry_point(entry_point_idx as usize),
516 )))
517 {
518 for (_, expr) in expressions.iter() {
519 match *expr {
520 Expression::ImageQuery {
522 image,
523 query,
524 ..
525 } => match query {
526 crate::ImageQuery::Size { .. } | crate::ImageQuery::NumLayers => {
531 if let TypeInner::Image {
532 class: ImageClass::Storage { .. }, ..
533 } = *info[image].ty.inner_with(&module.types) {
534 features.request(Features::IMAGE_SIZE)
535 }
536 },
537 crate::ImageQuery::NumLevels => features.request(Features::TEXTURE_LEVELS),
538 crate::ImageQuery::NumSamples => features.request(Features::TEXTURE_SAMPLES),
539 }
540 ,
541 Expression::ImageLoad {
544 sample, level, ..
545 } => {
546 if policies.image_load != crate::proc::BoundsCheckPolicy::Unchecked {
547 if sample.is_some() {
548 features.request(Features::TEXTURE_SAMPLES)
549 }
550
551 if level.is_some() {
552 features.request(Features::TEXTURE_LEVELS)
553 }
554 }
555 }
556 Expression::ImageSample { image, level, offset, .. } => {
557 if let TypeInner::Image {
558 dim,
559 arrayed,
560 class: ImageClass::Depth { .. },
561 } = *info[image].ty.inner_with(&module.types) {
562 let lod = matches!(level, SampleLevel::Zero | SampleLevel::Exact(_));
563 let bias = matches!(level, SampleLevel::Bias(_));
564 let auto = matches!(level, SampleLevel::Auto);
565 let cube = dim == ImageDimension::Cube;
566 let array2d = dim == ImageDimension::D2 && arrayed;
567 let gles = self.options.version.is_es();
568
569 let grad_workaround_applicable = (array2d || (cube && !arrayed)) && level == SampleLevel::Zero;
574 let prefer_grad_workaround = grad_workaround_applicable && !self.options.writer_flags.contains(WriterFlags::TEXTURE_SHADOW_LOD);
575
576 let mut ext_used = false;
577
578 ext_used |= (array2d || cube && arrayed) && bias;
581
582 ext_used |= array2d && (bias || (gles && auto)) && offset.is_some();
585
586 ext_used |= (cube || array2d) && lod && !prefer_grad_workaround;
591
592 if ext_used {
593 features.request(Features::TEXTURE_SHADOW_LOD);
594 }
595 }
596 }
597 Expression::SubgroupBallotResult |
598 Expression::SubgroupOperationResult { .. } => {
599 features.request(Features::SUBGROUP_OPERATIONS)
600 }
601 _ => {}
602 }
603 }
604 }
605
606 for blocks in module
607 .functions
608 .iter()
609 .map(|(_, f)| &f.body)
610 .chain(core::iter::once(&entry_point.function.body))
611 {
612 for (stmt, _) in blocks.span_iter() {
613 match *stmt {
614 crate::Statement::ImageAtomic { .. } => {
615 features.request(Features::TEXTURE_ATOMICS)
616 }
617 _ => {}
618 }
619 }
620 }
621
622 self.features.check_availability(self.options.version)
623 }
624
625 fn scalar_required_features(&mut self, scalar: Scalar) {
627 if scalar.kind == ScalarKind::Float && scalar.width == 8 {
628 self.features.request(Features::DOUBLE_TYPE);
629 }
630 }
631
632 fn varying_required_features(&mut self, binding: Option<&Binding>, ty: Handle<Type>) {
633 if let TypeInner::Struct { ref members, .. } = self.module.types[ty].inner {
634 for member in members {
635 self.varying_required_features(member.binding.as_ref(), member.ty);
636 }
637 } else if let Some(binding) = binding {
638 match *binding {
639 Binding::BuiltIn(built_in) => match built_in {
640 crate::BuiltIn::ClipDistances => self.features.request(Features::CLIP_DISTANCE),
641 crate::BuiltIn::CullDistance => self.features.request(Features::CULL_DISTANCE),
642 crate::BuiltIn::SampleIndex => {
643 self.features.request(Features::SAMPLE_VARIABLES)
644 }
645 crate::BuiltIn::ViewIndex => self.features.request(Features::MULTI_VIEW),
646 crate::BuiltIn::InstanceIndex | crate::BuiltIn::DrawIndex => {
647 self.features.request(Features::INSTANCE_INDEX)
648 }
649 crate::BuiltIn::Barycentric { .. } => {
650 self.features.request(Features::SHADER_BARYCENTRICS)
651 }
652 crate::BuiltIn::PrimitiveIndex => {
653 self.features.request(Features::PRIMITIVE_INDEX)
654 }
655 _ => {}
656 },
657 Binding::Location {
658 location: _,
659 interpolation,
660 sampling,
661 blend_src,
662 per_primitive: _,
663 } => {
664 if interpolation == Some(Interpolation::Linear) {
665 self.features.request(Features::NOPERSPECTIVE_QUALIFIER);
666 }
667 if sampling == Some(Sampling::Sample) {
668 self.features.request(Features::SAMPLE_QUALIFIER);
669 }
670 if blend_src.is_some() {
671 self.features.request(Features::DUAL_SOURCE_BLENDING);
672 }
673 }
674 }
675 }
676 }
677}