Skip to main content

naga/back/spv/
writer.rs

1use alloc::{format, string::String, vec, vec::Vec};
2
3use arrayvec::ArrayVec;
4use hashbrown::hash_map::Entry;
5use spirv::Word;
6
7use super::{
8    block::DebugInfoInner,
9    helpers::{contains_builtin, global_needs_wrapper, map_storage_class},
10    Block, BlockContext, CachedConstant, CachedExpressions, CooperativeType, DebugInfo,
11    EntryPointContext, Error, Function, FunctionArgument, GlobalVariable, IdGenerator, Instruction,
12    LocalImageType, LocalType, LocalVariable, LogicalLayout, LookupFunctionType, LookupType,
13    NumericType, Options, PhysicalLayout, PipelineOptions, ResultMember, Writer, WriterFlags,
14    BITS_PER_BYTE,
15};
16use crate::{
17    arena::{Handle, HandleVec, UniqueArena},
18    back::spv::{
19        helpers::{is_uniform_matcx2_struct_member_access, BindingDecorations},
20        BindingInfo, Std140CompatTypeInfo, WrappedFunction,
21    },
22    common::ForDebugWithTypes as _,
23    proc::{Alignment, TypeResolution},
24    valid::{FunctionInfo, ModuleInfo},
25};
26
27pub struct FunctionInterface<'a> {
28    pub varying_ids: &'a mut Vec<Word>,
29    pub stage: crate::ShaderStage,
30    pub task_payload: Option<Handle<crate::GlobalVariable>>,
31    pub mesh_info: Option<crate::MeshStageInfo>,
32    pub workgroup_size: [u32; 3],
33}
34
35impl Function {
36    pub(super) fn to_words(&self, sink: &mut impl Extend<Word>) {
37        self.signature.as_ref().unwrap().to_words(sink);
38        for argument in self.parameters.iter() {
39            argument.instruction.to_words(sink);
40        }
41        for (index, block) in self.blocks.iter().enumerate() {
42            Instruction::label(block.label_id).to_words(sink);
43            if index == 0 {
44                for local_var in self.variables.values() {
45                    local_var.instruction.to_words(sink);
46                }
47                for local_var in self.ray_query_initialization_tracker_variables.values() {
48                    local_var.instruction.to_words(sink);
49                }
50                for local_var in self.ray_query_t_max_tracker_variables.values() {
51                    local_var.instruction.to_words(sink);
52                }
53                for local_var in self.force_loop_bounding_vars.iter() {
54                    local_var.instruction.to_words(sink);
55                }
56                for internal_var in self.spilled_composites.values() {
57                    internal_var.instruction.to_words(sink);
58                }
59            }
60            for instruction in block.body.iter() {
61                instruction.to_words(sink);
62            }
63        }
64        Instruction::function_end().to_words(sink);
65    }
66}
67
68impl Writer {
69    pub fn new(options: &Options) -> Result<Self, Error> {
70        let (major, minor) = options.lang_version;
71        if major != 1 {
72            return Err(Error::UnsupportedVersion(major, minor));
73        }
74
75        let mut capabilities_used = crate::FastIndexSet::default();
76        capabilities_used.insert(spirv::Capability::Shader);
77
78        let mut id_gen = IdGenerator::default();
79        let gl450_ext_inst_id = id_gen.next();
80        let void_type = id_gen.next();
81
82        Ok(Writer {
83            physical_layout: PhysicalLayout::new(major, minor),
84            logical_layout: LogicalLayout::default(),
85            id_gen,
86            capabilities_available: options.capabilities.clone(),
87            capabilities_used,
88            extensions_used: crate::FastIndexSet::default(),
89            debug_strings: vec![],
90            debugs: vec![],
91            annotations: vec![],
92            flags: options.flags,
93            bounds_check_policies: options.bounds_check_policies,
94            zero_initialize_workgroup_memory: options.zero_initialize_workgroup_memory,
95            force_loop_bounding: options.force_loop_bounding,
96            ray_query_initialization_tracking: options.ray_query_initialization_tracking,
97            trace_ray_argument_validation: options.trace_ray_argument_validation,
98            use_storage_input_output_16: options.use_storage_input_output_16,
99            emit_int_div_checks: options.emit_int_div_checks,
100            void_type,
101            tuple_of_u32s_ty_id: None,
102            lookup_type: crate::FastHashMap::default(),
103            lookup_function: crate::FastHashMap::default(),
104            lookup_function_type: crate::FastHashMap::default(),
105            wrapped_functions: crate::FastHashMap::default(),
106            constant_ids: HandleVec::new(),
107            cached_constants: crate::FastHashMap::default(),
108            global_variables: HandleVec::new(),
109            std140_compat_uniform_types: crate::FastHashMap::default(),
110            fake_missing_bindings: options.fake_missing_bindings,
111            binding_map: options.binding_map.clone(),
112            saved_cached: CachedExpressions::default(),
113            gl450_ext_inst_id,
114            temp_list: Vec::new(),
115            ray_query_functions: crate::FastHashMap::default(),
116            ray_tracing_functions: crate::FastHashMap::default(),
117            has_ray_tracing_pipeline: false,
118            io_f16_polyfills: super::f16_polyfill::F16IoPolyfill::new(
119                options.use_storage_input_output_16,
120            ),
121            debug_printf: None,
122            task_dispatch_limits: options.task_dispatch_limits,
123            mesh_shader_primitive_indices_clamp: options.mesh_shader_primitive_indices_clamp,
124        })
125    }
126
127    pub fn set_options(&mut self, options: &Options) -> Result<(), Error> {
128        let (major, minor) = options.lang_version;
129        if major != 1 {
130            return Err(Error::UnsupportedVersion(major, minor));
131        }
132        self.physical_layout = PhysicalLayout::new(major, minor);
133        self.capabilities_available = options.capabilities.clone();
134        self.flags = options.flags;
135        self.bounds_check_policies = options.bounds_check_policies;
136        self.zero_initialize_workgroup_memory = options.zero_initialize_workgroup_memory;
137        self.force_loop_bounding = options.force_loop_bounding;
138        self.use_storage_input_output_16 = options.use_storage_input_output_16;
139        self.binding_map = options.binding_map.clone();
140        self.io_f16_polyfills =
141            super::f16_polyfill::F16IoPolyfill::new(options.use_storage_input_output_16);
142        self.task_dispatch_limits = options.task_dispatch_limits;
143        self.mesh_shader_primitive_indices_clamp = options.mesh_shader_primitive_indices_clamp;
144        Ok(())
145    }
146
147    /// Returns `(major, minor)` of the SPIR-V language version.
148    pub const fn lang_version(&self) -> (u8, u8) {
149        self.physical_layout.lang_version()
150    }
151
152    /// Reset `Writer` to its initial state, retaining any allocations.
153    ///
154    /// Why not just implement `Reclaimable` for `Writer`? By design,
155    /// `Reclaimable::reclaim` requires ownership of the value, not just
156    /// `&mut`; see the trait documentation. But we need to use this method
157    /// from functions like `Writer::write`, which only have `&mut Writer`.
158    /// Workarounds include unsafe code (`core::ptr::read`, then `write`, ugh)
159    /// or something like a `Default` impl that returns an oddly-initialized
160    /// `Writer`, which is worse.
161    fn reset(&mut self) {
162        use super::reclaimable::Reclaimable;
163        use core::mem::take;
164
165        let mut id_gen = IdGenerator::default();
166        let gl450_ext_inst_id = id_gen.next();
167        let void_type = id_gen.next();
168
169        // Every field of the old writer that is not determined by the `Options`
170        // passed to `Writer::new` should be reset somehow.
171        let fresh = Writer {
172            // Copied from the old Writer:
173            flags: self.flags,
174            bounds_check_policies: self.bounds_check_policies,
175            zero_initialize_workgroup_memory: self.zero_initialize_workgroup_memory,
176            force_loop_bounding: self.force_loop_bounding,
177            ray_query_initialization_tracking: self.ray_query_initialization_tracking,
178            trace_ray_argument_validation: self.trace_ray_argument_validation,
179            use_storage_input_output_16: self.use_storage_input_output_16,
180            capabilities_available: take(&mut self.capabilities_available),
181            fake_missing_bindings: self.fake_missing_bindings,
182            binding_map: take(&mut self.binding_map),
183            task_dispatch_limits: self.task_dispatch_limits,
184            mesh_shader_primitive_indices_clamp: self.mesh_shader_primitive_indices_clamp,
185            emit_int_div_checks: self.emit_int_div_checks,
186
187            // Initialized afresh:
188            id_gen,
189            void_type,
190            tuple_of_u32s_ty_id: None,
191            gl450_ext_inst_id,
192
193            // Reclaimed:
194            capabilities_used: take(&mut self.capabilities_used).reclaim(),
195            extensions_used: take(&mut self.extensions_used).reclaim(),
196            physical_layout: self.physical_layout.clone().reclaim(),
197            logical_layout: take(&mut self.logical_layout).reclaim(),
198            debug_strings: take(&mut self.debug_strings).reclaim(),
199            debugs: take(&mut self.debugs).reclaim(),
200            annotations: take(&mut self.annotations).reclaim(),
201            lookup_type: take(&mut self.lookup_type).reclaim(),
202            lookup_function: take(&mut self.lookup_function).reclaim(),
203            lookup_function_type: take(&mut self.lookup_function_type).reclaim(),
204            wrapped_functions: take(&mut self.wrapped_functions).reclaim(),
205            constant_ids: take(&mut self.constant_ids).reclaim(),
206            cached_constants: take(&mut self.cached_constants).reclaim(),
207            global_variables: take(&mut self.global_variables).reclaim(),
208            std140_compat_uniform_types: take(&mut self.std140_compat_uniform_types).reclaim(),
209            saved_cached: take(&mut self.saved_cached).reclaim(),
210            temp_list: take(&mut self.temp_list).reclaim(),
211            ray_query_functions: take(&mut self.ray_query_functions).reclaim(),
212            ray_tracing_functions: take(&mut self.ray_tracing_functions).reclaim(),
213            has_ray_tracing_pipeline: false,
214            io_f16_polyfills: take(&mut self.io_f16_polyfills).reclaim(),
215            debug_printf: None,
216        };
217
218        *self = fresh;
219
220        self.capabilities_used.insert(spirv::Capability::Shader);
221    }
222
223    /// Indicate that the code requires any one of the listed capabilities.
224    ///
225    /// If nothing in `capabilities` appears in the available capabilities
226    /// specified in the [`Options`] from which this `Writer` was created,
227    /// return an error. The `what` string is used in the error message to
228    /// explain what provoked the requirement. (If no available capabilities were
229    /// given, assume everything is available.)
230    ///
231    /// The first acceptable capability will be added to this `Writer`'s
232    /// [`capabilities_used`] table, and an `OpCapability` emitted for it in the
233    /// result. For this reason, more specific capabilities should be listed
234    /// before more general.
235    ///
236    /// [`capabilities_used`]: Writer::capabilities_used
237    pub(super) fn require_any(
238        &mut self,
239        what: &'static str,
240        capabilities: &[spirv::Capability],
241    ) -> Result<(), Error> {
242        match *capabilities {
243            [] => Ok(()),
244            [first, ..] => {
245                // Find the first acceptable capability, or return an error if
246                // there is none.
247                let selected = match self.capabilities_available {
248                    None => first,
249                    Some(ref available) => {
250                        match capabilities
251                            .iter()
252                            // need explicit type for hashbrown::HashSet::contains fn call to keep rustc happy
253                            .find(|cap| available.contains::<spirv::Capability>(cap))
254                        {
255                            Some(&cap) => cap,
256                            None => {
257                                return Err(Error::MissingCapabilities(what, capabilities.to_vec()))
258                            }
259                        }
260                    }
261                };
262                self.capabilities_used.insert(selected);
263                Ok(())
264            }
265        }
266    }
267
268    /// Indicate that the code requires all of the listed capabilities.
269    ///
270    /// If all entries of `capabilities` appear in the available capabilities
271    /// specified in the [`Options`] from which this `Writer` was created
272    /// (including the case where [`Options::capabilities`] is `None`), add
273    /// them all to this `Writer`'s [`capabilities_used`] table, and return
274    /// `Ok(())`. If at least one of the listed capabilities is not available,
275    /// do not add anything to the `capabilities_used` table, and return the
276    /// first unavailable requested capability, wrapped in `Err()`.
277    ///
278    /// This method is does not return an [`enum@Error`] in case of failure
279    /// because it may be used in cases where the caller can recover (e.g.,
280    /// with a polyfill) if the requested capabilities are not available. In
281    /// this case, it would be unnecessary work to find *all* the unavailable
282    /// requested capabilities, and to allocate a `Vec` for them, just so we
283    /// could return an [`Error::MissingCapabilities`]).
284    ///
285    /// [`capabilities_used`]: Writer::capabilities_used
286    pub(super) fn require_all(
287        &mut self,
288        capabilities: &[spirv::Capability],
289    ) -> Result<(), spirv::Capability> {
290        if let Some(ref available) = self.capabilities_available {
291            for requested in capabilities {
292                if !available.contains(requested) {
293                    return Err(*requested);
294                }
295            }
296        }
297
298        for requested in capabilities {
299            self.capabilities_used.insert(*requested);
300        }
301
302        Ok(())
303    }
304
305    /// Indicate that the code uses the given extension.
306    pub(super) fn use_extension(&mut self, extension: &'static str) {
307        self.extensions_used.insert(extension);
308    }
309
310    pub(super) fn get_type_id(&mut self, lookup_ty: LookupType) -> Word {
311        match self.lookup_type.entry(lookup_ty) {
312            Entry::Occupied(e) => *e.get(),
313            Entry::Vacant(e) => {
314                let local = match lookup_ty {
315                    LookupType::Handle(_handle) => unreachable!("Handles are populated at start"),
316                    LookupType::Local(local) => local,
317                };
318
319                let id = self.id_gen.next();
320                e.insert(id);
321                self.write_type_declaration_local(id, local);
322                id
323            }
324        }
325    }
326
327    pub(super) fn get_handle_type_id(&mut self, handle: Handle<crate::Type>) -> Word {
328        self.get_type_id(LookupType::Handle(handle))
329    }
330
331    pub(super) fn get_expression_lookup_type(&mut self, tr: &TypeResolution) -> LookupType {
332        match *tr {
333            TypeResolution::Handle(ty_handle) => LookupType::Handle(ty_handle),
334            TypeResolution::Value(ref inner) => {
335                let inner_local_type = self.localtype_from_inner(inner).unwrap();
336                LookupType::Local(inner_local_type)
337            }
338        }
339    }
340
341    pub(super) fn get_expression_type_id(&mut self, tr: &TypeResolution) -> Word {
342        let lookup_ty = self.get_expression_lookup_type(tr);
343        self.get_type_id(lookup_ty)
344    }
345
346    pub(super) fn get_localtype_id(&mut self, local: LocalType) -> Word {
347        self.get_type_id(LookupType::Local(local))
348    }
349
350    pub(super) fn get_pointer_type_id(&mut self, base: Word, class: spirv::StorageClass) -> Word {
351        self.get_type_id(LookupType::Local(LocalType::Pointer { base, class }))
352    }
353
354    pub(super) fn get_handle_pointer_type_id(
355        &mut self,
356        base: Handle<crate::Type>,
357        class: spirv::StorageClass,
358    ) -> Word {
359        let base_id = self.get_handle_type_id(base);
360        self.get_pointer_type_id(base_id, class)
361    }
362
363    pub(super) fn get_ray_query_pointer_id(&mut self) -> Word {
364        let rq_id = self.get_type_id(LookupType::Local(LocalType::RayQuery));
365        self.get_pointer_type_id(rq_id, spirv::StorageClass::Function)
366    }
367
368    /// Return a SPIR-V type for a pointer to `resolution`.
369    ///
370    /// The given `resolution` must be one that we can represent
371    /// either as a `LocalType::Pointer` or `LocalType::LocalPointer`.
372    pub(super) fn get_resolution_pointer_id(
373        &mut self,
374        resolution: &TypeResolution,
375        class: spirv::StorageClass,
376    ) -> Word {
377        let resolution_type_id = self.get_expression_type_id(resolution);
378        self.get_pointer_type_id(resolution_type_id, class)
379    }
380
381    pub(super) fn get_numeric_type_id(&mut self, numeric: NumericType) -> Word {
382        self.get_type_id(LocalType::Numeric(numeric).into())
383    }
384
385    pub(super) fn get_u32_type_id(&mut self) -> Word {
386        self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::U32))
387    }
388
389    pub(super) fn get_f32_type_id(&mut self) -> Word {
390        self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::F32))
391    }
392
393    pub(super) fn get_vec2u_type_id(&mut self) -> Word {
394        self.get_numeric_type_id(NumericType::Vector {
395            size: crate::VectorSize::Bi,
396            scalar: crate::Scalar::U32,
397        })
398    }
399
400    pub(super) fn get_vec2f_type_id(&mut self) -> Word {
401        self.get_numeric_type_id(NumericType::Vector {
402            size: crate::VectorSize::Bi,
403            scalar: crate::Scalar::F32,
404        })
405    }
406
407    pub(super) fn get_vec3u_type_id(&mut self) -> Word {
408        self.get_numeric_type_id(NumericType::Vector {
409            size: crate::VectorSize::Tri,
410            scalar: crate::Scalar::U32,
411        })
412    }
413
414    pub(super) fn get_f32_pointer_type_id(&mut self, class: spirv::StorageClass) -> Word {
415        let f32_id = self.get_f32_type_id();
416        self.get_pointer_type_id(f32_id, class)
417    }
418
419    pub(super) fn get_vec2u_pointer_type_id(&mut self, class: spirv::StorageClass) -> Word {
420        let vec2u_id = self.get_numeric_type_id(NumericType::Vector {
421            size: crate::VectorSize::Bi,
422            scalar: crate::Scalar::U32,
423        });
424        self.get_pointer_type_id(vec2u_id, class)
425    }
426
427    pub(super) fn get_bool_type_id(&mut self) -> Word {
428        self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::BOOL))
429    }
430
431    pub(super) fn get_vec2_bool_type_id(&mut self) -> Word {
432        self.get_numeric_type_id(NumericType::Vector {
433            size: crate::VectorSize::Bi,
434            scalar: crate::Scalar::BOOL,
435        })
436    }
437
438    pub(super) fn get_vec3_bool_type_id(&mut self) -> Word {
439        self.get_numeric_type_id(NumericType::Vector {
440            size: crate::VectorSize::Tri,
441            scalar: crate::Scalar::BOOL,
442        })
443    }
444
445    /// Used for "mulhi" to get the upper bits of multiplication.
446    ///
447    /// More specifically, `OpUMulExtended` multiplies 2 numbers and returns the lower and upper bits of the result
448    /// as a user-defined struct type with 2 u32s. This defines that struct.
449    pub(super) fn get_tuple_of_u32s_ty_id(&mut self) -> Word {
450        if let Some(val) = self.tuple_of_u32s_ty_id {
451            val
452        } else {
453            let id = self.id_gen.next();
454            let u32_id = self.get_u32_type_id();
455            let ins = Instruction::type_struct(id, &[u32_id, u32_id]);
456            ins.to_words(&mut self.logical_layout.declarations);
457            self.tuple_of_u32s_ty_id = Some(id);
458            id
459        }
460    }
461
462    pub(super) fn decorate(&mut self, id: Word, decoration: spirv::Decoration, operands: &[Word]) {
463        self.annotations
464            .push(Instruction::decorate(id, decoration, operands));
465    }
466
467    /// Return `inner` as a `LocalType`, if that's possible.
468    ///
469    /// If `inner` can be represented as a `LocalType`, return
470    /// `Some(local_type)`.
471    ///
472    /// Otherwise, return `None`. In this case, the type must always be looked
473    /// up using a `LookupType::Handle`.
474    fn localtype_from_inner(&mut self, inner: &crate::TypeInner) -> Option<LocalType> {
475        Some(match *inner {
476            crate::TypeInner::Scalar(_)
477            | crate::TypeInner::Atomic(_)
478            | crate::TypeInner::Vector { .. }
479            | crate::TypeInner::Matrix { .. } => {
480                // We expect `NumericType::from_inner` to handle all
481                // these cases, so unwrap.
482                LocalType::Numeric(NumericType::from_inner(inner).unwrap())
483            }
484            crate::TypeInner::CooperativeMatrix { .. } => {
485                LocalType::Cooperative(CooperativeType::from_inner(inner).unwrap())
486            }
487            crate::TypeInner::Pointer { base, space } => {
488                let base_type_id = self.get_handle_type_id(base);
489                LocalType::Pointer {
490                    base: base_type_id,
491                    class: map_storage_class(space),
492                }
493            }
494            crate::TypeInner::ValuePointer {
495                size,
496                scalar,
497                space,
498            } => {
499                let base_numeric_type = match size {
500                    Some(size) => NumericType::Vector { size, scalar },
501                    None => NumericType::Scalar(scalar),
502                };
503                LocalType::Pointer {
504                    base: self.get_numeric_type_id(base_numeric_type),
505                    class: map_storage_class(space),
506                }
507            }
508            crate::TypeInner::Image {
509                dim,
510                arrayed,
511                class,
512            } => LocalType::Image(LocalImageType::from_inner(dim, arrayed, class)),
513            crate::TypeInner::Sampler { comparison: _ } => LocalType::Sampler,
514            crate::TypeInner::AccelerationStructure { .. } => LocalType::AccelerationStructure,
515            crate::TypeInner::RayQuery { .. } => LocalType::RayQuery,
516            crate::TypeInner::Array { .. }
517            | crate::TypeInner::Struct { .. }
518            | crate::TypeInner::BindingArray { .. } => return None,
519        })
520    }
521
522    /// Resolve the [`BindingInfo`] for a [`crate::ResourceBinding`] from the
523    /// provided [`Writer::binding_map`].
524    ///
525    /// If the specified resource is not present in the binding map this will
526    /// return an error, unless [`Writer::fake_missing_bindings`] is set.
527    pub(super) fn resolve_resource_binding(
528        &self,
529        res_binding: &crate::ResourceBinding,
530    ) -> Result<BindingInfo, Error> {
531        match self.binding_map.get(res_binding) {
532            Some(target) => Ok(*target),
533            None if self.fake_missing_bindings => Ok(BindingInfo {
534                descriptor_set: res_binding.group,
535                binding: res_binding.binding,
536                binding_array_size: None,
537                external_texture: false,
538            }),
539            None => Err(Error::MissingBinding(*res_binding)),
540        }
541    }
542
543    /// Emits code for any wrapper functions required by the expressions in ir_function.
544    /// The IDs of any emitted functions will be stored in [`Self::wrapped_functions`].
545    fn write_wrapped_functions(
546        &mut self,
547        ir_function: &crate::Function,
548        info: &FunctionInfo,
549        ir_module: &crate::Module,
550    ) -> Result<(), Error> {
551        log::trace!("Generating wrapped functions for {:?}", ir_function.name);
552
553        for (expr_handle, expr) in ir_function.expressions.iter() {
554            match *expr {
555                crate::Expression::Binary { op, left, right } => {
556                    let expr_ty_inner = info[expr_handle].ty.inner_with(&ir_module.types);
557                    if let Some(expr_ty) = NumericType::from_inner(expr_ty_inner) {
558                        match (op, expr_ty.scalar().kind) {
559                            // Division and modulo are undefined behaviour when the
560                            // dividend is the minimum representable value and the divisor
561                            // is negative one, or when the divisor is zero. These wrapped
562                            // functions override the divisor to one in these cases,
563                            // matching the WGSL spec.
564                            //
565                            // Signed `%` is additionally always wrapped (even without
566                            // `emit_int_div_checks`) so it can be lowered to
567                            // `a - b * (a / b)`: `OpSRem` produces a poison result for
568                            // negative operands in the Vulkan SPIR-V environment unless
569                            // `VK_KHR_maintenance8` is enabled. See
570                            // <https://github.com/gfx-rs/wgpu/issues/8191>.
571                            (
572                                crate::BinaryOperator::Divide | crate::BinaryOperator::Modulo,
573                                crate::ScalarKind::Sint | crate::ScalarKind::Uint,
574                            ) if self.emit_int_div_checks
575                                || matches!(
576                                    (op, expr_ty.scalar().kind),
577                                    (crate::BinaryOperator::Modulo, crate::ScalarKind::Sint)
578                                ) =>
579                            {
580                                self.write_wrapped_binary_op(
581                                    op,
582                                    expr_ty,
583                                    &info[left].ty,
584                                    &info[right].ty,
585                                )?;
586                            }
587                            _ => {}
588                        }
589                    }
590                }
591                crate::Expression::Load { pointer } => {
592                    if let crate::TypeInner::Pointer {
593                        base: pointer_type,
594                        space: crate::AddressSpace::Uniform,
595                    } = *info[pointer].ty.inner_with(&ir_module.types)
596                    {
597                        if self.std140_compat_uniform_types.contains_key(&pointer_type) {
598                            // Loading a std140 compat type requires the wrapper function
599                            // to convert to the regular type.
600                            self.write_wrapped_convert_from_std140_compat_type(
601                                ir_module,
602                                pointer_type,
603                            )?;
604                        }
605                    }
606                }
607                crate::Expression::Access { base, .. } => {
608                    if let crate::TypeInner::Pointer {
609                        base: base_type,
610                        space: crate::AddressSpace::Uniform,
611                    } = *info[base].ty.inner_with(&ir_module.types)
612                    {
613                        // Dynamic accesses of a two-row matrix's columns require a
614                        // wrapper function.
615                        if let crate::TypeInner::Matrix {
616                            rows: crate::VectorSize::Bi,
617                            ..
618                        } = ir_module.types[base_type].inner
619                        {
620                            self.write_wrapped_matcx2_get_column(ir_module, base_type)?;
621                            // If the matrix is *not* directly a member of a struct, then
622                            // we additionally require a wrapper function to convert from
623                            // the std140 compat type to the regular type.
624                            if !is_uniform_matcx2_struct_member_access(
625                                ir_function,
626                                info,
627                                ir_module,
628                                base,
629                            ) {
630                                self.write_wrapped_convert_from_std140_compat_type(
631                                    ir_module, base_type,
632                                )?;
633                            }
634                        }
635                    }
636                }
637                _ => {}
638            }
639        }
640
641        Ok(())
642    }
643
644    /// Write a SPIR-V function that performs the operator `op` with Naga IR semantics.
645    ///
646    /// Define a function that performs an integer division or modulo operation,
647    /// except that using a divisor of zero or causing signed overflow with a
648    /// divisor of -1 returns the numerator unchanged, rather than exhibiting
649    /// undefined behavior.
650    ///
651    /// Store the generated function's id in the [`wrapped_functions`] table.
652    ///
653    /// The operator `op` must be either [`Divide`] or [`Modulo`].
654    ///
655    /// # Panics
656    ///
657    /// The `return_type`, `left_type` or `right_type` arguments must all be
658    /// integer scalars or vectors. If not, this function panics.
659    ///
660    /// [`wrapped_functions`]: Writer::wrapped_functions
661    /// [`Divide`]: crate::BinaryOperator::Divide
662    /// [`Modulo`]: crate::BinaryOperator::Modulo
663    fn write_wrapped_binary_op(
664        &mut self,
665        op: crate::BinaryOperator,
666        return_type: NumericType,
667        left_type: &TypeResolution,
668        right_type: &TypeResolution,
669    ) -> Result<(), Error> {
670        let return_type_id = self.get_localtype_id(LocalType::Numeric(return_type));
671        let left_type_id = self.get_expression_type_id(left_type);
672        let right_type_id = self.get_expression_type_id(right_type);
673
674        // Check if we've already emitted this function.
675        let wrapped = WrappedFunction::BinaryOp {
676            op,
677            left_type_id,
678            right_type_id,
679        };
680        let function_id = match self.wrapped_functions.entry(wrapped) {
681            Entry::Occupied(_) => return Ok(()),
682            Entry::Vacant(e) => *e.insert(self.id_gen.next()),
683        };
684
685        let scalar = return_type.scalar();
686
687        if self.flags.contains(WriterFlags::DEBUG) {
688            let function_name = match op {
689                crate::BinaryOperator::Divide => "naga_div",
690                crate::BinaryOperator::Modulo => "naga_mod",
691                _ => unreachable!(),
692            };
693            self.debugs
694                .push(Instruction::name(function_id, function_name));
695        }
696        let mut function = Function::default();
697
698        let function_type_id = self.get_function_type(LookupFunctionType {
699            parameter_type_ids: vec![left_type_id, right_type_id],
700            return_type_id,
701        });
702        function.signature = Some(Instruction::function(
703            return_type_id,
704            function_id,
705            spirv::FunctionControl::empty(),
706            function_type_id,
707        ));
708
709        let lhs_id = self.id_gen.next();
710        let rhs_id = self.id_gen.next();
711        if self.flags.contains(WriterFlags::DEBUG) {
712            self.debugs.push(Instruction::name(lhs_id, "lhs"));
713            self.debugs.push(Instruction::name(rhs_id, "rhs"));
714        }
715        let left_par = Instruction::function_parameter(left_type_id, lhs_id);
716        let right_par = Instruction::function_parameter(right_type_id, rhs_id);
717        for instruction in [left_par, right_par] {
718            function.parameters.push(FunctionArgument {
719                instruction,
720                handle_id: 0,
721            });
722        }
723
724        let label_id = self.id_gen.next();
725        let mut block = Block::new(label_id);
726
727        let bool_type = return_type.with_scalar(crate::Scalar::BOOL);
728        let bool_type_id = self.get_numeric_type_id(bool_type);
729
730        let maybe_splat_const = |writer: &mut Self, const_id| match return_type {
731            NumericType::Scalar(_) => const_id,
732            NumericType::Vector { size, .. } => {
733                let constituent_ids = [const_id; crate::VectorSize::MAX];
734                writer.get_constant_composite(
735                    LookupType::Local(LocalType::Numeric(return_type)),
736                    &constituent_ids[..size as usize],
737                )
738            }
739            NumericType::Matrix { .. } => unreachable!(),
740        };
741
742        let const_zero_id = self.get_constant_scalar_with(0, scalar)?;
743        let composite_zero_id = maybe_splat_const(self, const_zero_id);
744        let rhs_eq_zero_id = self.id_gen.next();
745        block.body.push(Instruction::binary(
746            spirv::Op::IEqual,
747            bool_type_id,
748            rhs_eq_zero_id,
749            rhs_id,
750            composite_zero_id,
751        ));
752        let divisor_selector_id = match scalar.kind {
753            crate::ScalarKind::Sint => {
754                let (const_min_id, const_neg_one_id) = match scalar.width {
755                    2 => Ok((
756                        self.get_constant_scalar(crate::Literal::I16(i16::MIN)),
757                        self.get_constant_scalar(crate::Literal::I16(-1i16)),
758                    )),
759                    4 => Ok((
760                        self.get_constant_scalar(crate::Literal::I32(i32::MIN)),
761                        self.get_constant_scalar(crate::Literal::I32(-1i32)),
762                    )),
763                    8 => Ok((
764                        self.get_constant_scalar(crate::Literal::I64(i64::MIN)),
765                        self.get_constant_scalar(crate::Literal::I64(-1i64)),
766                    )),
767                    _ => Err(Error::Validation("Unexpected scalar width")),
768                }?;
769                let composite_min_id = maybe_splat_const(self, const_min_id);
770                let composite_neg_one_id = maybe_splat_const(self, const_neg_one_id);
771
772                let lhs_eq_int_min_id = self.id_gen.next();
773                block.body.push(Instruction::binary(
774                    spirv::Op::IEqual,
775                    bool_type_id,
776                    lhs_eq_int_min_id,
777                    lhs_id,
778                    composite_min_id,
779                ));
780                let rhs_eq_neg_one_id = self.id_gen.next();
781                block.body.push(Instruction::binary(
782                    spirv::Op::IEqual,
783                    bool_type_id,
784                    rhs_eq_neg_one_id,
785                    rhs_id,
786                    composite_neg_one_id,
787                ));
788                let lhs_eq_int_min_and_rhs_eq_neg_one_id = self.id_gen.next();
789                block.body.push(Instruction::binary(
790                    spirv::Op::LogicalAnd,
791                    bool_type_id,
792                    lhs_eq_int_min_and_rhs_eq_neg_one_id,
793                    lhs_eq_int_min_id,
794                    rhs_eq_neg_one_id,
795                ));
796                let rhs_eq_zero_or_lhs_eq_int_min_and_rhs_eq_neg_one_id = self.id_gen.next();
797                block.body.push(Instruction::binary(
798                    spirv::Op::LogicalOr,
799                    bool_type_id,
800                    rhs_eq_zero_or_lhs_eq_int_min_and_rhs_eq_neg_one_id,
801                    rhs_eq_zero_id,
802                    lhs_eq_int_min_and_rhs_eq_neg_one_id,
803                ));
804                rhs_eq_zero_or_lhs_eq_int_min_and_rhs_eq_neg_one_id
805            }
806            crate::ScalarKind::Uint => rhs_eq_zero_id,
807            _ => unreachable!(),
808        };
809
810        let const_one_id = self.get_constant_scalar_with(1, scalar)?;
811        let composite_one_id = maybe_splat_const(self, const_one_id);
812        let divisor_id = self.id_gen.next();
813        block.body.push(Instruction::select(
814            right_type_id,
815            divisor_id,
816            divisor_selector_id,
817            composite_one_id,
818            rhs_id,
819        ));
820        let return_id = if matches!(op, crate::BinaryOperator::Modulo)
821            && matches!(scalar.kind, crate::ScalarKind::Sint)
822        {
823            // `OpSRem` produces a poison result for negative operands in the Vulkan
824            // environment without the `maintenance8` feature. `OpSDiv` is not poisoned, so
825            // reconstruct the remainder as `a - b * (a / b)`, which is well-defined for
826            // negative operands. `divisor_id` is the zero/overflow-guarded divisor selected
827            // above, so the degenerate cases still match `OpSRem`'s guarded result (0).
828            let quotient_id = self.id_gen.next();
829            block.body.push(Instruction::binary(
830                spirv::Op::SDiv,
831                return_type_id,
832                quotient_id,
833                lhs_id,
834                divisor_id,
835            ));
836            let product_id = self.id_gen.next();
837            block.body.push(Instruction::binary(
838                spirv::Op::IMul,
839                return_type_id,
840                product_id,
841                quotient_id,
842                divisor_id,
843            ));
844            let remainder_id = self.id_gen.next();
845            block.body.push(Instruction::binary(
846                spirv::Op::ISub,
847                return_type_id,
848                remainder_id,
849                lhs_id,
850                product_id,
851            ));
852            remainder_id
853        } else {
854            let spv_op = match (op, scalar.kind) {
855                (crate::BinaryOperator::Divide, crate::ScalarKind::Sint) => spirv::Op::SDiv,
856                (crate::BinaryOperator::Divide, crate::ScalarKind::Uint) => spirv::Op::UDiv,
857                (crate::BinaryOperator::Modulo, crate::ScalarKind::Sint) => spirv::Op::SRem,
858                (crate::BinaryOperator::Modulo, crate::ScalarKind::Uint) => spirv::Op::UMod,
859                _ => unreachable!(),
860            };
861            let return_id = self.id_gen.next();
862            block.body.push(Instruction::binary(
863                spv_op,
864                return_type_id,
865                return_id,
866                lhs_id,
867                divisor_id,
868            ));
869            return_id
870        };
871
872        function.consume(block, Instruction::return_value(return_id));
873        function.to_words(&mut self.logical_layout.function_definitions);
874        Ok(())
875    }
876
877    /// Writes a wrapper function to convert from a std140 compat type to its
878    /// corresponding regular type.
879    ///
880    /// See [`Self::write_std140_compat_type_declaration`] for more details.
881    fn write_wrapped_convert_from_std140_compat_type(
882        &mut self,
883        ir_module: &crate::Module,
884        r#type: Handle<crate::Type>,
885    ) -> Result<(), Error> {
886        if !self.std140_compat_uniform_types.contains_key(&r#type) {
887            return Ok(());
888        }
889        // Check if we've already emitted this function.
890        let wrapped = WrappedFunction::ConvertFromStd140CompatType { r#type };
891        let function_id = match self.wrapped_functions.entry(wrapped) {
892            Entry::Occupied(_) => return Ok(()),
893            Entry::Vacant(e) => *e.insert(self.id_gen.next()),
894        };
895        if self.flags.contains(WriterFlags::DEBUG) {
896            self.debugs.push(Instruction::name(
897                function_id,
898                &format!("{:?}_from_std140", r#type.for_debug(&ir_module.types)),
899            ));
900        }
901        let param_type_id = self.std140_compat_uniform_types[&r#type].type_id;
902        let return_type_id = self.get_handle_type_id(r#type);
903
904        let mut function = Function::default();
905        let function_type_id = self.get_function_type(LookupFunctionType {
906            parameter_type_ids: vec![param_type_id],
907            return_type_id,
908        });
909        function.signature = Some(Instruction::function(
910            return_type_id,
911            function_id,
912            spirv::FunctionControl::empty(),
913            function_type_id,
914        ));
915        let param_id = self.id_gen.next();
916        function.parameters.push(FunctionArgument {
917            instruction: Instruction::function_parameter(param_type_id, param_id),
918            handle_id: 0,
919        });
920
921        let label_id = self.id_gen.next();
922        let mut block = Block::new(label_id);
923
924        let result_id = match ir_module.types[r#type].inner {
925            // Param is struct containing a vector member for each of the
926            // matrix's columns. Extract each column from the struct then
927            // composite into a matrix.
928            crate::TypeInner::Matrix {
929                columns,
930                rows: rows @ crate::VectorSize::Bi,
931                scalar,
932            } => {
933                let column_type_id =
934                    self.get_numeric_type_id(NumericType::Vector { size: rows, scalar });
935
936                let mut column_ids: ArrayVec<Word, 4> = ArrayVec::new();
937                for column in 0..columns as u32 {
938                    let column_id = self.id_gen.next();
939                    block.body.push(Instruction::composite_extract(
940                        column_type_id,
941                        column_id,
942                        param_id,
943                        &[column],
944                    ));
945                    column_ids.push(column_id);
946                }
947                let result_id = self.id_gen.next();
948                block.body.push(Instruction::composite_construct(
949                    return_type_id,
950                    result_id,
951                    &column_ids,
952                ));
953                result_id
954            }
955            // Param is an array where the base type is the std140 compatible
956            // type corresponding to `base`. Iterate through each element and
957            // call its conversion function, then composite into a new array.
958            crate::TypeInner::Array { base, size, .. } => {
959                // Ensure the conversion function for the array's base type is
960                // declared.
961                self.write_wrapped_convert_from_std140_compat_type(ir_module, base)?;
962
963                let element_type_id = self.get_handle_type_id(base);
964                let std140_info = self.std140_compat_uniform_types.get(&base);
965                let mut element_ids = Vec::new();
966                let size = match size.resolve(ir_module.to_ctx())? {
967                    crate::proc::IndexableLength::Known(size) => size,
968                    crate::proc::IndexableLength::Dynamic => {
969                        return Err(Error::Validation(
970                            "Uniform buffers cannot contain dynamic arrays",
971                        ))
972                    }
973                };
974                for i in 0..size {
975                    let std140_element_id = self.id_gen.next();
976                    let std140_element_type_id =
977                        std140_info.map_or(element_type_id, |info| info.type_id);
978                    block.body.push(Instruction::composite_extract(
979                        std140_element_type_id,
980                        std140_element_id,
981                        param_id,
982                        &[i],
983                    ));
984
985                    // Only call the conversion function if a compatibility mapping actually exists.
986                    let final_element_id = if std140_info.is_some() {
987                        let conversion_fn_id = self.wrapped_functions
988                            [&WrappedFunction::ConvertFromStd140CompatType { r#type: base }];
989                        let id = self.id_gen.next();
990                        block.body.push(Instruction::function_call(
991                            element_type_id,
992                            id,
993                            conversion_fn_id,
994                            &[std140_element_id],
995                        ));
996                        id
997                    } else {
998                        std140_element_type_id
999                    };
1000                    element_ids.push(final_element_id);
1001                }
1002                let result_id = self.id_gen.next();
1003                block.body.push(Instruction::composite_construct(
1004                    return_type_id,
1005                    result_id,
1006                    &element_ids,
1007                ));
1008                result_id
1009            }
1010            // Param is a struct where each two-row matrix member has been
1011            // decomposed in to separate vector members for each column.
1012            // Other members use their std140 compatible type if one exists, or
1013            // else their regular type. Iterate through each member, converting
1014            // or composing any matrices if required, then finally compose into
1015            // the struct.
1016            crate::TypeInner::Struct { ref members, .. } => {
1017                let mut member_ids = Vec::new();
1018                let mut next_index = 0;
1019                for member in members {
1020                    let member_id = self.id_gen.next();
1021                    let member_type_id = self.get_handle_type_id(member.ty);
1022                    match ir_module.types[member.ty].inner {
1023                        crate::TypeInner::Matrix {
1024                            columns,
1025                            rows: rows @ crate::VectorSize::Bi,
1026                            scalar,
1027                        } => {
1028                            let mut column_ids: ArrayVec<Word, 4> = ArrayVec::new();
1029                            let column_type_id = self
1030                                .get_numeric_type_id(NumericType::Vector { size: rows, scalar });
1031                            for _ in 0..columns as u32 {
1032                                let column_id = self.id_gen.next();
1033                                block.body.push(Instruction::composite_extract(
1034                                    column_type_id,
1035                                    column_id,
1036                                    param_id,
1037                                    &[next_index],
1038                                ));
1039                                column_ids.push(column_id);
1040                                next_index += 1;
1041                            }
1042                            block.body.push(Instruction::composite_construct(
1043                                member_type_id,
1044                                member_id,
1045                                &column_ids,
1046                            ));
1047                        }
1048                        _ => {
1049                            // Ensure the conversion function for the member's
1050                            // type is declared.
1051                            self.write_wrapped_convert_from_std140_compat_type(
1052                                ir_module, member.ty,
1053                            )?;
1054                            match self.std140_compat_uniform_types.get(&member.ty) {
1055                                Some(std140_type_info) => {
1056                                    let std140_member_id = self.id_gen.next();
1057                                    block.body.push(Instruction::composite_extract(
1058                                        std140_type_info.type_id,
1059                                        std140_member_id,
1060                                        param_id,
1061                                        &[next_index],
1062                                    ));
1063                                    let function_id = self.wrapped_functions
1064                                        [&WrappedFunction::ConvertFromStd140CompatType {
1065                                            r#type: member.ty,
1066                                        }];
1067                                    block.body.push(Instruction::function_call(
1068                                        member_type_id,
1069                                        member_id,
1070                                        function_id,
1071                                        &[std140_member_id],
1072                                    ));
1073                                    next_index += 1;
1074                                }
1075                                None => {
1076                                    block.body.push(Instruction::composite_extract(
1077                                        member_type_id,
1078                                        member_id,
1079                                        param_id,
1080                                        &[next_index],
1081                                    ));
1082                                    next_index += 1;
1083                                }
1084                            }
1085                        }
1086                    }
1087                    member_ids.push(member_id);
1088                }
1089                let result_id = self.id_gen.next();
1090                block.body.push(Instruction::composite_construct(
1091                    return_type_id,
1092                    result_id,
1093                    &member_ids,
1094                ));
1095                result_id
1096            }
1097            _ => unreachable!(),
1098        };
1099
1100        function.consume(block, Instruction::return_value(result_id));
1101        function.to_words(&mut self.logical_layout.function_definitions);
1102        Ok(())
1103    }
1104
1105    /// Writes a wrapper function to get an `OpTypeVector` column from an
1106    /// `OpTypeMatrix` with a dynamic index.
1107    ///
1108    /// This is used when accessing a column of a [`TypeInner::Matrix`] through
1109    /// a [`Uniform`] address space pointer. In such cases, the matrix will have
1110    /// been declared in SPIR-V using an alternative type where each column is a
1111    /// member of a containing struct. SPIR-V is unable to dynamically access
1112    /// struct members, so instead we load the matrix then call this function to
1113    /// access a column from the loaded value.
1114    ///
1115    /// [`TypeInner::Matrix`]: crate::TypeInner::Matrix
1116    /// [`Uniform`]: crate::AddressSpace::Uniform
1117    fn write_wrapped_matcx2_get_column(
1118        &mut self,
1119        ir_module: &crate::Module,
1120        r#type: Handle<crate::Type>,
1121    ) -> Result<(), Error> {
1122        let wrapped = WrappedFunction::MatCx2GetColumn { r#type };
1123        let function_id = match self.wrapped_functions.entry(wrapped) {
1124            Entry::Occupied(_) => return Ok(()),
1125            Entry::Vacant(e) => *e.insert(self.id_gen.next()),
1126        };
1127        if self.flags.contains(WriterFlags::DEBUG) {
1128            self.debugs.push(Instruction::name(
1129                function_id,
1130                &format!("{:?}_get_column", r#type.for_debug(&ir_module.types)),
1131            ));
1132        }
1133
1134        let crate::TypeInner::Matrix {
1135            columns,
1136            rows: rows @ crate::VectorSize::Bi,
1137            scalar,
1138        } = ir_module.types[r#type].inner
1139        else {
1140            unreachable!();
1141        };
1142
1143        let mut function = Function::default();
1144        let matrix_type_id = self.get_handle_type_id(r#type);
1145        let column_index_type_id = self.get_u32_type_id();
1146        let column_type_id = self.get_numeric_type_id(NumericType::Vector { size: rows, scalar });
1147        let matrix_param_id = self.id_gen.next();
1148        let column_index_param_id = self.id_gen.next();
1149        function.parameters.push(FunctionArgument {
1150            instruction: Instruction::function_parameter(matrix_type_id, matrix_param_id),
1151            handle_id: 0,
1152        });
1153        function.parameters.push(FunctionArgument {
1154            instruction: Instruction::function_parameter(
1155                column_index_type_id,
1156                column_index_param_id,
1157            ),
1158            handle_id: 0,
1159        });
1160        let function_type_id = self.get_function_type(LookupFunctionType {
1161            parameter_type_ids: vec![matrix_type_id, column_index_type_id],
1162            return_type_id: column_type_id,
1163        });
1164        function.signature = Some(Instruction::function(
1165            column_type_id,
1166            function_id,
1167            spirv::FunctionControl::empty(),
1168            function_type_id,
1169        ));
1170
1171        let label_id = self.id_gen.next();
1172        let mut block = Block::new(label_id);
1173
1174        // Create a switch case for each column in the matrix, where each case
1175        // extracts its column from the matrix. Finally we use OpPhi to return
1176        // the correct column.
1177        let merge_id = self.id_gen.next();
1178        block.body.push(Instruction::selection_merge(
1179            merge_id,
1180            spirv::SelectionControl::NONE,
1181        ));
1182        let cases = (0..columns as u32)
1183            .map(|i| super::instructions::Case {
1184                value: i,
1185                label_id: self.id_gen.next(),
1186            })
1187            .collect::<ArrayVec<_, 4>>();
1188
1189        // Which label we branch to in the default (column index out-of-bounds)
1190        // case depends on our bounds check policy.
1191        let default_id = match self.bounds_check_policies.index {
1192            // For `Restrict`, treat the same as the final column.
1193            crate::proc::BoundsCheckPolicy::Restrict => cases.last().unwrap().label_id,
1194            // For `ReadZeroSkipWrite`, branch directly to the merge block. This
1195            // will be handled in the `OpPhi` below to produce a zero value.
1196            crate::proc::BoundsCheckPolicy::ReadZeroSkipWrite => merge_id,
1197            // For `Unchecked` we create a new block containing an
1198            // `OpUnreachable`.
1199            crate::proc::BoundsCheckPolicy::Unchecked => self.id_gen.next(),
1200        };
1201        function.consume(
1202            block,
1203            Instruction::switch(column_index_param_id, default_id, &cases),
1204        );
1205
1206        // Emit a block for each case, and produce a list of variable and parent
1207        // block IDs that will be used in an `OpPhi` below to select the right
1208        // value.
1209        let mut var_parent_pairs = cases
1210            .into_iter()
1211            .map(|case| {
1212                let mut block = Block::new(case.label_id);
1213                let column_id = self.id_gen.next();
1214                block.body.push(Instruction::composite_extract(
1215                    column_type_id,
1216                    column_id,
1217                    matrix_param_id,
1218                    &[case.value],
1219                ));
1220                function.consume(block, Instruction::branch(merge_id));
1221                (column_id, case.label_id)
1222            })
1223            // Need capacity for up to 4 columns plus possibly a default case.
1224            .collect::<ArrayVec<_, 5>>();
1225
1226        // Emit a block or append the variable and parent `OpPhi` pair for the
1227        // column index out-of-bounds case, if required.
1228        match self.bounds_check_policies.index {
1229            // Don't need to do anything for `Restrict` as we have branched from
1230            // the final column case's block.
1231            crate::proc::BoundsCheckPolicy::Restrict => {}
1232            // For `ReadZeroSkipWrite` we have branched directly from the block
1233            // containing the `OpSwitch`. The `OpPhi` should produce a zero
1234            // value.
1235            crate::proc::BoundsCheckPolicy::ReadZeroSkipWrite => {
1236                var_parent_pairs.push((self.get_constant_null(column_type_id), label_id));
1237            }
1238            // For `Unchecked` create a new block containing `OpUnreachable`.
1239            // This does not need to be handled by the `OpPhi`.
1240            crate::proc::BoundsCheckPolicy::Unchecked => {
1241                function.consume(
1242                    Block::new(default_id),
1243                    Instruction::new(spirv::Op::Unreachable),
1244                );
1245            }
1246        }
1247
1248        let mut block = Block::new(merge_id);
1249        let result_id = self.id_gen.next();
1250        block.body.push(Instruction::phi(
1251            column_type_id,
1252            result_id,
1253            &var_parent_pairs,
1254        ));
1255
1256        function.consume(block, Instruction::return_value(result_id));
1257        function.to_words(&mut self.logical_layout.function_definitions);
1258        Ok(())
1259    }
1260
1261    fn write_function(
1262        &mut self,
1263        ir_function: &crate::Function,
1264        info: &FunctionInfo,
1265        ir_module: &crate::Module,
1266        mut interface: Option<FunctionInterface>,
1267        debug_info: &Option<DebugInfoInner>,
1268    ) -> Result<Word, Error> {
1269        self.write_wrapped_functions(ir_function, info, ir_module)?;
1270
1271        log::trace!("Generating code for {:?}", ir_function.name);
1272        let mut function = Function::default();
1273
1274        let prelude_id = self.id_gen.next();
1275        let mut prelude = Block::new(prelude_id);
1276        let mut ep_context = EntryPointContext {
1277            argument_ids: Vec::new(),
1278            results: Vec::new(),
1279            task_payload_variable_id: if let Some(ref i) = interface {
1280                i.task_payload.map(|a| self.global_variables[a].var_id)
1281            } else {
1282                None
1283            },
1284            mesh_state: None,
1285        };
1286
1287        let mut parameter_type_ids = Vec::with_capacity(ir_function.arguments.len());
1288
1289        let mut local_invocation_index_var_id = None;
1290        let mut local_invocation_index_id = None;
1291
1292        for argument in ir_function.arguments.iter() {
1293            let class = spirv::StorageClass::Input;
1294            let handle_ty = ir_module.types[argument.ty].inner.is_handle();
1295            let argument_type_id = if handle_ty {
1296                self.get_handle_pointer_type_id(argument.ty, spirv::StorageClass::UniformConstant)
1297            } else {
1298                self.get_handle_type_id(argument.ty)
1299            };
1300
1301            if let Some(ref mut iface) = interface {
1302                let id = if let Some(ref binding) = argument.binding {
1303                    let name = argument.name.as_deref();
1304
1305                    let varying_id = self.write_varying(
1306                        ir_module,
1307                        iface.stage,
1308                        class,
1309                        name,
1310                        argument.ty,
1311                        binding,
1312                    )?;
1313                    iface.varying_ids.push(varying_id);
1314                    let id = self.load_io_with_f16_polyfill(
1315                        &mut prelude.body,
1316                        varying_id,
1317                        argument_type_id,
1318                    );
1319                    if binding == &crate::Binding::BuiltIn(crate::BuiltIn::LocalInvocationIndex) {
1320                        local_invocation_index_id = Some(id);
1321                        local_invocation_index_var_id = Some(varying_id);
1322                    }
1323
1324                    id
1325                } else if let crate::TypeInner::Struct { ref members, .. } =
1326                    ir_module.types[argument.ty].inner
1327                {
1328                    let struct_id = self.id_gen.next();
1329                    let mut constituent_ids = Vec::with_capacity(members.len());
1330                    for member in members {
1331                        let type_id = self.get_handle_type_id(member.ty);
1332                        let name = member.name.as_deref();
1333                        let binding = member.binding.as_ref().unwrap();
1334                        let varying_id = self.write_varying(
1335                            ir_module,
1336                            iface.stage,
1337                            class,
1338                            name,
1339                            member.ty,
1340                            binding,
1341                        )?;
1342                        iface.varying_ids.push(varying_id);
1343                        let id =
1344                            self.load_io_with_f16_polyfill(&mut prelude.body, varying_id, type_id);
1345                        constituent_ids.push(id);
1346                        if binding == &crate::Binding::BuiltIn(crate::BuiltIn::LocalInvocationIndex)
1347                        {
1348                            local_invocation_index_id = Some(id);
1349                            local_invocation_index_var_id = Some(varying_id);
1350                        }
1351                    }
1352                    prelude.body.push(Instruction::composite_construct(
1353                        argument_type_id,
1354                        struct_id,
1355                        &constituent_ids,
1356                    ));
1357                    struct_id
1358                } else {
1359                    unreachable!("Missing argument binding on an entry point");
1360                };
1361                ep_context.argument_ids.push(id);
1362            } else {
1363                let argument_id = self.id_gen.next();
1364                let instruction = Instruction::function_parameter(argument_type_id, argument_id);
1365                if self.flags.contains(WriterFlags::DEBUG) {
1366                    if let Some(ref name) = argument.name {
1367                        self.debugs.push(Instruction::name(argument_id, name));
1368                    }
1369                }
1370                function.parameters.push(FunctionArgument {
1371                    instruction,
1372                    handle_id: if handle_ty {
1373                        let id = self.id_gen.next();
1374                        prelude.body.push(Instruction::load(
1375                            self.get_handle_type_id(argument.ty),
1376                            id,
1377                            argument_id,
1378                            None,
1379                        ));
1380                        id
1381                    } else {
1382                        0
1383                    },
1384                });
1385                parameter_type_ids.push(argument_type_id);
1386            };
1387        }
1388
1389        let return_type_id = match ir_function.result {
1390            Some(ref result) => {
1391                if let Some(ref mut iface) = interface {
1392                    let mut has_point_size = false;
1393                    let class = spirv::StorageClass::Output;
1394                    if let Some(ref binding) = result.binding {
1395                        has_point_size |=
1396                            *binding == crate::Binding::BuiltIn(crate::BuiltIn::PointSize);
1397                        let type_id = self.get_handle_type_id(result.ty);
1398                        let varying_id =
1399                            if *binding == crate::Binding::BuiltIn(crate::BuiltIn::MeshTaskSize) {
1400                                0
1401                            } else {
1402                                let varying_id = self.write_varying(
1403                                    ir_module,
1404                                    iface.stage,
1405                                    class,
1406                                    None,
1407                                    result.ty,
1408                                    binding,
1409                                )?;
1410                                iface.varying_ids.push(varying_id);
1411                                varying_id
1412                            };
1413                        ep_context.results.push(ResultMember {
1414                            id: varying_id,
1415                            type_id,
1416                            built_in: binding.to_built_in(),
1417                        });
1418                    } else if let crate::TypeInner::Struct { ref members, .. } =
1419                        ir_module.types[result.ty].inner
1420                    {
1421                        for member in members {
1422                            let type_id = self.get_handle_type_id(member.ty);
1423                            let name = member.name.as_deref();
1424                            let binding = member.binding.as_ref().unwrap();
1425                            has_point_size |=
1426                                *binding == crate::Binding::BuiltIn(crate::BuiltIn::PointSize);
1427                            // This isn't an actual builtin in SPIR-V. It can only appear as the
1428                            // output of a task shader and the output is used when writing the
1429                            // entry point return, in which case the id is ignored anyway.
1430                            let varying_id = if *binding
1431                                == crate::Binding::BuiltIn(crate::BuiltIn::MeshTaskSize)
1432                            {
1433                                0
1434                            } else {
1435                                let varying_id = self.write_varying(
1436                                    ir_module,
1437                                    iface.stage,
1438                                    class,
1439                                    name,
1440                                    member.ty,
1441                                    binding,
1442                                )?;
1443                                iface.varying_ids.push(varying_id);
1444                                varying_id
1445                            };
1446                            ep_context.results.push(ResultMember {
1447                                id: varying_id,
1448                                type_id,
1449                                built_in: binding.to_built_in(),
1450                            });
1451                        }
1452                    } else {
1453                        unreachable!("Missing result binding on an entry point");
1454                    }
1455
1456                    if self.flags.contains(WriterFlags::FORCE_POINT_SIZE)
1457                        && iface.stage == crate::ShaderStage::Vertex
1458                        && !has_point_size
1459                    {
1460                        // add point size artificially
1461                        let varying_id = self.id_gen.next();
1462                        let pointer_type_id = self.get_f32_pointer_type_id(class);
1463                        Instruction::variable(pointer_type_id, varying_id, class, None)
1464                            .to_words(&mut self.logical_layout.declarations);
1465                        self.decorate(
1466                            varying_id,
1467                            spirv::Decoration::BuiltIn,
1468                            &[spirv::BuiltIn::PointSize as u32],
1469                        );
1470                        iface.varying_ids.push(varying_id);
1471
1472                        let default_value_id = self.get_constant_scalar(crate::Literal::F32(1.0));
1473                        prelude
1474                            .body
1475                            .push(Instruction::store(varying_id, default_value_id, None));
1476                    }
1477                    if iface.stage == crate::ShaderStage::Task {
1478                        self.get_vec3u_type_id()
1479                    } else {
1480                        self.void_type
1481                    }
1482                } else {
1483                    self.get_handle_type_id(result.ty)
1484                }
1485            }
1486            None => self.void_type,
1487        };
1488
1489        if let Some(ref mut iface) = interface {
1490            if let Some(task_payload) = iface.task_payload {
1491                iface
1492                    .varying_ids
1493                    .push(self.global_variables[task_payload].var_id);
1494            }
1495            // The mesh output copy and the workgroup memory polyfill both read
1496            // `LocalInvocationIndex`, which an entry point may only declare once.
1497            if iface.mesh_info.is_some() && local_invocation_index_var_id.is_none() {
1498                let u32_type_id = self.get_u32_type_id();
1499                let pointer_type_id =
1500                    self.get_pointer_type_id(u32_type_id, spirv::StorageClass::Input);
1501                let var_id = self.id_gen.next();
1502                Instruction::variable(pointer_type_id, var_id, spirv::StorageClass::Input, None)
1503                    .to_words(&mut self.logical_layout.declarations);
1504                self.decorate(
1505                    var_id,
1506                    spirv::Decoration::BuiltIn,
1507                    &[spirv::BuiltIn::LocalInvocationIndex as u32],
1508                );
1509                iface.varying_ids.push(var_id);
1510
1511                let id = self.id_gen.next();
1512                prelude
1513                    .body
1514                    .push(Instruction::load(u32_type_id, id, var_id, None));
1515                local_invocation_index_var_id = Some(var_id);
1516                local_invocation_index_id = Some(id);
1517            }
1518            self.write_entry_point_mesh_shader_info(
1519                iface,
1520                local_invocation_index_var_id,
1521                ir_module,
1522                &mut ep_context,
1523            )?;
1524        }
1525
1526        let lookup_function_type = LookupFunctionType {
1527            parameter_type_ids,
1528            return_type_id,
1529        };
1530
1531        let function_id = self.id_gen.next();
1532        if self.flags.contains(WriterFlags::DEBUG) {
1533            if let Some(ref name) = ir_function.name {
1534                self.debugs.push(Instruction::name(function_id, name));
1535            }
1536        }
1537
1538        let function_type = self.get_function_type(lookup_function_type);
1539        function.signature = Some(Instruction::function(
1540            return_type_id,
1541            function_id,
1542            spirv::FunctionControl::empty(),
1543            function_type,
1544        ));
1545
1546        if interface.is_some() {
1547            function.entry_point_context = Some(ep_context);
1548        }
1549
1550        // fill up the `GlobalVariable::access_id`
1551        for gv in self.global_variables.iter_mut() {
1552            gv.reset_for_function();
1553        }
1554        for (handle, var) in ir_module.global_variables.iter() {
1555            if info[handle].is_empty() {
1556                continue;
1557            }
1558
1559            let mut gv = self.global_variables[handle].clone();
1560            if let Some(ref mut iface) = interface {
1561                // Have to include global variables in the interface
1562                if self.physical_layout.version >= 0x10400 && iface.task_payload != Some(handle) {
1563                    iface.varying_ids.push(gv.var_id);
1564                }
1565            }
1566
1567            match ir_module.types[var.ty].inner {
1568                // Any that are binding arrays we skip as we cannot load the array, we must load the result after indexing.
1569                crate::TypeInner::BindingArray { .. } => {
1570                    gv.access_id = gv.var_id;
1571                }
1572                _ => {
1573                    // Handle globals are pre-emitted and should be loaded automatically.
1574                    if var.space == crate::AddressSpace::Handle {
1575                        let var_type_id = self.get_handle_type_id(var.ty);
1576                        let id = self.id_gen.next();
1577                        prelude
1578                            .body
1579                            .push(Instruction::load(var_type_id, id, gv.var_id, None));
1580                        gv.access_id = gv.var_id;
1581                        gv.handle_id = id;
1582                    } else if global_needs_wrapper(ir_module, var) {
1583                        let class = map_storage_class(var.space);
1584                        let pointer_type_id = match self.std140_compat_uniform_types.get(&var.ty) {
1585                            Some(std140_type_info) if var.space == crate::AddressSpace::Uniform => {
1586                                self.get_pointer_type_id(std140_type_info.type_id, class)
1587                            }
1588                            _ => self.get_handle_pointer_type_id(var.ty, class),
1589                        };
1590                        let index_id = self.get_index_constant(0);
1591                        let id = self.id_gen.next();
1592                        prelude.body.push(Instruction::access_chain(
1593                            pointer_type_id,
1594                            id,
1595                            gv.var_id,
1596                            &[index_id],
1597                        ));
1598                        gv.access_id = id;
1599                    } else {
1600                        // by default, the variable ID is accessed as is
1601                        gv.access_id = gv.var_id;
1602                    };
1603                }
1604            }
1605
1606            // work around borrow checking in the presence of `self.xxx()` calls
1607            self.global_variables[handle] = gv;
1608        }
1609
1610        // Create a `BlockContext` for generating SPIR-V for the function's
1611        // body.
1612        let mut context = BlockContext {
1613            ir_module,
1614            ir_function,
1615            fun_info: info,
1616            function: &mut function,
1617            // Re-use the cached expression table from prior functions.
1618            cached: core::mem::take(&mut self.saved_cached),
1619
1620            // Steal the Writer's temp list for a bit.
1621            temp_list: core::mem::take(&mut self.temp_list),
1622            force_loop_bounding: self.force_loop_bounding,
1623            writer: self,
1624            expression_constness: super::ExpressionConstnessTracker::from_arena(
1625                &ir_function.expressions,
1626            ),
1627            ray_query_tracker_expr: crate::FastHashMap::default(),
1628        };
1629
1630        // fill up the pre-emitted and const expressions
1631        context.cached.reset(ir_function.expressions.len());
1632        for (handle, expr) in ir_function.expressions.iter() {
1633            if (expr.needs_pre_emit() && !matches!(*expr, crate::Expression::LocalVariable(_)))
1634                || context.expression_constness.is_const(handle)
1635            {
1636                context.cache_expression_value(handle, &mut prelude)?;
1637            }
1638        }
1639
1640        for (handle, variable) in ir_function.local_variables.iter() {
1641            let id = context.gen_id();
1642
1643            if context.writer.flags.contains(WriterFlags::DEBUG) {
1644                if let Some(ref name) = variable.name {
1645                    context.writer.debugs.push(Instruction::name(id, name));
1646                }
1647            }
1648
1649            let init_word = variable.init.map(|constant| context.cached[constant]);
1650            let pointer_type_id = context
1651                .writer
1652                .get_handle_pointer_type_id(variable.ty, spirv::StorageClass::Function);
1653            let instruction = Instruction::variable(
1654                pointer_type_id,
1655                id,
1656                spirv::StorageClass::Function,
1657                init_word.or_else(|| match ir_module.types[variable.ty].inner {
1658                    crate::TypeInner::RayQuery { .. } => None,
1659                    _ => {
1660                        let type_id = context.get_handle_type_id(variable.ty);
1661                        Some(context.writer.write_constant_null(type_id))
1662                    }
1663                }),
1664            );
1665
1666            context
1667                .function
1668                .variables
1669                .insert(handle, LocalVariable { id, instruction });
1670
1671            if let crate::TypeInner::RayQuery { .. } = ir_module.types[variable.ty].inner {
1672                // Don't refactor this into a struct: Although spirv itself allows opaque types in structs,
1673                // the vulkan environment for spirv does not. Putting ray queries into structs can cause
1674                // confusing bugs.
1675                let u32_type_id = context.writer.get_u32_type_id();
1676                let ptr_u32_type_id = context
1677                    .writer
1678                    .get_pointer_type_id(u32_type_id, spirv::StorageClass::Function);
1679                let tracker_id = context.gen_id();
1680                let tracker_init_id = context.writer.get_constant_scalar(crate::Literal::U32(
1681                    crate::back::RayQueryPoint::empty().bits(),
1682                ));
1683                let tracker_instruction = Instruction::variable(
1684                    ptr_u32_type_id,
1685                    tracker_id,
1686                    spirv::StorageClass::Function,
1687                    Some(tracker_init_id),
1688                );
1689
1690                context
1691                    .function
1692                    .ray_query_initialization_tracker_variables
1693                    .insert(
1694                        handle,
1695                        LocalVariable {
1696                            id: tracker_id,
1697                            instruction: tracker_instruction,
1698                        },
1699                    );
1700                let f32_type_id = context.writer.get_f32_type_id();
1701                let ptr_f32_type_id = context
1702                    .writer
1703                    .get_pointer_type_id(f32_type_id, spirv::StorageClass::Function);
1704                let t_max_tracker_id = context.gen_id();
1705                let t_max_tracker_init_id =
1706                    context.writer.get_constant_scalar(crate::Literal::F32(0.0));
1707                let t_max_tracker_instruction = Instruction::variable(
1708                    ptr_f32_type_id,
1709                    t_max_tracker_id,
1710                    spirv::StorageClass::Function,
1711                    Some(t_max_tracker_init_id),
1712                );
1713
1714                context.function.ray_query_t_max_tracker_variables.insert(
1715                    handle,
1716                    LocalVariable {
1717                        id: t_max_tracker_id,
1718                        instruction: t_max_tracker_instruction,
1719                    },
1720                );
1721            }
1722        }
1723
1724        for (handle, expr) in ir_function.expressions.iter() {
1725            match *expr {
1726                crate::Expression::LocalVariable(_) => {
1727                    // Cache the `OpVariable` instruction we generated above as
1728                    // the value of this expression.
1729                    context.cache_expression_value(handle, &mut prelude)?;
1730                }
1731                crate::Expression::Access { base, .. }
1732                | crate::Expression::AccessIndex { base, .. } => {
1733                    // Count references to `base` by `Access` and `AccessIndex`
1734                    // instructions. See `access_uses` for details.
1735                    *context.function.access_uses.entry(base).or_insert(0) += 1;
1736                }
1737                _ => {}
1738            }
1739        }
1740
1741        let next_id = context.gen_id();
1742
1743        context
1744            .function
1745            .consume(prelude, Instruction::branch(next_id));
1746
1747        let workgroup_vars_init_exit_block_id =
1748            match (context.writer.zero_initialize_workgroup_memory, interface) {
1749                (
1750                    super::ZeroInitializeWorkgroupMemoryMode::Polyfill,
1751                    Some(
1752                        ref mut interface @ FunctionInterface {
1753                            stage:
1754                                crate::ShaderStage::Compute
1755                                | crate::ShaderStage::Mesh
1756                                | crate::ShaderStage::Task,
1757                            ..
1758                        },
1759                    ),
1760                ) => context.writer.generate_workgroup_vars_init_block(
1761                    next_id,
1762                    ir_module,
1763                    info,
1764                    local_invocation_index_id,
1765                    interface,
1766                    context.function,
1767                ),
1768                _ => None,
1769            };
1770
1771        let main_id = if let Some(exit_id) = workgroup_vars_init_exit_block_id {
1772            exit_id
1773        } else {
1774            next_id
1775        };
1776
1777        context.write_function_body(main_id, debug_info.as_ref())?;
1778
1779        // Consume the `BlockContext`, ending its borrows and letting the
1780        // `Writer` steal back its cached expression table and temp_list.
1781        let BlockContext {
1782            cached, temp_list, ..
1783        } = context;
1784        self.saved_cached = cached;
1785        self.temp_list = temp_list;
1786
1787        function.to_words(&mut self.logical_layout.function_definitions);
1788
1789        if let Some(EntryPointContext {
1790            mesh_state: Some(ref mesh_state),
1791            ..
1792        }) = function.entry_point_context
1793        {
1794            self.write_mesh_shader_wrapper(mesh_state, function_id)
1795        } else if let Some(EntryPointContext {
1796            task_payload_variable_id: Some(tp),
1797            ..
1798        }) = function.entry_point_context
1799        {
1800            self.write_task_shader_wrapper(tp, function_id)
1801        } else {
1802            Ok(function_id)
1803        }
1804    }
1805
1806    fn write_execution_mode(
1807        &mut self,
1808        function_id: Word,
1809        mode: spirv::ExecutionMode,
1810    ) -> Result<(), Error> {
1811        //self.check(mode.required_capabilities())?;
1812        Instruction::execution_mode(function_id, mode, &[])
1813            .to_words(&mut self.logical_layout.execution_modes);
1814        Ok(())
1815    }
1816
1817    // TODO Move to instructions module
1818    fn write_entry_point(
1819        &mut self,
1820        entry_point: &crate::EntryPoint,
1821        info: &FunctionInfo,
1822        ir_module: &crate::Module,
1823        debug_info: &Option<DebugInfoInner>,
1824    ) -> Result<Instruction, Error> {
1825        let mut interface_ids = Vec::new();
1826
1827        let function_id = self.write_function(
1828            &entry_point.function,
1829            info,
1830            ir_module,
1831            Some(FunctionInterface {
1832                varying_ids: &mut interface_ids,
1833                stage: entry_point.stage,
1834                task_payload: entry_point.task_payload,
1835                mesh_info: entry_point.mesh_info.clone(),
1836                workgroup_size: entry_point.workgroup_size,
1837            }),
1838            debug_info,
1839        )?;
1840
1841        let exec_model = match entry_point.stage {
1842            crate::ShaderStage::Vertex => spirv::ExecutionModel::Vertex,
1843            crate::ShaderStage::Fragment => {
1844                self.write_execution_mode(function_id, spirv::ExecutionMode::OriginUpperLeft)?;
1845                match entry_point.early_depth_test {
1846                    Some(crate::EarlyDepthTest::Force) => {
1847                        self.write_execution_mode(
1848                            function_id,
1849                            spirv::ExecutionMode::EarlyFragmentTests,
1850                        )?;
1851                    }
1852                    Some(crate::EarlyDepthTest::Allow { conservative }) => {
1853                        // TODO: Consider emitting EarlyAndLateFragmentTestsAMD here, if available.
1854                        // https://github.khronos.org/SPIRV-Registry/extensions/AMD/SPV_AMD_shader_early_and_late_fragment_tests.html
1855                        // This permits early depth tests even if the shader writes to a storage
1856                        // binding
1857                        match conservative {
1858                            crate::ConservativeDepth::GreaterEqual => self.write_execution_mode(
1859                                function_id,
1860                                spirv::ExecutionMode::DepthGreater,
1861                            )?,
1862                            crate::ConservativeDepth::LessEqual => self.write_execution_mode(
1863                                function_id,
1864                                spirv::ExecutionMode::DepthLess,
1865                            )?,
1866                            crate::ConservativeDepth::Unchanged => self.write_execution_mode(
1867                                function_id,
1868                                spirv::ExecutionMode::DepthUnchanged,
1869                            )?,
1870                        }
1871                    }
1872                    None => {}
1873                }
1874                if let Some(ref result) = entry_point.function.result {
1875                    if contains_builtin(
1876                        result.binding.as_ref(),
1877                        result.ty,
1878                        &ir_module.types,
1879                        crate::BuiltIn::FragDepth,
1880                    ) {
1881                        self.write_execution_mode(
1882                            function_id,
1883                            spirv::ExecutionMode::DepthReplacing,
1884                        )?;
1885                    }
1886                }
1887                spirv::ExecutionModel::Fragment
1888            }
1889            crate::ShaderStage::Compute => {
1890                let execution_mode = spirv::ExecutionMode::LocalSize;
1891                Instruction::execution_mode(
1892                    function_id,
1893                    execution_mode,
1894                    &entry_point.workgroup_size,
1895                )
1896                .to_words(&mut self.logical_layout.execution_modes);
1897                spirv::ExecutionModel::GLCompute
1898            }
1899            crate::ShaderStage::Task => {
1900                let execution_mode = spirv::ExecutionMode::LocalSize;
1901                Instruction::execution_mode(
1902                    function_id,
1903                    execution_mode,
1904                    &entry_point.workgroup_size,
1905                )
1906                .to_words(&mut self.logical_layout.execution_modes);
1907                spirv::ExecutionModel::TaskEXT
1908            }
1909            crate::ShaderStage::Mesh => {
1910                let execution_mode = spirv::ExecutionMode::LocalSize;
1911                Instruction::execution_mode(
1912                    function_id,
1913                    execution_mode,
1914                    &entry_point.workgroup_size,
1915                )
1916                .to_words(&mut self.logical_layout.execution_modes);
1917                let mesh_info = entry_point.mesh_info.as_ref().unwrap();
1918                Instruction::execution_mode(
1919                    function_id,
1920                    match mesh_info.topology {
1921                        crate::MeshOutputTopology::Points => spirv::ExecutionMode::OutputPoints,
1922                        crate::MeshOutputTopology::Lines => spirv::ExecutionMode::OutputLinesEXT,
1923                        crate::MeshOutputTopology::Triangles => {
1924                            spirv::ExecutionMode::OutputTrianglesEXT
1925                        }
1926                    },
1927                    &[],
1928                )
1929                .to_words(&mut self.logical_layout.execution_modes);
1930                Instruction::execution_mode(
1931                    function_id,
1932                    spirv::ExecutionMode::OutputVertices,
1933                    core::slice::from_ref(&mesh_info.max_vertices),
1934                )
1935                .to_words(&mut self.logical_layout.execution_modes);
1936                Instruction::execution_mode(
1937                    function_id,
1938                    spirv::ExecutionMode::OutputPrimitivesEXT,
1939                    core::slice::from_ref(&mesh_info.max_primitives),
1940                )
1941                .to_words(&mut self.logical_layout.execution_modes);
1942                spirv::ExecutionModel::MeshEXT
1943            }
1944            crate::ShaderStage::RayGeneration => {
1945                self.require_any("ray tracing pipelines", &[spirv::Capability::RayTracingKHR])?;
1946                spirv::ExecutionModel::RayGenerationKHR
1947            }
1948            crate::ShaderStage::AnyHit => {
1949                self.require_any("ray tracing pipelines", &[spirv::Capability::RayTracingKHR])?;
1950                spirv::ExecutionModel::AnyHitKHR
1951            }
1952            crate::ShaderStage::ClosestHit => {
1953                self.require_any("ray tracing pipelines", &[spirv::Capability::RayTracingKHR])?;
1954                spirv::ExecutionModel::ClosestHitKHR
1955            }
1956            crate::ShaderStage::Miss => {
1957                self.require_any("ray tracing pipelines", &[spirv::Capability::RayTracingKHR])?;
1958                spirv::ExecutionModel::MissKHR
1959            }
1960        };
1961        //self.check(exec_model.required_capabilities())?;
1962
1963        Ok(Instruction::entry_point(
1964            exec_model,
1965            function_id,
1966            &entry_point.name,
1967            interface_ids.as_slice(),
1968        ))
1969    }
1970
1971    fn make_scalar(&mut self, id: Word, scalar: crate::Scalar) -> Instruction {
1972        use crate::ScalarKind as Sk;
1973
1974        let bits = (scalar.width * BITS_PER_BYTE) as u32;
1975        match scalar.kind {
1976            Sk::Sint | Sk::Uint => {
1977                let signedness = if scalar.kind == Sk::Sint {
1978                    super::instructions::Signedness::Signed
1979                } else {
1980                    super::instructions::Signedness::Unsigned
1981                };
1982                let cap = match bits {
1983                    8 => Some(spirv::Capability::Int8),
1984                    16 => Some(spirv::Capability::Int16),
1985                    64 => Some(spirv::Capability::Int64),
1986                    _ => None,
1987                };
1988                if let Some(cap) = cap {
1989                    self.capabilities_used.insert(cap);
1990                }
1991                if bits == 16 {
1992                    self.capabilities_used
1993                        .insert(spirv::Capability::StorageBuffer16BitAccess);
1994                    self.capabilities_used
1995                        .insert(spirv::Capability::UniformAndStorageBuffer16BitAccess);
1996                    if self.use_storage_input_output_16 {
1997                        self.capabilities_used
1998                            .insert(spirv::Capability::StorageInputOutput16);
1999                    }
2000                }
2001                Instruction::type_int(id, bits, signedness)
2002            }
2003            Sk::Float => {
2004                if bits == 64 {
2005                    self.capabilities_used.insert(spirv::Capability::Float64);
2006                }
2007                if bits == 16 {
2008                    self.capabilities_used.insert(spirv::Capability::Float16);
2009                    self.capabilities_used
2010                        .insert(spirv::Capability::StorageBuffer16BitAccess);
2011                    self.capabilities_used
2012                        .insert(spirv::Capability::UniformAndStorageBuffer16BitAccess);
2013                    if self.use_storage_input_output_16 {
2014                        self.capabilities_used
2015                            .insert(spirv::Capability::StorageInputOutput16);
2016                    }
2017                }
2018                Instruction::type_float(id, bits)
2019            }
2020            Sk::Bool => Instruction::type_bool(id),
2021            Sk::AbstractInt | Sk::AbstractFloat => {
2022                unreachable!("abstract types should never reach the backend");
2023            }
2024        }
2025    }
2026
2027    fn request_type_capabilities(&mut self, inner: &crate::TypeInner) -> Result<(), Error> {
2028        match *inner {
2029            crate::TypeInner::Image {
2030                dim,
2031                arrayed,
2032                class,
2033            } => {
2034                let sampled = match class {
2035                    crate::ImageClass::Sampled { .. } => true,
2036                    crate::ImageClass::Depth { .. } => true,
2037                    crate::ImageClass::Storage { format, .. } => {
2038                        self.request_image_format_capabilities(format.into())?;
2039                        false
2040                    }
2041                    crate::ImageClass::External => unimplemented!(),
2042                };
2043
2044                match dim {
2045                    crate::ImageDimension::D1 => {
2046                        if sampled {
2047                            self.require_any("sampled 1D images", &[spirv::Capability::Sampled1D])?;
2048                        } else {
2049                            self.require_any("1D storage images", &[spirv::Capability::Image1D])?;
2050                        }
2051                    }
2052                    crate::ImageDimension::Cube if arrayed => {
2053                        if sampled {
2054                            self.require_any(
2055                                "sampled cube array images",
2056                                &[spirv::Capability::SampledCubeArray],
2057                            )?;
2058                        } else {
2059                            self.require_any(
2060                                "cube array storage images",
2061                                &[spirv::Capability::ImageCubeArray],
2062                            )?;
2063                        }
2064                    }
2065                    _ => {}
2066                }
2067            }
2068            crate::TypeInner::AccelerationStructure { .. } => {
2069                self.require_any(
2070                    "Acceleration Structure",
2071                    // unless we use this conditional, the ray query snapshot
2072                    // tests pick the wrong capability
2073                    &[if self.has_ray_tracing_pipeline {
2074                        spirv::Capability::RayTracingKHR
2075                    } else {
2076                        spirv::Capability::RayQueryKHR
2077                    }],
2078                )?;
2079            }
2080            crate::TypeInner::RayQuery { .. } => {
2081                self.require_any("Ray Query", &[spirv::Capability::RayQueryKHR])?;
2082            }
2083            crate::TypeInner::Atomic(crate::Scalar { width: 8, kind: _ }) => {
2084                self.require_any("64 bit integer atomics", &[spirv::Capability::Int64Atomics])?;
2085            }
2086            crate::TypeInner::Atomic(crate::Scalar {
2087                width: 4,
2088                kind: crate::ScalarKind::Float,
2089            }) => {
2090                self.require_any(
2091                    "32 bit floating-point atomics",
2092                    &[spirv::Capability::AtomicFloat32AddEXT],
2093                )?;
2094                self.use_extension("SPV_EXT_shader_atomic_float_add");
2095            }
2096            // 16 bit floating-point support requires Float16 capability
2097            crate::TypeInner::Matrix {
2098                scalar: crate::Scalar::F16,
2099                ..
2100            }
2101            | crate::TypeInner::Vector {
2102                scalar: crate::Scalar::F16,
2103                ..
2104            }
2105            | crate::TypeInner::Scalar(crate::Scalar::F16) => {
2106                self.require_any("16 bit floating-point", &[spirv::Capability::Float16])?;
2107                self.use_extension("SPV_KHR_16bit_storage");
2108            }
2109            // 16 bit integer support requires Int16 capability
2110            crate::TypeInner::Vector {
2111                scalar:
2112                    crate::Scalar {
2113                        kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
2114                        width: 2,
2115                    },
2116                ..
2117            }
2118            | crate::TypeInner::Scalar(crate::Scalar {
2119                kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
2120                width: 2,
2121            }) => {
2122                self.require_any("16 bit integer", &[spirv::Capability::Int16])?;
2123                self.use_extension("SPV_KHR_16bit_storage");
2124            }
2125            // Cooperative types and ops
2126            crate::TypeInner::CooperativeMatrix { .. } => {
2127                self.require_any(
2128                    "cooperative matrix",
2129                    &[spirv::Capability::CooperativeMatrixKHR],
2130                )?;
2131                self.require_any("memory model", &[spirv::Capability::VulkanMemoryModel])?;
2132                self.use_extension("SPV_KHR_cooperative_matrix");
2133                self.use_extension("SPV_KHR_vulkan_memory_model");
2134            }
2135            _ => {}
2136        }
2137        Ok(())
2138    }
2139
2140    fn write_numeric_type_declaration_local(&mut self, id: Word, numeric: NumericType) {
2141        let instruction = match numeric {
2142            NumericType::Scalar(scalar) => self.make_scalar(id, scalar),
2143            NumericType::Vector { size, scalar } => {
2144                let scalar_id = self.get_numeric_type_id(NumericType::Scalar(scalar));
2145                Instruction::type_vector(id, scalar_id, size)
2146            }
2147            NumericType::Matrix {
2148                columns,
2149                rows,
2150                scalar,
2151            } => {
2152                let column_id =
2153                    self.get_numeric_type_id(NumericType::Vector { size: rows, scalar });
2154                Instruction::type_matrix(id, column_id, columns)
2155            }
2156        };
2157
2158        instruction.to_words(&mut self.logical_layout.declarations);
2159    }
2160
2161    fn write_cooperative_type_declaration_local(&mut self, id: Word, coop: CooperativeType) {
2162        let instruction = match coop {
2163            CooperativeType::Matrix {
2164                columns,
2165                rows,
2166                scalar,
2167                role,
2168            } => {
2169                let scalar_id =
2170                    self.get_localtype_id(LocalType::Numeric(NumericType::Scalar(scalar)));
2171                let scope_id = self.get_index_constant(spirv::Scope::Subgroup as u32);
2172                let columns_id = self.get_index_constant(columns as u32);
2173                let rows_id = self.get_index_constant(rows as u32);
2174                let role_id =
2175                    self.get_index_constant(spirv::CooperativeMatrixUse::from(role) as u32);
2176                Instruction::type_coop_matrix(id, scalar_id, scope_id, rows_id, columns_id, role_id)
2177            }
2178        };
2179
2180        instruction.to_words(&mut self.logical_layout.declarations);
2181    }
2182
2183    fn write_type_declaration_local(&mut self, id: Word, local_ty: LocalType) {
2184        let instruction = match local_ty {
2185            LocalType::Numeric(numeric) => {
2186                self.write_numeric_type_declaration_local(id, numeric);
2187                return;
2188            }
2189            LocalType::Cooperative(coop) => {
2190                self.write_cooperative_type_declaration_local(id, coop);
2191                return;
2192            }
2193            LocalType::Pointer { base, class } => Instruction::type_pointer(id, class, base),
2194            LocalType::Image(image) => {
2195                let local_type = LocalType::Numeric(NumericType::Scalar(image.sampled_type));
2196                let type_id = self.get_localtype_id(local_type);
2197                Instruction::type_image(id, type_id, image.dim, image.flags, image.image_format)
2198            }
2199            LocalType::Sampler => Instruction::type_sampler(id),
2200            LocalType::SampledImage { image_type_id } => {
2201                Instruction::type_sampled_image(id, image_type_id)
2202            }
2203            LocalType::BindingArray { base, size } => {
2204                let inner_ty = self.get_handle_type_id(base);
2205                let scalar_id = self.get_constant_scalar(crate::Literal::U32(size));
2206                Instruction::type_array(id, inner_ty, scalar_id)
2207            }
2208            LocalType::AccelerationStructure => Instruction::type_acceleration_structure(id),
2209            LocalType::RayQuery => Instruction::type_ray_query(id),
2210        };
2211
2212        instruction.to_words(&mut self.logical_layout.declarations);
2213    }
2214
2215    fn write_type_declaration_arena(
2216        &mut self,
2217        module: &crate::Module,
2218        handle: Handle<crate::Type>,
2219    ) -> Result<Word, Error> {
2220        let ty = &module.types[handle];
2221        // If it's a type that needs SPIR-V capabilities, request them now.
2222        // This needs to happen regardless of the LocalType lookup succeeding,
2223        // because some types which map to the same LocalType have different
2224        // capability requirements. See https://github.com/gfx-rs/wgpu/issues/5569
2225        self.request_type_capabilities(&ty.inner)?;
2226        let id = if let Some(local) = self.localtype_from_inner(&ty.inner) {
2227            // This type can be represented as a `LocalType`, so check if we've
2228            // already written an instruction for it. If not, do so now, with
2229            // `write_type_declaration_local`.
2230            match self.lookup_type.entry(LookupType::Local(local)) {
2231                // We already have an id for this `LocalType`.
2232                Entry::Occupied(e) => *e.get(),
2233
2234                // It's a type we haven't seen before.
2235                Entry::Vacant(e) => {
2236                    let id = self.id_gen.next();
2237                    e.insert(id);
2238
2239                    self.write_type_declaration_local(id, local);
2240
2241                    id
2242                }
2243            }
2244        } else {
2245            use spirv::Decoration;
2246
2247            let id = self.id_gen.next();
2248            let instruction = match ty.inner {
2249                crate::TypeInner::Array { base, size, stride } => {
2250                    self.decorate(id, Decoration::ArrayStride, &[stride]);
2251
2252                    let type_id = self.get_handle_type_id(base);
2253                    match size.resolve(module.to_ctx())? {
2254                        crate::proc::IndexableLength::Known(length) => {
2255                            let length_id = self.get_index_constant(length);
2256                            Instruction::type_array(id, type_id, length_id)
2257                        }
2258                        crate::proc::IndexableLength::Dynamic => {
2259                            Instruction::type_runtime_array(id, type_id)
2260                        }
2261                    }
2262                }
2263                crate::TypeInner::BindingArray { base, size } => {
2264                    let type_id = self.get_handle_type_id(base);
2265                    match size.resolve(module.to_ctx())? {
2266                        crate::proc::IndexableLength::Known(length) => {
2267                            let length_id = self.get_index_constant(length);
2268                            Instruction::type_array(id, type_id, length_id)
2269                        }
2270                        crate::proc::IndexableLength::Dynamic => {
2271                            Instruction::type_runtime_array(id, type_id)
2272                        }
2273                    }
2274                }
2275                crate::TypeInner::Struct {
2276                    ref members,
2277                    span: _,
2278                } => {
2279                    let mut has_runtime_array = false;
2280                    let mut member_ids = Vec::with_capacity(members.len());
2281                    for (index, member) in members.iter().enumerate() {
2282                        let member_ty = &module.types[member.ty];
2283                        match member_ty.inner {
2284                            crate::TypeInner::Array {
2285                                base: _,
2286                                size: crate::ArraySize::Dynamic,
2287                                stride: _,
2288                            } => {
2289                                has_runtime_array = true;
2290                            }
2291                            _ => (),
2292                        }
2293                        self.decorate_struct_member(id, index, member, &module.types)?;
2294                        let member_id = self.get_handle_type_id(member.ty);
2295                        member_ids.push(member_id);
2296                    }
2297                    if has_runtime_array {
2298                        self.decorate(id, Decoration::Block, &[]);
2299                    }
2300                    Instruction::type_struct(id, member_ids.as_slice())
2301                }
2302
2303                // These all have TypeLocal representations, so they should have been
2304                // handled by `write_type_declaration_local` above.
2305                crate::TypeInner::Scalar(_)
2306                | crate::TypeInner::Atomic(_)
2307                | crate::TypeInner::Vector { .. }
2308                | crate::TypeInner::Matrix { .. }
2309                | crate::TypeInner::CooperativeMatrix { .. }
2310                | crate::TypeInner::Pointer { .. }
2311                | crate::TypeInner::ValuePointer { .. }
2312                | crate::TypeInner::Image { .. }
2313                | crate::TypeInner::Sampler { .. }
2314                | crate::TypeInner::AccelerationStructure { .. }
2315                | crate::TypeInner::RayQuery { .. } => unreachable!(),
2316            };
2317
2318            instruction.to_words(&mut self.logical_layout.declarations);
2319            id
2320        };
2321
2322        // Add this handle as a new alias for that type.
2323        self.lookup_type.insert(LookupType::Handle(handle), id);
2324
2325        if self.flags.contains(WriterFlags::DEBUG) {
2326            if let Some(ref name) = ty.name {
2327                self.debugs.push(Instruction::name(id, name));
2328            }
2329        }
2330
2331        Ok(id)
2332    }
2333
2334    /// Writes a std140 layout compatible type declaration for a type. Returns
2335    /// the ID of the declared type, or None if no declaration is required.
2336    ///
2337    /// This should be called for any type for which there exists a
2338    /// [`GlobalVariable`] in the [`Uniform`] address space. If the type already
2339    /// adheres to std140 layout rules it will return without declaring any
2340    /// types. If the type contains another type which requires a std140
2341    /// compatible type declaration, it will recursively call itself.
2342    ///
2343    /// When `handle` refers to a [`TypeInner::Matrix`] with 2 rows, the
2344    /// declared type will be an `OpTypeStruct` containing an `OpVector` for
2345    /// each of the matrix's columns.
2346    ///
2347    /// When `handle` refers to a [`TypeInner::Array`] whose base type is a
2348    /// matrix with 2 rows, this will declare an `OpTypeArray` whose element
2349    /// type is the matrix's corresponding std140 compatible type.
2350    ///
2351    /// When `handle` refers to a [`TypeInner::Struct`] and any of its members
2352    /// require a std140 compatible type declaration, this will declare a new
2353    /// struct with the following rules:
2354    /// * Struct or array members will be declared with their std140 compatible
2355    ///   type declaration, if one is required.
2356    /// * Two-row matrix members will have each of their columns hoisted
2357    ///   directly into the struct as 2-component vector members.
2358    /// * All other members will be declared with their normal type.
2359    ///
2360    /// Note that this means the Naga IR index of a struct member may not match
2361    /// the index in the generated SPIR-V. The mapping can be obtained via
2362    /// `Std140TypeInfo::member_indices`.
2363    ///
2364    /// [`GlobalVariable`]: crate::GlobalVariable
2365    /// [`Uniform`]: crate::AddressSpace::Uniform
2366    /// [`TypeInner::Matrix`]: crate::TypeInner::Matrix
2367    /// [`TypeInner::Array`]: crate::TypeInner::Array
2368    /// [`TypeInner::Struct`]: crate::TypeInner::Struct
2369    fn write_std140_compat_type_declaration(
2370        &mut self,
2371        module: &crate::Module,
2372        handle: Handle<crate::Type>,
2373    ) -> Result<Option<Word>, Error> {
2374        if let Some(std140_type_info) = self.std140_compat_uniform_types.get(&handle) {
2375            return Ok(Some(std140_type_info.type_id));
2376        }
2377
2378        let type_inner = &module.types[handle].inner;
2379        let std140_type_id = match *type_inner {
2380            crate::TypeInner::Matrix {
2381                columns,
2382                rows: rows @ crate::VectorSize::Bi,
2383                scalar,
2384            } => {
2385                let std140_type_id = self.id_gen.next();
2386                let mut member_type_ids: ArrayVec<Word, 4> = ArrayVec::new();
2387                let column_type_id =
2388                    self.get_numeric_type_id(NumericType::Vector { size: rows, scalar });
2389                for column in 0..columns as u32 {
2390                    member_type_ids.push(column_type_id);
2391                    self.annotations.push(Instruction::member_decorate(
2392                        std140_type_id,
2393                        column,
2394                        spirv::Decoration::Offset,
2395                        &[column * rows as u32 * scalar.width as u32],
2396                    ));
2397                    if self.flags.contains(WriterFlags::DEBUG) {
2398                        self.debugs.push(Instruction::member_name(
2399                            std140_type_id,
2400                            column,
2401                            &format!("col{column}"),
2402                        ));
2403                    }
2404                }
2405                Instruction::type_struct(std140_type_id, &member_type_ids)
2406                    .to_words(&mut self.logical_layout.declarations);
2407                self.std140_compat_uniform_types.insert(
2408                    handle,
2409                    Std140CompatTypeInfo {
2410                        type_id: std140_type_id,
2411                        member_indices: Vec::new(),
2412                    },
2413                );
2414                Some(std140_type_id)
2415            }
2416            crate::TypeInner::Array { base, size, stride } => {
2417                match self.write_std140_compat_type_declaration(module, base)? {
2418                    Some(std140_base_type_id) => {
2419                        let std140_type_id = self.id_gen.next();
2420                        self.decorate(std140_type_id, spirv::Decoration::ArrayStride, &[stride]);
2421                        let instruction = match size.resolve(module.to_ctx())? {
2422                            crate::proc::IndexableLength::Known(length) => {
2423                                let length_id = self.get_index_constant(length);
2424                                Instruction::type_array(
2425                                    std140_type_id,
2426                                    std140_base_type_id,
2427                                    length_id,
2428                                )
2429                            }
2430                            crate::proc::IndexableLength::Dynamic => {
2431                                unreachable!()
2432                            }
2433                        };
2434                        instruction.to_words(&mut self.logical_layout.declarations);
2435                        self.std140_compat_uniform_types.insert(
2436                            handle,
2437                            Std140CompatTypeInfo {
2438                                type_id: std140_type_id,
2439                                member_indices: Vec::new(),
2440                            },
2441                        );
2442                        Some(std140_type_id)
2443                    }
2444                    None => None,
2445                }
2446            }
2447            crate::TypeInner::Struct { ref members, .. } => {
2448                let mut needs_std140_type = false;
2449                for member in members {
2450                    match module.types[member.ty].inner {
2451                        // We don't need to write a std140 type for the matrix itself as
2452                        // it will be decomposed into the parent struct. As a result, the
2453                        // struct does need a std140 type, however.
2454                        crate::TypeInner::Matrix {
2455                            rows: crate::VectorSize::Bi,
2456                            ..
2457                        } => needs_std140_type = true,
2458                        // If an array member needs a std140 type, because it is an array
2459                        // (of an array, etc) of `matCx2`s, then the struct also needs
2460                        // a std140 type which uses the std140 type for this member.
2461                        crate::TypeInner::Array { .. }
2462                            if self
2463                                .write_std140_compat_type_declaration(module, member.ty)?
2464                                .is_some() =>
2465                        {
2466                            needs_std140_type = true;
2467                        }
2468                        _ => {}
2469                    }
2470                }
2471
2472                if needs_std140_type {
2473                    let std140_type_id = self.id_gen.next();
2474                    let mut member_ids = Vec::new();
2475                    let mut member_indices = Vec::new();
2476                    let mut next_index = 0;
2477
2478                    for member in members {
2479                        member_indices.push(next_index);
2480                        match module.types[member.ty].inner {
2481                            crate::TypeInner::Matrix {
2482                                columns,
2483                                rows: rows @ crate::VectorSize::Bi,
2484                                scalar,
2485                            } => {
2486                                let vector_type_id =
2487                                    self.get_numeric_type_id(NumericType::Vector {
2488                                        size: rows,
2489                                        scalar,
2490                                    });
2491                                for column in 0..columns as u32 {
2492                                    self.annotations.push(Instruction::member_decorate(
2493                                        std140_type_id,
2494                                        next_index,
2495                                        spirv::Decoration::Offset,
2496                                        &[member.offset
2497                                            + column * rows as u32 * scalar.width as u32],
2498                                    ));
2499                                    if self.flags.contains(WriterFlags::DEBUG) {
2500                                        if let Some(ref name) = member.name {
2501                                            self.debugs.push(Instruction::member_name(
2502                                                std140_type_id,
2503                                                next_index,
2504                                                &format!("{name}_col{column}"),
2505                                            ));
2506                                        }
2507                                    }
2508                                    member_ids.push(vector_type_id);
2509                                    next_index += 1;
2510                                }
2511                            }
2512                            _ => {
2513                                let member_id =
2514                                    match self.std140_compat_uniform_types.get(&member.ty) {
2515                                        Some(std140_member_type_info) => {
2516                                            self.annotations.push(Instruction::member_decorate(
2517                                                std140_type_id,
2518                                                next_index,
2519                                                spirv::Decoration::Offset,
2520                                                &[member.offset],
2521                                            ));
2522                                            if self.flags.contains(WriterFlags::DEBUG) {
2523                                                if let Some(ref name) = member.name {
2524                                                    self.debugs.push(Instruction::member_name(
2525                                                        std140_type_id,
2526                                                        next_index,
2527                                                        name,
2528                                                    ));
2529                                                }
2530                                            }
2531                                            std140_member_type_info.type_id
2532                                        }
2533                                        None => {
2534                                            self.decorate_struct_member(
2535                                                std140_type_id,
2536                                                next_index as usize,
2537                                                member,
2538                                                &module.types,
2539                                            )?;
2540                                            self.get_handle_type_id(member.ty)
2541                                        }
2542                                    };
2543                                member_ids.push(member_id);
2544                                next_index += 1;
2545                            }
2546                        }
2547                    }
2548
2549                    Instruction::type_struct(std140_type_id, &member_ids)
2550                        .to_words(&mut self.logical_layout.declarations);
2551                    self.std140_compat_uniform_types.insert(
2552                        handle,
2553                        Std140CompatTypeInfo {
2554                            type_id: std140_type_id,
2555                            member_indices,
2556                        },
2557                    );
2558                    Some(std140_type_id)
2559                } else {
2560                    None
2561                }
2562            }
2563            _ => None,
2564        };
2565
2566        if let Some(std140_type_id) = std140_type_id {
2567            if self.flags.contains(WriterFlags::DEBUG) {
2568                let name = format!("std140_{:?}", handle.for_debug(&module.types));
2569                self.debugs.push(Instruction::name(std140_type_id, &name));
2570            }
2571        }
2572        Ok(std140_type_id)
2573    }
2574
2575    fn request_image_format_capabilities(
2576        &mut self,
2577        format: spirv::ImageFormat,
2578    ) -> Result<(), Error> {
2579        use spirv::ImageFormat as If;
2580        match format {
2581            If::Rg32f
2582            | If::Rg16f
2583            | If::R11fG11fB10f
2584            | If::R16f
2585            | If::Rgba16
2586            | If::Rgb10A2
2587            | If::Rg16
2588            | If::Rg8
2589            | If::R16
2590            | If::R8
2591            | If::Rgba16Snorm
2592            | If::Rg16Snorm
2593            | If::Rg8Snorm
2594            | If::R16Snorm
2595            | If::R8Snorm
2596            | If::Rg32i
2597            | If::Rg16i
2598            | If::Rg8i
2599            | If::R16i
2600            | If::R8i
2601            | If::Rgb10a2ui
2602            | If::Rg32ui
2603            | If::Rg16ui
2604            | If::Rg8ui
2605            | If::R16ui
2606            | If::R8ui => self.require_any(
2607                "storage image format",
2608                &[spirv::Capability::StorageImageExtendedFormats],
2609            ),
2610            If::R64ui | If::R64i => {
2611                self.use_extension("SPV_EXT_shader_image_int64");
2612                self.require_any(
2613                    "64-bit integer storage image format",
2614                    &[spirv::Capability::Int64ImageEXT],
2615                )
2616            }
2617            If::Unknown
2618            | If::Rgba32f
2619            | If::Rgba16f
2620            | If::R32f
2621            | If::Rgba8
2622            | If::Rgba8Snorm
2623            | If::Rgba32i
2624            | If::Rgba16i
2625            | If::Rgba8i
2626            | If::R32i
2627            | If::Rgba32ui
2628            | If::Rgba16ui
2629            | If::Rgba8ui
2630            | If::R32ui => Ok(()),
2631        }
2632    }
2633
2634    pub(super) fn get_index_constant(&mut self, index: Word) -> Word {
2635        self.get_constant_scalar(crate::Literal::U32(index))
2636    }
2637
2638    pub(super) fn get_constant_scalar_with(
2639        &mut self,
2640        value: u8,
2641        scalar: crate::Scalar,
2642    ) -> Result<Word, Error> {
2643        Ok(
2644            self.get_constant_scalar(crate::Literal::new(value, scalar).ok_or(
2645                Error::Validation("Unexpected kind and/or width for Literal"),
2646            )?),
2647        )
2648    }
2649
2650    pub(super) fn get_constant_scalar(&mut self, value: crate::Literal) -> Word {
2651        let scalar = CachedConstant::Literal(value.into());
2652        if let Some(&id) = self.cached_constants.get(&scalar) {
2653            return id;
2654        }
2655        let id = self.id_gen.next();
2656        self.write_constant_scalar(id, &value, None);
2657        self.cached_constants.insert(scalar, id);
2658        id
2659    }
2660
2661    fn write_constant_scalar(
2662        &mut self,
2663        id: Word,
2664        value: &crate::Literal,
2665        debug_name: Option<&String>,
2666    ) {
2667        if self.flags.contains(WriterFlags::DEBUG) {
2668            if let Some(name) = debug_name {
2669                self.debugs.push(Instruction::name(id, name));
2670            }
2671        }
2672        let type_id = self.get_numeric_type_id(NumericType::Scalar(value.scalar()));
2673        let instruction = match *value {
2674            crate::Literal::F64(value) => {
2675                let bits = value.to_bits();
2676                Instruction::constant_64bit(type_id, id, bits as u32, (bits >> 32) as u32)
2677            }
2678            crate::Literal::F32(value) => Instruction::constant_32bit(type_id, id, value.to_bits()),
2679            crate::Literal::F16(value) => {
2680                let low = value.to_bits();
2681                Instruction::constant_16bit(type_id, id, low as u32)
2682            }
2683            crate::Literal::U16(value) => Instruction::constant_16bit(type_id, id, value as u32),
2684            crate::Literal::I16(value) => {
2685                // Sign-extend into the 32-bit word so that `spirv-as` can
2686                // round-trip the disassembly (it expects signed values for
2687                // signed types).
2688                Instruction::constant_16bit(type_id, id, value as i32 as u32)
2689            }
2690            crate::Literal::U32(value) => Instruction::constant_32bit(type_id, id, value),
2691            crate::Literal::I32(value) => Instruction::constant_32bit(type_id, id, value as u32),
2692            crate::Literal::U64(value) => {
2693                Instruction::constant_64bit(type_id, id, value as u32, (value >> 32) as u32)
2694            }
2695            crate::Literal::I64(value) => {
2696                Instruction::constant_64bit(type_id, id, value as u32, (value >> 32) as u32)
2697            }
2698            crate::Literal::Bool(true) => Instruction::constant_true(type_id, id),
2699            crate::Literal::Bool(false) => Instruction::constant_false(type_id, id),
2700            crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
2701                unreachable!("Abstract types should not appear in IR presented to backends");
2702            }
2703        };
2704
2705        instruction.to_words(&mut self.logical_layout.declarations);
2706    }
2707
2708    pub(super) fn get_constant_composite(
2709        &mut self,
2710        ty: LookupType,
2711        constituent_ids: &[Word],
2712    ) -> Word {
2713        let composite = CachedConstant::Composite {
2714            ty,
2715            constituent_ids: constituent_ids.to_vec(),
2716        };
2717        if let Some(&id) = self.cached_constants.get(&composite) {
2718            return id;
2719        }
2720        let id = self.id_gen.next();
2721        self.write_constant_composite(id, ty, constituent_ids, None);
2722        self.cached_constants.insert(composite, id);
2723        id
2724    }
2725
2726    fn write_constant_composite(
2727        &mut self,
2728        id: Word,
2729        ty: LookupType,
2730        constituent_ids: &[Word],
2731        debug_name: Option<&String>,
2732    ) {
2733        if self.flags.contains(WriterFlags::DEBUG) {
2734            if let Some(name) = debug_name {
2735                self.debugs.push(Instruction::name(id, name));
2736            }
2737        }
2738        let type_id = self.get_type_id(ty);
2739        Instruction::constant_composite(type_id, id, constituent_ids)
2740            .to_words(&mut self.logical_layout.declarations);
2741    }
2742
2743    pub(super) fn get_constant_null(&mut self, type_id: Word) -> Word {
2744        let null = CachedConstant::ZeroValue(type_id);
2745        if let Some(&id) = self.cached_constants.get(&null) {
2746            return id;
2747        }
2748        let id = self.write_constant_null(type_id);
2749        self.cached_constants.insert(null, id);
2750        id
2751    }
2752
2753    pub(super) fn write_constant_null(&mut self, type_id: Word) -> Word {
2754        let null_id = self.id_gen.next();
2755        Instruction::constant_null(type_id, null_id)
2756            .to_words(&mut self.logical_layout.declarations);
2757        null_id
2758    }
2759
2760    fn write_constant_expr(
2761        &mut self,
2762        handle: Handle<crate::Expression>,
2763        ir_module: &crate::Module,
2764        mod_info: &ModuleInfo,
2765    ) -> Result<Word, Error> {
2766        let id = match ir_module.global_expressions[handle] {
2767            crate::Expression::Literal(literal) => self.get_constant_scalar(literal),
2768            crate::Expression::Constant(constant) => {
2769                let constant = &ir_module.constants[constant];
2770                self.constant_ids[constant.init]
2771            }
2772            crate::Expression::ZeroValue(ty) => {
2773                let type_id = self.get_handle_type_id(ty);
2774                self.get_constant_null(type_id)
2775            }
2776            crate::Expression::Compose { ty, ref components } => {
2777                let component_ids: Vec<_> = crate::proc::flatten_compose(
2778                    ty,
2779                    components,
2780                    &ir_module.global_expressions,
2781                    &ir_module.types,
2782                )
2783                .map(|component| self.constant_ids[component])
2784                .collect();
2785                self.get_constant_composite(LookupType::Handle(ty), component_ids.as_slice())
2786            }
2787            crate::Expression::Splat { size, value } => {
2788                let value_id = self.constant_ids[value];
2789                let component_ids = &[value_id; 4][..size as usize];
2790
2791                let ty = self.get_expression_lookup_type(&mod_info[handle]);
2792
2793                self.get_constant_composite(ty, component_ids)
2794            }
2795            _ => {
2796                return Err(Error::Override);
2797            }
2798        };
2799
2800        self.constant_ids[handle] = id;
2801
2802        Ok(id)
2803    }
2804
2805    pub(super) fn write_control_barrier(
2806        &mut self,
2807        flags: crate::Barrier,
2808        body: &mut Vec<Instruction>,
2809    ) {
2810        let memory_scope = if flags.contains(crate::Barrier::STORAGE) {
2811            spirv::Scope::Device
2812        } else if flags.contains(crate::Barrier::SUB_GROUP) {
2813            spirv::Scope::Subgroup
2814        } else {
2815            spirv::Scope::Workgroup
2816        };
2817        let mut semantics = spirv::MemorySemantics::ACQUIRE_RELEASE;
2818        semantics.set(
2819            spirv::MemorySemantics::UNIFORM_MEMORY,
2820            flags.contains(crate::Barrier::STORAGE),
2821        );
2822        semantics.set(
2823            spirv::MemorySemantics::WORKGROUP_MEMORY,
2824            flags.contains(crate::Barrier::WORK_GROUP),
2825        );
2826        semantics.set(
2827            spirv::MemorySemantics::SUBGROUP_MEMORY,
2828            flags.contains(crate::Barrier::SUB_GROUP),
2829        );
2830        semantics.set(
2831            spirv::MemorySemantics::IMAGE_MEMORY,
2832            flags.contains(crate::Barrier::TEXTURE),
2833        );
2834        let exec_scope_id = if flags.contains(crate::Barrier::SUB_GROUP) {
2835            self.get_index_constant(spirv::Scope::Subgroup as u32)
2836        } else {
2837            self.get_index_constant(spirv::Scope::Workgroup as u32)
2838        };
2839        let mem_scope_id = self.get_index_constant(memory_scope as u32);
2840        let semantics_id = self.get_index_constant(semantics.bits());
2841        body.push(Instruction::control_barrier(
2842            exec_scope_id,
2843            mem_scope_id,
2844            semantics_id,
2845        ));
2846    }
2847
2848    pub(super) fn write_memory_barrier(&mut self, flags: crate::Barrier, block: &mut Block) {
2849        let mut semantics = spirv::MemorySemantics::ACQUIRE_RELEASE;
2850        semantics.set(
2851            spirv::MemorySemantics::UNIFORM_MEMORY,
2852            flags.contains(crate::Barrier::STORAGE),
2853        );
2854        semantics.set(
2855            spirv::MemorySemantics::WORKGROUP_MEMORY,
2856            flags.contains(crate::Barrier::WORK_GROUP),
2857        );
2858        semantics.set(
2859            spirv::MemorySemantics::SUBGROUP_MEMORY,
2860            flags.contains(crate::Barrier::SUB_GROUP),
2861        );
2862        semantics.set(
2863            spirv::MemorySemantics::IMAGE_MEMORY,
2864            flags.contains(crate::Barrier::TEXTURE),
2865        );
2866        let mem_scope_id = if flags.contains(crate::Barrier::STORAGE) {
2867            self.get_index_constant(spirv::Scope::Device as u32)
2868        } else if flags.contains(crate::Barrier::SUB_GROUP) {
2869            self.get_index_constant(spirv::Scope::Subgroup as u32)
2870        } else {
2871            self.get_index_constant(spirv::Scope::Workgroup as u32)
2872        };
2873        let semantics_id = self.get_index_constant(semantics.bits());
2874        block
2875            .body
2876            .push(Instruction::memory_barrier(mem_scope_id, semantics_id));
2877    }
2878
2879    fn generate_workgroup_vars_init_block(
2880        &mut self,
2881        entry_id: Word,
2882        ir_module: &crate::Module,
2883        info: &FunctionInfo,
2884        local_invocation_index: Option<Word>,
2885        interface: &mut FunctionInterface,
2886        function: &mut Function,
2887    ) -> Option<Word> {
2888        let body = ir_module
2889            .global_variables
2890            .iter()
2891            .filter(|&(handle, var)| {
2892                let task_exception = (var.space == crate::AddressSpace::TaskPayload)
2893                    && interface.stage == crate::ShaderStage::Task;
2894                !info[handle].is_empty()
2895                    && (var.space == crate::AddressSpace::WorkGroup || task_exception)
2896            })
2897            .map(|(handle, var)| {
2898                // It's safe to use `var_id` here, not `access_id`, because only
2899                // variables in the `Uniform` and `StorageBuffer` address spaces
2900                // get wrapped, and we're initializing `WorkGroup` variables.
2901                let var_id = self.global_variables[handle].var_id;
2902                let var_type_id = self.get_handle_type_id(var.ty);
2903                let init_word = self.get_constant_null(var_type_id);
2904                Instruction::store(var_id, init_word, None)
2905            })
2906            .collect::<Vec<_>>();
2907
2908        if body.is_empty() {
2909            return None;
2910        }
2911
2912        let mut pre_if_block = Block::new(entry_id);
2913
2914        let local_invocation_index = if let Some(local_invocation_index) = local_invocation_index {
2915            local_invocation_index
2916        } else {
2917            let varying_id = self.id_gen.next();
2918            let class = spirv::StorageClass::Input;
2919            let u32_ty_id = self.get_u32_type_id();
2920            let pointer_type_id = self.get_pointer_type_id(u32_ty_id, class);
2921
2922            Instruction::variable(pointer_type_id, varying_id, class, None)
2923                .to_words(&mut self.logical_layout.declarations);
2924
2925            self.decorate(
2926                varying_id,
2927                spirv::Decoration::BuiltIn,
2928                &[spirv::BuiltIn::LocalInvocationIndex as u32],
2929            );
2930
2931            interface.varying_ids.push(varying_id);
2932            let id = self.id_gen.next();
2933            pre_if_block
2934                .body
2935                .push(Instruction::load(u32_ty_id, id, varying_id, None));
2936
2937            id
2938        };
2939
2940        let zero_id = self.get_constant_scalar(crate::Literal::U32(0));
2941
2942        let eq_id = self.id_gen.next();
2943        pre_if_block.body.push(Instruction::binary(
2944            spirv::Op::IEqual,
2945            self.get_bool_type_id(),
2946            eq_id,
2947            local_invocation_index,
2948            zero_id,
2949        ));
2950
2951        let merge_id = self.id_gen.next();
2952        pre_if_block.body.push(Instruction::selection_merge(
2953            merge_id,
2954            spirv::SelectionControl::NONE,
2955        ));
2956
2957        let accept_id = self.id_gen.next();
2958        function.consume(
2959            pre_if_block,
2960            Instruction::branch_conditional(eq_id, accept_id, merge_id),
2961        );
2962
2963        let accept_block = Block {
2964            label_id: accept_id,
2965            body,
2966        };
2967        function.consume(accept_block, Instruction::branch(merge_id));
2968
2969        let mut post_if_block = Block::new(merge_id);
2970
2971        self.write_control_barrier(crate::Barrier::WORK_GROUP, &mut post_if_block.body);
2972
2973        let next_id = self.id_gen.next();
2974        function.consume(post_if_block, Instruction::branch(next_id));
2975        Some(next_id)
2976    }
2977
2978    /// Generate an `OpVariable` for one value in an [`EntryPoint`]'s IO interface.
2979    ///
2980    /// The [`Binding`]s of the arguments and result of an [`EntryPoint`]'s
2981    /// [`Function`] describe a SPIR-V shader interface. In SPIR-V, the
2982    /// interface is represented by global variables in the `Input` and `Output`
2983    /// storage classes, with decorations indicating which builtin or location
2984    /// each variable corresponds to.
2985    ///
2986    /// This function emits a single global `OpVariable` for a single value from
2987    /// the interface, and adds appropriate decorations to indicate which
2988    /// builtin or location it represents, how it should be interpolated, and so
2989    /// on. The `class` argument gives the variable's SPIR-V storage class,
2990    /// which should be either [`Input`] or [`Output`]. The one exception is
2991    /// `hit_barycentrics`, which overrides `class` to `HitAttributeKHR`.
2992    ///
2993    /// [`Binding`]: crate::Binding
2994    /// [`Function`]: crate::Function
2995    /// [`EntryPoint`]: crate::EntryPoint
2996    /// [`Input`]: spirv::StorageClass::Input
2997    /// [`Output`]: spirv::StorageClass::Output
2998    fn write_varying(
2999        &mut self,
3000        ir_module: &crate::Module,
3001        stage: crate::ShaderStage,
3002        class: spirv::StorageClass,
3003        debug_name: Option<&str>,
3004        ty: Handle<crate::Type>,
3005        binding: &crate::Binding,
3006    ) -> Result<Word, Error> {
3007        // Triangle barycentrics are supplied as a `HitAttributeKHR` variable, not as an `Input`
3008        // builtin: when a hit group has no intersection shader, the hit attribute an any-hit or
3009        // closest-hit shader reads is a two-component float vector holding the barycentric
3010        // coordinates of the hit, and hit attributes carry no `BuiltIn` decoration.
3011        //
3012        // SPEC: https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#interfaces-raypipeline
3013        //
3014        // Declaring it here, alongside the entry point's other varyings, works because ray tracing
3015        // requires SPIR-V 1.4, where the `OpEntryPoint` interface lists global variables from every
3016        // storage class rather than just `Input` and `Output`.
3017        //
3018        // SPEC: https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpEntryPoint
3019        let class = match *binding {
3020            crate::Binding::BuiltIn(crate::BuiltIn::HitBarycentrics) => {
3021                spirv::StorageClass::HitAttributeKHR
3022            }
3023            _ => class,
3024        };
3025
3026        let id = self.id_gen.next();
3027        let ty_inner = &ir_module.types[ty].inner;
3028        let needs_polyfill = self.needs_f16_polyfill(ty_inner);
3029
3030        let pointer_type_id = if needs_polyfill {
3031            let f32_value_local =
3032                super::f16_polyfill::F16IoPolyfill::create_polyfill_type(ty_inner)
3033                    .expect("needs_polyfill returned true but create_polyfill_type returned None");
3034
3035            let f32_type_id = self.get_localtype_id(f32_value_local);
3036            let ptr_id = self.get_pointer_type_id(f32_type_id, class);
3037            self.io_f16_polyfills.register_io_var(id, f32_type_id);
3038
3039            ptr_id
3040        } else {
3041            self.get_handle_pointer_type_id(ty, class)
3042        };
3043
3044        Instruction::variable(pointer_type_id, id, class, None)
3045            .to_words(&mut self.logical_layout.declarations);
3046
3047        if self
3048            .flags
3049            .contains(WriterFlags::DEBUG | WriterFlags::LABEL_VARYINGS)
3050        {
3051            if let Some(name) = debug_name {
3052                self.debugs.push(Instruction::name(id, name));
3053            }
3054        }
3055
3056        let binding = self.map_binding(ir_module, stage, class, ty, binding)?;
3057        self.write_binding(id, binding);
3058
3059        Ok(id)
3060    }
3061
3062    pub fn write_binding(&mut self, id: Word, binding: BindingDecorations) {
3063        match binding {
3064            BindingDecorations::None => (),
3065            BindingDecorations::BuiltIn(bi, others) => {
3066                self.decorate(id, spirv::Decoration::BuiltIn, &[bi as u32]);
3067                for other in others {
3068                    self.decorate(id, other, &[]);
3069                }
3070            }
3071            BindingDecorations::Location {
3072                location,
3073                others,
3074                blend_src,
3075            } => {
3076                self.decorate(id, spirv::Decoration::Location, &[location]);
3077                for other in others {
3078                    self.decorate(id, other, &[]);
3079                }
3080                if let Some(blend_src) = blend_src {
3081                    self.decorate(id, spirv::Decoration::Index, &[blend_src]);
3082                }
3083            }
3084        }
3085    }
3086
3087    pub fn write_binding_struct_member(
3088        &mut self,
3089        struct_id: Word,
3090        member_idx: Word,
3091        binding_info: BindingDecorations,
3092    ) {
3093        match binding_info {
3094            BindingDecorations::None => (),
3095            BindingDecorations::BuiltIn(bi, others) => {
3096                self.annotations.push(Instruction::member_decorate(
3097                    struct_id,
3098                    member_idx,
3099                    spirv::Decoration::BuiltIn,
3100                    &[bi as Word],
3101                ));
3102                for other in others {
3103                    self.annotations.push(Instruction::member_decorate(
3104                        struct_id,
3105                        member_idx,
3106                        other,
3107                        &[],
3108                    ));
3109                }
3110            }
3111            BindingDecorations::Location {
3112                location,
3113                others,
3114                blend_src,
3115            } => {
3116                self.annotations.push(Instruction::member_decorate(
3117                    struct_id,
3118                    member_idx,
3119                    spirv::Decoration::Location,
3120                    &[location],
3121                ));
3122                for other in others {
3123                    self.annotations.push(Instruction::member_decorate(
3124                        struct_id,
3125                        member_idx,
3126                        other,
3127                        &[],
3128                    ));
3129                }
3130                if let Some(blend_src) = blend_src {
3131                    self.annotations.push(Instruction::member_decorate(
3132                        struct_id,
3133                        member_idx,
3134                        spirv::Decoration::Index,
3135                        &[blend_src],
3136                    ));
3137                }
3138            }
3139        }
3140    }
3141
3142    pub fn map_binding(
3143        &mut self,
3144        ir_module: &crate::Module,
3145        stage: crate::ShaderStage,
3146        class: spirv::StorageClass,
3147        ty: Handle<crate::Type>,
3148        binding: &crate::Binding,
3149    ) -> Result<BindingDecorations, Error> {
3150        use spirv::BuiltIn;
3151        use spirv::Decoration;
3152        match *binding {
3153            crate::Binding::Location {
3154                location,
3155                interpolation,
3156                sampling,
3157                blend_src,
3158                per_primitive,
3159            } => {
3160                let mut others = ArrayVec::new();
3161
3162                let no_decorations =
3163                    // VUID-StandaloneSpirv-Flat-06202
3164                    // > The Flat, NoPerspective, Sample, and Centroid decorations
3165                    // > must not be used on variables with the Input storage class in a vertex shader
3166                    (class == spirv::StorageClass::Input && stage == crate::ShaderStage::Vertex) ||
3167                    // VUID-StandaloneSpirv-Flat-06201
3168                    // > The Flat, NoPerspective, Sample, and Centroid decorations
3169                    // > must not be used on variables with the Output storage class in a fragment shader
3170                    (class == spirv::StorageClass::Output && stage == crate::ShaderStage::Fragment);
3171
3172                if !no_decorations {
3173                    match interpolation {
3174                        // Perspective-correct interpolation is the default in SPIR-V.
3175                        None | Some(crate::Interpolation::Perspective) => (),
3176                        Some(crate::Interpolation::Flat) => {
3177                            others.push(Decoration::Flat);
3178                        }
3179                        Some(crate::Interpolation::Linear) => {
3180                            others.push(Decoration::NoPerspective);
3181                        }
3182                        Some(crate::Interpolation::PerVertex) => {
3183                            others.push(Decoration::PerVertexKHR);
3184                            self.require_any(
3185                                "`per_vertex` interpolation",
3186                                &[spirv::Capability::FragmentBarycentricKHR],
3187                            )?;
3188                            self.use_extension("SPV_KHR_fragment_shader_barycentric");
3189                        }
3190                    }
3191                    match sampling {
3192                        // Center sampling is the default in SPIR-V.
3193                        None
3194                        | Some(
3195                            crate::Sampling::Center
3196                            | crate::Sampling::First
3197                            | crate::Sampling::Either,
3198                        ) => (),
3199                        Some(crate::Sampling::Centroid) => {
3200                            others.push(Decoration::Centroid);
3201                        }
3202                        Some(crate::Sampling::Sample) => {
3203                            self.require_any(
3204                                "per-sample interpolation",
3205                                &[spirv::Capability::SampleRateShading],
3206                            )?;
3207                            others.push(Decoration::Sample);
3208                        }
3209                    }
3210                }
3211                if per_primitive && stage == crate::ShaderStage::Fragment {
3212                    others.push(Decoration::PerPrimitiveEXT);
3213                }
3214                Ok(BindingDecorations::Location {
3215                    location,
3216                    others,
3217                    blend_src,
3218                })
3219            }
3220            crate::Binding::BuiltIn(built_in) => {
3221                use crate::BuiltIn as Bi;
3222                let mut others = ArrayVec::new();
3223
3224                let built_in = match built_in {
3225                    Bi::Position { invariant } => {
3226                        if invariant {
3227                            others.push(Decoration::Invariant);
3228                        }
3229
3230                        if class == spirv::StorageClass::Output {
3231                            BuiltIn::Position
3232                        } else {
3233                            BuiltIn::FragCoord
3234                        }
3235                    }
3236                    Bi::ViewIndex => {
3237                        self.require_any("`view_index` built-in", &[spirv::Capability::MultiView])?;
3238                        BuiltIn::ViewIndex
3239                    }
3240                    // vertex
3241                    Bi::BaseInstance => BuiltIn::BaseInstance,
3242                    Bi::BaseVertex => BuiltIn::BaseVertex,
3243                    Bi::ClipDistances => {
3244                        self.require_any(
3245                            "`clip_distances` built-in",
3246                            &[spirv::Capability::ClipDistance],
3247                        )?;
3248                        BuiltIn::ClipDistance
3249                    }
3250                    Bi::CullDistance => {
3251                        self.require_any(
3252                            "`cull_distance` built-in",
3253                            &[spirv::Capability::CullDistance],
3254                        )?;
3255                        BuiltIn::CullDistance
3256                    }
3257                    Bi::InstanceIndex => match stage {
3258                        crate::ShaderStage::AnyHit | crate::ShaderStage::ClosestHit => {
3259                            BuiltIn::InstanceId
3260                        }
3261                        _ => BuiltIn::InstanceIndex,
3262                    },
3263                    Bi::PointSize => BuiltIn::PointSize,
3264                    Bi::VertexIndex => BuiltIn::VertexIndex,
3265                    Bi::DrawIndex => {
3266                        self.use_extension("SPV_KHR_shader_draw_parameters");
3267                        self.require_any(
3268                            "`draw_index built-in",
3269                            &[spirv::Capability::DrawParameters],
3270                        )?;
3271                        BuiltIn::DrawIndex
3272                    }
3273                    // fragment
3274                    Bi::FragDepth => BuiltIn::FragDepth,
3275                    Bi::PointCoord => BuiltIn::PointCoord,
3276                    Bi::FrontFacing => BuiltIn::FrontFacing,
3277                    Bi::PrimitiveIndex => {
3278                        // `PrimitiveId` is enabled by any of `Geometry`, `Tessellation`,
3279                        // `RayTracingKHR` or `MeshShadingEXT`. `require_any` picks the first one the target
3280                        // allows.
3281                        //
3282                        // SPEC: https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#:~:text=PrimitiveId
3283                        let enabled_by: &[spirv::Capability] = match stage {
3284                            // The stage itself already requires `RayTracingKHR`.
3285                            crate::ShaderStage::AnyHit | crate::ShaderStage::ClosestHit => &[],
3286                            // The stage itself already requires `MeshShadingEXT`.
3287                            crate::ShaderStage::Mesh => &[],
3288                            // A fragment shader can be fed primitive IDs by a geometry,
3289                            // tessellation or mesh pipeline.
3290                            crate::ShaderStage::Fragment => &[
3291                                spirv::Capability::Geometry,
3292                                spirv::Capability::Tessellation,
3293                                spirv::Capability::MeshShadingEXT,
3294                            ],
3295                            // `PrimitiveId` isn't permitted in these execution models.
3296                            _ => return Err(Error::Validation(
3297                                "`primitive_index` built-in is not allowed in this shader stage",
3298                            )),
3299                        };
3300                        self.require_any("`primitive_index` built-in", enabled_by)?;
3301
3302                        if stage == crate::ShaderStage::Mesh {
3303                            others.push(Decoration::PerPrimitiveEXT);
3304                        }
3305
3306                        BuiltIn::PrimitiveId
3307                    }
3308                    Bi::Barycentric { perspective } => {
3309                        self.require_any(
3310                            "`barycentric` built-in",
3311                            &[spirv::Capability::FragmentBarycentricKHR],
3312                        )?;
3313                        self.use_extension("SPV_KHR_fragment_shader_barycentric");
3314                        if perspective {
3315                            BuiltIn::BaryCoordKHR
3316                        } else {
3317                            BuiltIn::BaryCoordNoPerspKHR
3318                        }
3319                    }
3320                    Bi::SampleIndex => {
3321                        self.require_any(
3322                            "`sample_index` built-in",
3323                            &[spirv::Capability::SampleRateShading],
3324                        )?;
3325
3326                        BuiltIn::SampleId
3327                    }
3328                    Bi::SampleMask => BuiltIn::SampleMask,
3329                    // compute
3330                    Bi::GlobalInvocationId => BuiltIn::GlobalInvocationId,
3331                    Bi::LocalInvocationId => BuiltIn::LocalInvocationId,
3332                    Bi::LocalInvocationIndex => BuiltIn::LocalInvocationIndex,
3333                    Bi::WorkGroupId => BuiltIn::WorkgroupId,
3334                    Bi::WorkGroupSize => BuiltIn::WorkgroupSize,
3335                    Bi::NumWorkGroups => BuiltIn::NumWorkgroups,
3336                    // Subgroup
3337                    Bi::NumSubgroups => {
3338                        self.require_any(
3339                            "`num_subgroups` built-in",
3340                            &[spirv::Capability::GroupNonUniform],
3341                        )?;
3342                        BuiltIn::NumSubgroups
3343                    }
3344                    Bi::SubgroupId => {
3345                        self.require_any(
3346                            "`subgroup_id` built-in",
3347                            &[spirv::Capability::GroupNonUniform],
3348                        )?;
3349                        BuiltIn::SubgroupId
3350                    }
3351                    Bi::SubgroupSize => {
3352                        self.require_any(
3353                            "`subgroup_size` built-in",
3354                            &[
3355                                spirv::Capability::GroupNonUniform,
3356                                spirv::Capability::SubgroupBallotKHR,
3357                            ],
3358                        )?;
3359                        BuiltIn::SubgroupSize
3360                    }
3361                    Bi::SubgroupInvocationId => {
3362                        self.require_any(
3363                            "`subgroup_invocation_id` built-in",
3364                            &[
3365                                spirv::Capability::GroupNonUniform,
3366                                spirv::Capability::SubgroupBallotKHR,
3367                            ],
3368                        )?;
3369                        BuiltIn::SubgroupLocalInvocationId
3370                    }
3371                    Bi::CullPrimitive => {
3372                        others.push(Decoration::PerPrimitiveEXT);
3373                        BuiltIn::CullPrimitiveEXT
3374                    }
3375                    Bi::PointIndex => BuiltIn::PrimitivePointIndicesEXT,
3376                    Bi::LineIndices => BuiltIn::PrimitiveLineIndicesEXT,
3377                    Bi::TriangleIndices => BuiltIn::PrimitiveTriangleIndicesEXT,
3378                    // No decoration, this EmitMeshTasksEXT is called at function return
3379                    Bi::MeshTaskSize => return Ok(BindingDecorations::None),
3380                    // These aren't normal builtins and don't occur in function output
3381                    Bi::VertexCount | Bi::Vertices | Bi::PrimitiveCount | Bi::Primitives => {
3382                        unreachable!()
3383                    }
3384                    // ray tracing pipeline
3385                    Bi::RayInvocationId => BuiltIn::LaunchIdKHR,
3386                    Bi::NumRayInvocations => BuiltIn::LaunchSizeKHR,
3387                    Bi::InstanceCustomData => BuiltIn::InstanceCustomIndexKHR,
3388                    Bi::GeometryIndex => BuiltIn::RayGeometryIndexKHR,
3389                    Bi::WorldRayOrigin => BuiltIn::WorldRayOriginKHR,
3390                    Bi::WorldRayDirection => BuiltIn::WorldRayDirectionKHR,
3391                    Bi::ObjectRayOrigin => BuiltIn::ObjectRayOriginKHR,
3392                    Bi::ObjectRayDirection => BuiltIn::ObjectRayDirectionKHR,
3393                    Bi::RayTmin => BuiltIn::RayTminKHR,
3394                    Bi::RayTCurrentMax => BuiltIn::RayTmaxKHR,
3395                    Bi::ObjectToWorld => BuiltIn::ObjectToWorldKHR,
3396                    Bi::WorldToObject => BuiltIn::WorldToObjectKHR,
3397                    Bi::HitKind => BuiltIn::HitKindKHR,
3398                    // Read from the entry point's `HitAttributeKHR` variable instead.
3399                    Bi::HitBarycentrics => return Ok(BindingDecorations::None),
3400                };
3401
3402                use crate::ScalarKind as Sk;
3403
3404                // Per the Vulkan spec, `VUID-StandaloneSpirv-Flat-04744`:
3405                //
3406                // > Any variable with integer or double-precision floating-
3407                // > point type and with Input storage class in a fragment
3408                // > shader, must be decorated Flat
3409                if class == spirv::StorageClass::Input && stage == crate::ShaderStage::Fragment {
3410                    let is_flat = match ir_module.types[ty].inner {
3411                        crate::TypeInner::Scalar(scalar)
3412                        | crate::TypeInner::Vector { scalar, .. } => match scalar.kind {
3413                            Sk::Uint | Sk::Sint | Sk::Bool => true,
3414                            Sk::Float => false,
3415                            Sk::AbstractInt | Sk::AbstractFloat => {
3416                                return Err(Error::Validation(
3417                                    "Abstract types should not appear in IR presented to backends",
3418                                ))
3419                            }
3420                        },
3421                        _ => false,
3422                    };
3423
3424                    if is_flat {
3425                        others.push(Decoration::Flat);
3426                    }
3427                }
3428                Ok(BindingDecorations::BuiltIn(built_in, others))
3429            }
3430        }
3431    }
3432
3433    /// Load an IO variable, converting from `f32` to `f16` if polyfill is active.
3434    /// Returns the id of the loaded value matching `target_type_id`.
3435    pub(super) fn load_io_with_f16_polyfill(
3436        &mut self,
3437        body: &mut Vec<Instruction>,
3438        varying_id: Word,
3439        target_type_id: Word,
3440    ) -> Word {
3441        let tmp = self.id_gen.next();
3442        if let Some(f32_ty) = self.io_f16_polyfills.get_f32_io_type(varying_id) {
3443            body.push(Instruction::load(f32_ty, tmp, varying_id, None));
3444            let converted = self.id_gen.next();
3445            super::f16_polyfill::F16IoPolyfill::emit_f32_to_f16_conversion(
3446                tmp,
3447                target_type_id,
3448                converted,
3449                body,
3450            );
3451            converted
3452        } else {
3453            body.push(Instruction::load(target_type_id, tmp, varying_id, None));
3454            tmp
3455        }
3456    }
3457
3458    /// Store an IO variable, converting from `f16` to `f32` if polyfill is active.
3459    pub(super) fn store_io_with_f16_polyfill(
3460        &mut self,
3461        body: &mut Vec<Instruction>,
3462        varying_id: Word,
3463        value_id: Word,
3464    ) {
3465        if let Some(f32_ty) = self.io_f16_polyfills.get_f32_io_type(varying_id) {
3466            let converted = self.id_gen.next();
3467            super::f16_polyfill::F16IoPolyfill::emit_f16_to_f32_conversion(
3468                value_id, f32_ty, converted, body,
3469            );
3470            body.push(Instruction::store(varying_id, converted, None));
3471        } else {
3472            body.push(Instruction::store(varying_id, value_id, None));
3473        }
3474    }
3475
3476    fn write_global_variable(
3477        &mut self,
3478        ir_module: &crate::Module,
3479        global_variable: &crate::GlobalVariable,
3480    ) -> Result<Word, Error> {
3481        use spirv::Decoration;
3482
3483        let id = self.id_gen.next();
3484        let class = map_storage_class(global_variable.space);
3485
3486        if let crate::AddressSpace::RayPayload | crate::AddressSpace::IncomingRayPayload =
3487            global_variable.space
3488        {
3489            self.require_any("ray tracing pipelines", &[spirv::Capability::RayTracingKHR])?;
3490        }
3491
3492        //self.check(class.required_capabilities())?;
3493
3494        if global_variable
3495            .memory_decorations
3496            .contains(crate::MemoryDecorations::COHERENT)
3497        {
3498            self.decorate(id, Decoration::Coherent, &[]);
3499        }
3500        if global_variable
3501            .memory_decorations
3502            .contains(crate::MemoryDecorations::VOLATILE)
3503        {
3504            self.decorate(id, Decoration::Volatile, &[]);
3505        }
3506
3507        if self.flags.contains(WriterFlags::DEBUG) {
3508            if let Some(ref name) = global_variable.name {
3509                self.debugs.push(Instruction::name(id, name));
3510            }
3511        }
3512
3513        let storage_access = match global_variable.space {
3514            crate::AddressSpace::Storage { access } => Some(access),
3515            _ => match ir_module.types[global_variable.ty].inner {
3516                crate::TypeInner::Image {
3517                    class: crate::ImageClass::Storage { access, .. },
3518                    ..
3519                } => Some(access),
3520                _ => None,
3521            },
3522        };
3523        if let Some(storage_access) = storage_access {
3524            if !storage_access.contains(crate::StorageAccess::LOAD) {
3525                self.decorate(id, Decoration::NonReadable, &[]);
3526            }
3527            if !storage_access.contains(crate::StorageAccess::STORE) {
3528                self.decorate(id, Decoration::NonWritable, &[]);
3529            }
3530        }
3531
3532        // Note: we should be able to substitute `binding_array<Foo, 0>`,
3533        // but there is still code that tries to register the pre-substituted type,
3534        // and it is failing on 0.
3535        let mut substitute_inner_type_lookup = None;
3536        if let Some(ref res_binding) = global_variable.binding {
3537            let bind_target = self.resolve_resource_binding(res_binding)?;
3538            self.decorate(id, Decoration::DescriptorSet, &[bind_target.descriptor_set]);
3539            self.decorate(id, Decoration::Binding, &[bind_target.binding]);
3540
3541            if let Some(remapped_binding_array_size) = bind_target.binding_array_size {
3542                if let crate::TypeInner::BindingArray { base, .. } =
3543                    ir_module.types[global_variable.ty].inner
3544                {
3545                    let binding_array_type_id =
3546                        self.get_type_id(LookupType::Local(LocalType::BindingArray {
3547                            base,
3548                            size: remapped_binding_array_size,
3549                        }));
3550                    substitute_inner_type_lookup = Some(LookupType::Local(LocalType::Pointer {
3551                        base: binding_array_type_id,
3552                        class,
3553                    }));
3554                }
3555            }
3556        };
3557
3558        let init_word = global_variable
3559            .init
3560            .map(|constant| self.constant_ids[constant]);
3561        let inner_type_id = self.get_type_id(
3562            substitute_inner_type_lookup.unwrap_or(LookupType::Handle(global_variable.ty)),
3563        );
3564
3565        // generate the wrapping structure if needed
3566        let pointer_type_id = if global_needs_wrapper(ir_module, global_variable) {
3567            let wrapper_type_id = self.id_gen.next();
3568
3569            self.decorate(wrapper_type_id, Decoration::Block, &[]);
3570
3571            match self.std140_compat_uniform_types.get(&global_variable.ty) {
3572                Some(std140_type_info) if global_variable.space == crate::AddressSpace::Uniform => {
3573                    self.annotations.push(Instruction::member_decorate(
3574                        wrapper_type_id,
3575                        0,
3576                        Decoration::Offset,
3577                        &[0],
3578                    ));
3579                    Instruction::type_struct(wrapper_type_id, &[std140_type_info.type_id])
3580                        .to_words(&mut self.logical_layout.declarations);
3581                }
3582                _ => {
3583                    let member = crate::StructMember {
3584                        name: None,
3585                        ty: global_variable.ty,
3586                        binding: None,
3587                        offset: 0,
3588                    };
3589                    self.decorate_struct_member(wrapper_type_id, 0, &member, &ir_module.types)?;
3590
3591                    Instruction::type_struct(wrapper_type_id, &[inner_type_id])
3592                        .to_words(&mut self.logical_layout.declarations);
3593                }
3594            }
3595
3596            let pointer_type_id = self.id_gen.next();
3597            Instruction::type_pointer(pointer_type_id, class, wrapper_type_id)
3598                .to_words(&mut self.logical_layout.declarations);
3599
3600            pointer_type_id
3601        } else {
3602            // This is a global variable in the Storage address space. The only
3603            // way it could have `global_needs_wrapper() == false` is if it has
3604            // a runtime-sized or binding array.
3605            // Runtime-sized arrays were decorated when iterating through struct content.
3606            // Now binding arrays require Block decorating.
3607            if let crate::AddressSpace::Storage { .. } = global_variable.space {
3608                match ir_module.types[global_variable.ty].inner {
3609                    crate::TypeInner::BindingArray { base, .. } => {
3610                        let ty = &ir_module.types[base];
3611                        let mut should_decorate = true;
3612                        // Check if the type has a runtime array.
3613                        // A normal runtime array gets validated out,
3614                        // so only structs can be with runtime arrays
3615                        if let crate::TypeInner::Struct { ref members, .. } = ty.inner {
3616                            // only the last member in a struct can be dynamically sized
3617                            if let Some(last_member) = members.last() {
3618                                if let &crate::TypeInner::Array {
3619                                    size: crate::ArraySize::Dynamic,
3620                                    ..
3621                                } = &ir_module.types[last_member.ty].inner
3622                                {
3623                                    should_decorate = false;
3624                                }
3625                            }
3626                        }
3627                        if should_decorate {
3628                            let decorated_id = self.get_handle_type_id(base);
3629                            self.decorate(decorated_id, Decoration::Block, &[]);
3630                        }
3631                    }
3632                    _ => (),
3633                };
3634            }
3635            if substitute_inner_type_lookup.is_some() {
3636                inner_type_id
3637            } else {
3638                self.get_handle_pointer_type_id(global_variable.ty, class)
3639            }
3640        };
3641
3642        let init_word = match (global_variable.space, self.zero_initialize_workgroup_memory) {
3643            (crate::AddressSpace::Private, _)
3644            | (crate::AddressSpace::WorkGroup, super::ZeroInitializeWorkgroupMemoryMode::Native) => {
3645                init_word.or_else(|| Some(self.get_constant_null(inner_type_id)))
3646            }
3647            _ => init_word,
3648        };
3649
3650        Instruction::variable(pointer_type_id, id, class, init_word)
3651            .to_words(&mut self.logical_layout.declarations);
3652        Ok(id)
3653    }
3654
3655    /// Write the necessary decorations for a struct member.
3656    ///
3657    /// Emit decorations for the `index`'th member of the struct type
3658    /// designated by `struct_id`, described by `member`.
3659    fn decorate_struct_member(
3660        &mut self,
3661        struct_id: Word,
3662        index: usize,
3663        member: &crate::StructMember,
3664        arena: &UniqueArena<crate::Type>,
3665    ) -> Result<(), Error> {
3666        use spirv::Decoration;
3667
3668        self.annotations.push(Instruction::member_decorate(
3669            struct_id,
3670            index as u32,
3671            Decoration::Offset,
3672            &[member.offset],
3673        ));
3674
3675        if self.flags.contains(WriterFlags::DEBUG) {
3676            if let Some(ref name) = member.name {
3677                self.debugs
3678                    .push(Instruction::member_name(struct_id, index as u32, name));
3679            }
3680        }
3681
3682        // Matrices and (potentially nested) arrays of matrices both require decorations,
3683        // so "see through" any arrays to determine if they're needed.
3684        let mut member_array_subty_inner = &arena[member.ty].inner;
3685        while let crate::TypeInner::Array { base, .. } = *member_array_subty_inner {
3686            member_array_subty_inner = &arena[base].inner;
3687        }
3688
3689        if let crate::TypeInner::Matrix {
3690            columns: _,
3691            rows,
3692            scalar,
3693        } = *member_array_subty_inner
3694        {
3695            let byte_stride = Alignment::from(rows) * scalar.width as u32;
3696            self.annotations.push(Instruction::member_decorate(
3697                struct_id,
3698                index as u32,
3699                Decoration::ColMajor,
3700                &[],
3701            ));
3702            self.annotations.push(Instruction::member_decorate(
3703                struct_id,
3704                index as u32,
3705                Decoration::MatrixStride,
3706                &[byte_stride],
3707            ));
3708        }
3709
3710        Ok(())
3711    }
3712
3713    pub(super) fn get_function_type(&mut self, lookup_function_type: LookupFunctionType) -> Word {
3714        match self
3715            .lookup_function_type
3716            .entry(lookup_function_type.clone())
3717        {
3718            Entry::Occupied(e) => *e.get(),
3719            Entry::Vacant(_) => {
3720                let id = self.id_gen.next();
3721                let instruction = Instruction::type_function(
3722                    id,
3723                    lookup_function_type.return_type_id,
3724                    &lookup_function_type.parameter_type_ids,
3725                );
3726                instruction.to_words(&mut self.logical_layout.declarations);
3727                self.lookup_function_type.insert(lookup_function_type, id);
3728                id
3729            }
3730        }
3731    }
3732
3733    const fn write_physical_layout(&mut self) {
3734        self.physical_layout.bound = self.id_gen.0 + 1;
3735    }
3736
3737    fn write_logical_layout(
3738        &mut self,
3739        ir_module: &crate::Module,
3740        mod_info: &ModuleInfo,
3741        ep_index: Option<usize>,
3742        debug_info: &Option<DebugInfo>,
3743    ) -> Result<(), Error> {
3744        fn has_view_index_check(
3745            ir_module: &crate::Module,
3746            binding: Option<&crate::Binding>,
3747            ty: Handle<crate::Type>,
3748        ) -> bool {
3749            match ir_module.types[ty].inner {
3750                crate::TypeInner::Struct { ref members, .. } => members.iter().any(|member| {
3751                    has_view_index_check(ir_module, member.binding.as_ref(), member.ty)
3752                }),
3753                _ => binding == Some(&crate::Binding::BuiltIn(crate::BuiltIn::ViewIndex)),
3754            }
3755        }
3756
3757        let has_storage_buffers =
3758            ir_module
3759                .global_variables
3760                .iter()
3761                .any(|(_, var)| match var.space {
3762                    crate::AddressSpace::Storage { .. } => true,
3763                    _ => false,
3764                });
3765        let has_view_index = ir_module
3766            .entry_points
3767            .iter()
3768            .flat_map(|entry| entry.function.arguments.iter())
3769            .any(|arg| has_view_index_check(ir_module, arg.binding.as_ref(), arg.ty));
3770        let has_vertex_return = ir_module.special_types.ray_vertex_return.is_some();
3771
3772        let rt_uses = ir_module.uses_ray_tracing(ep_index);
3773        let has_ray_query = rt_uses.queries;
3774        let has_ray_tracing_pipeline = rt_uses.pipelines;
3775
3776        self.has_ray_tracing_pipeline = has_ray_tracing_pipeline;
3777
3778        if self.physical_layout.version < 0x10300 && has_storage_buffers {
3779            // enable the storage buffer class on < SPV-1.3
3780            Instruction::extension("SPV_KHR_storage_buffer_storage_class")
3781                .to_words(&mut self.logical_layout.extensions);
3782        }
3783        if has_view_index {
3784            Instruction::extension("SPV_KHR_multiview")
3785                .to_words(&mut self.logical_layout.extensions)
3786        }
3787        if has_ray_query {
3788            Instruction::extension("SPV_KHR_ray_query")
3789                .to_words(&mut self.logical_layout.extensions)
3790        }
3791        if has_vertex_return {
3792            Instruction::extension("SPV_KHR_ray_tracing_position_fetch")
3793                .to_words(&mut self.logical_layout.extensions);
3794        }
3795        if ir_module.uses_mesh_shaders() {
3796            self.use_extension("SPV_EXT_mesh_shader");
3797            self.require_any("Mesh Shaders", &[spirv::Capability::MeshShadingEXT])?;
3798            let lang_version = self.lang_version();
3799            if lang_version.0 <= 1 && lang_version.1 < 4 {
3800                return Err(Error::SpirvVersionTooLow(1, 4));
3801            }
3802        }
3803        if has_ray_tracing_pipeline {
3804            // `SPV_KHR_ray_tracing` requires SPIR-V 1.4. That is also what lets `write_varying`
3805            // emit `hit_barycentrics` as a `HitAttributeKHR` variable, since only from 1.4 on does
3806            // the `OpEntryPoint` interface cover storage classes other than `Input` and `Output`.
3807            //
3808            // SPEC: https://github.khronos.org/SPIRV-Registry/extensions/KHR/SPV_KHR_ray_tracing.html
3809            let lang_version = self.lang_version();
3810            if lang_version.0 <= 1 && lang_version.1 < 4 {
3811                return Err(Error::SpirvVersionTooLow(1, 4));
3812            }
3813            Instruction::extension("SPV_KHR_ray_tracing")
3814                .to_words(&mut self.logical_layout.extensions)
3815        }
3816        Instruction::type_void(self.void_type).to_words(&mut self.logical_layout.declarations);
3817        Instruction::ext_inst_import(self.gl450_ext_inst_id, "GLSL.std.450")
3818            .to_words(&mut self.logical_layout.ext_inst_imports);
3819
3820        let mut debug_info_inner = None;
3821        if self.flags.contains(WriterFlags::DEBUG) {
3822            if let Some(debug_info) = debug_info.as_ref() {
3823                let source_file_id = self.id_gen.next();
3824                self.debugs
3825                    .push(Instruction::string(debug_info.file_name, source_file_id));
3826
3827                debug_info_inner = Some(DebugInfoInner {
3828                    source_code: debug_info.source_code,
3829                    source_file_id,
3830                });
3831                self.debugs.append(&mut Instruction::source_auto_continued(
3832                    debug_info.language,
3833                    0,
3834                    &debug_info_inner,
3835                ));
3836            }
3837        }
3838
3839        // write all types
3840        for (handle, _) in ir_module.types.iter() {
3841            self.write_type_declaration_arena(ir_module, handle)?;
3842        }
3843
3844        // write std140 layout compatible types required by uniforms
3845        for (_, var) in ir_module.global_variables.iter() {
3846            if var.space == crate::AddressSpace::Uniform {
3847                self.write_std140_compat_type_declaration(ir_module, var.ty)?;
3848            }
3849        }
3850
3851        // write all const-expressions as constants
3852        self.constant_ids
3853            .resize(ir_module.global_expressions.len(), 0);
3854        for (handle, _) in ir_module.global_expressions.iter() {
3855            self.write_constant_expr(handle, ir_module, mod_info)?;
3856        }
3857        debug_assert!(self.constant_ids.iter().all(|&id| id != 0));
3858
3859        // write the name of constants on their respective const-expression initializer
3860        if self.flags.contains(WriterFlags::DEBUG) {
3861            for (_, constant) in ir_module.constants.iter() {
3862                if let Some(ref name) = constant.name {
3863                    let id = self.constant_ids[constant.init];
3864                    self.debugs.push(Instruction::name(id, name));
3865                }
3866            }
3867        }
3868
3869        // write all global variables
3870        for (handle, var) in ir_module.global_variables.iter() {
3871            // If a single entry point was specified, only write `OpVariable` instructions
3872            // for the globals it actually uses. Emit dummies for the others,
3873            // to preserve the indices in `global_variables`.
3874            let gvar = match ep_index {
3875                Some(index) if mod_info.get_entry_point(index)[handle].is_empty() => {
3876                    GlobalVariable::dummy()
3877                }
3878                _ => {
3879                    let id = self.write_global_variable(ir_module, var)?;
3880                    GlobalVariable::new(id)
3881                }
3882            };
3883            self.global_variables.insert(handle, gvar);
3884        }
3885
3886        // write all functions
3887        for (handle, ir_function) in ir_module.functions.iter() {
3888            let info = &mod_info[handle];
3889            if let Some(index) = ep_index {
3890                let ep_info = mod_info.get_entry_point(index);
3891                // If this function uses globals that we omitted from the SPIR-V
3892                // because the entry point and its callees didn't use them,
3893                // then we must skip it.
3894                if !ep_info.dominates_global_use(info) {
3895                    log::debug!("Skip function {:?}", ir_function.name);
3896                    continue;
3897                }
3898
3899                // Skip functions that that are not compatible with this entry point's stage.
3900                //
3901                // When validation is enabled, it rejects modules whose entry points try to call
3902                // incompatible functions, so if we got this far, then any functions incompatible
3903                // with our selected entry point must not be used.
3904                //
3905                // When validation is disabled, `fun_info.available_stages` is always just
3906                // `ShaderStages::all()`, so this will write all functions in the module, and
3907                // the downstream GLSL compiler will catch any problems.
3908                if !info.available_stages.contains(ep_info.available_stages) {
3909                    continue;
3910                }
3911            }
3912            let id = self.write_function(ir_function, info, ir_module, None, &debug_info_inner)?;
3913            self.lookup_function.insert(handle, id);
3914        }
3915
3916        // write all or one entry points
3917        for (index, ir_ep) in ir_module.entry_points.iter().enumerate() {
3918            if ep_index.is_some() && ep_index != Some(index) {
3919                continue;
3920            }
3921            let info = mod_info.get_entry_point(index);
3922            let ep_instruction =
3923                self.write_entry_point(ir_ep, info, ir_module, &debug_info_inner)?;
3924            ep_instruction.to_words(&mut self.logical_layout.entry_points);
3925        }
3926
3927        for capability in self.capabilities_used.iter() {
3928            Instruction::capability(*capability).to_words(&mut self.logical_layout.capabilities);
3929        }
3930        for extension in self.extensions_used.iter() {
3931            Instruction::extension(extension).to_words(&mut self.logical_layout.extensions);
3932        }
3933        if ir_module.entry_points.is_empty() {
3934            // SPIR-V doesn't like modules without entry points
3935            Instruction::capability(spirv::Capability::Linkage)
3936                .to_words(&mut self.logical_layout.capabilities);
3937        }
3938
3939        let addressing_model = spirv::AddressingModel::Logical;
3940        let memory_model = if self
3941            .capabilities_used
3942            .contains(&spirv::Capability::VulkanMemoryModel)
3943        {
3944            spirv::MemoryModel::Vulkan
3945        } else {
3946            spirv::MemoryModel::GLSL450
3947        };
3948        //self.check(addressing_model.required_capabilities())?;
3949        //self.check(memory_model.required_capabilities())?;
3950
3951        Instruction::memory_model(addressing_model, memory_model)
3952            .to_words(&mut self.logical_layout.memory_model);
3953
3954        for debug_string in self.debug_strings.iter() {
3955            debug_string.to_words(&mut self.logical_layout.debugs);
3956        }
3957
3958        if self.flags.contains(WriterFlags::DEBUG) {
3959            for debug in self.debugs.iter() {
3960                debug.to_words(&mut self.logical_layout.debugs);
3961            }
3962        }
3963
3964        for annotation in self.annotations.iter() {
3965            annotation.to_words(&mut self.logical_layout.annotations);
3966        }
3967
3968        Ok(())
3969    }
3970
3971    pub fn write(
3972        &mut self,
3973        ir_module: &crate::Module,
3974        info: &ModuleInfo,
3975        pipeline_options: Option<&PipelineOptions>,
3976        debug_info: &Option<DebugInfo>,
3977        words: &mut Vec<Word>,
3978    ) -> Result<(), Error> {
3979        self.reset();
3980
3981        // Try to find the entry point and corresponding index
3982        let ep_index = match pipeline_options {
3983            Some(po) => {
3984                let index = ir_module
3985                    .entry_points
3986                    .iter()
3987                    .position(|ep| po.shader_stage == ep.stage && po.entry_point == ep.name)
3988                    .ok_or(Error::EntryPointNotFound)?;
3989                Some(index)
3990            }
3991            None => None,
3992        };
3993
3994        self.write_logical_layout(ir_module, info, ep_index, debug_info)?;
3995        self.write_physical_layout();
3996
3997        self.physical_layout.in_words(words);
3998        self.logical_layout.in_words(words);
3999        Ok(())
4000    }
4001
4002    /// Return the set of capabilities the last module written used.
4003    pub const fn get_capabilities_used(&self) -> &crate::FastIndexSet<spirv::Capability> {
4004        &self.capabilities_used
4005    }
4006
4007    pub fn decorate_non_uniform_binding_array_access(&mut self, id: Word) -> Result<(), Error> {
4008        self.require_any("NonUniformEXT", &[spirv::Capability::ShaderNonUniform])?;
4009        self.use_extension("SPV_EXT_descriptor_indexing");
4010        self.decorate(id, spirv::Decoration::NonUniform, &[]);
4011        Ok(())
4012    }
4013
4014    pub(super) fn needs_f16_polyfill(&self, ty_inner: &crate::TypeInner) -> bool {
4015        self.io_f16_polyfills.needs_polyfill(ty_inner)
4016    }
4017
4018    pub(super) fn write_debug_printf(
4019        &mut self,
4020        block: &mut Block,
4021        string: &str,
4022        format_params: &[Word],
4023    ) {
4024        if self.debug_printf.is_none() {
4025            self.use_extension("SPV_KHR_non_semantic_info");
4026            let import_id = self.id_gen.next();
4027            Instruction::ext_inst_import(import_id, "NonSemantic.DebugPrintf")
4028                .to_words(&mut self.logical_layout.ext_inst_imports);
4029            self.debug_printf = Some(import_id)
4030        }
4031
4032        let import_id = self.debug_printf.unwrap();
4033
4034        let string_id = self.id_gen.next();
4035        self.debug_strings
4036            .push(Instruction::string(string, string_id));
4037
4038        let mut operands = Vec::with_capacity(1 + format_params.len());
4039        operands.push(string_id);
4040        operands.extend(format_params.iter());
4041
4042        let print_id = self.id_gen.next();
4043        block.body.push(Instruction::ext_inst(
4044            import_id,
4045            1,
4046            self.void_type,
4047            print_id,
4048            &operands,
4049        ));
4050    }
4051}
4052
4053#[test]
4054fn test_write_physical_layout() {
4055    let mut writer = Writer::new(&Options::default()).unwrap();
4056    assert_eq!(writer.physical_layout.bound, 0);
4057    writer.write_physical_layout();
4058    assert_eq!(writer.physical_layout.bound, 3);
4059}