naga/back/spv/
writer.rs

1use super::{
2    block::DebugInfoInner,
3    helpers::{contains_builtin, global_needs_wrapper, map_storage_class},
4    Block, BlockContext, CachedConstant, CachedExpressions, DebugInfo, EntryPointContext, Error,
5    Function, FunctionArgument, GlobalVariable, IdGenerator, Instruction, LocalType, LocalVariable,
6    LogicalLayout, LookupFunctionType, LookupType, NumericType, Options, PhysicalLayout,
7    PipelineOptions, ResultMember, Writer, WriterFlags, BITS_PER_BYTE,
8};
9use crate::{
10    arena::{Handle, HandleVec, UniqueArena},
11    back::spv::BindingInfo,
12    proc::{Alignment, TypeResolution},
13    valid::{FunctionInfo, ModuleInfo},
14};
15use spirv::Word;
16use std::collections::hash_map::Entry;
17
18struct FunctionInterface<'a> {
19    varying_ids: &'a mut Vec<Word>,
20    stage: crate::ShaderStage,
21}
22
23impl Function {
24    fn to_words(&self, sink: &mut impl Extend<Word>) {
25        self.signature.as_ref().unwrap().to_words(sink);
26        for argument in self.parameters.iter() {
27            argument.instruction.to_words(sink);
28        }
29        for (index, block) in self.blocks.iter().enumerate() {
30            Instruction::label(block.label_id).to_words(sink);
31            if index == 0 {
32                for local_var in self.variables.values() {
33                    local_var.instruction.to_words(sink);
34                }
35                for internal_var in self.spilled_composites.values() {
36                    internal_var.instruction.to_words(sink);
37                }
38            }
39            for instruction in block.body.iter() {
40                instruction.to_words(sink);
41            }
42        }
43    }
44}
45
46impl Writer {
47    pub fn new(options: &Options) -> Result<Self, Error> {
48        let (major, minor) = options.lang_version;
49        if major != 1 {
50            return Err(Error::UnsupportedVersion(major, minor));
51        }
52        let raw_version = ((major as u32) << 16) | ((minor as u32) << 8);
53
54        let mut capabilities_used = crate::FastIndexSet::default();
55        capabilities_used.insert(spirv::Capability::Shader);
56
57        let mut id_gen = IdGenerator::default();
58        let gl450_ext_inst_id = id_gen.next();
59        let void_type = id_gen.next();
60
61        Ok(Writer {
62            physical_layout: PhysicalLayout::new(raw_version),
63            logical_layout: LogicalLayout::default(),
64            id_gen,
65            capabilities_available: options.capabilities.clone(),
66            capabilities_used,
67            extensions_used: crate::FastIndexSet::default(),
68            debugs: vec![],
69            annotations: vec![],
70            flags: options.flags,
71            bounds_check_policies: options.bounds_check_policies,
72            zero_initialize_workgroup_memory: options.zero_initialize_workgroup_memory,
73            void_type,
74            lookup_type: crate::FastHashMap::default(),
75            lookup_function: crate::FastHashMap::default(),
76            lookup_function_type: crate::FastHashMap::default(),
77            constant_ids: HandleVec::new(),
78            cached_constants: crate::FastHashMap::default(),
79            global_variables: HandleVec::new(),
80            binding_map: options.binding_map.clone(),
81            saved_cached: CachedExpressions::default(),
82            gl450_ext_inst_id,
83            temp_list: Vec::new(),
84        })
85    }
86
87    /// Reset `Writer` to its initial state, retaining any allocations.
88    ///
89    /// Why not just implement `Recyclable` for `Writer`? By design,
90    /// `Recyclable::recycle` requires ownership of the value, not just
91    /// `&mut`; see the trait documentation. But we need to use this method
92    /// from functions like `Writer::write`, which only have `&mut Writer`.
93    /// Workarounds include unsafe code (`std::ptr::read`, then `write`, ugh)
94    /// or something like a `Default` impl that returns an oddly-initialized
95    /// `Writer`, which is worse.
96    fn reset(&mut self) {
97        use super::recyclable::Recyclable;
98        use std::mem::take;
99
100        let mut id_gen = IdGenerator::default();
101        let gl450_ext_inst_id = id_gen.next();
102        let void_type = id_gen.next();
103
104        // Every field of the old writer that is not determined by the `Options`
105        // passed to `Writer::new` should be reset somehow.
106        let fresh = Writer {
107            // Copied from the old Writer:
108            flags: self.flags,
109            bounds_check_policies: self.bounds_check_policies,
110            zero_initialize_workgroup_memory: self.zero_initialize_workgroup_memory,
111            capabilities_available: take(&mut self.capabilities_available),
112            binding_map: take(&mut self.binding_map),
113
114            // Initialized afresh:
115            id_gen,
116            void_type,
117            gl450_ext_inst_id,
118
119            // Recycled:
120            capabilities_used: take(&mut self.capabilities_used).recycle(),
121            extensions_used: take(&mut self.extensions_used).recycle(),
122            physical_layout: self.physical_layout.clone().recycle(),
123            logical_layout: take(&mut self.logical_layout).recycle(),
124            debugs: take(&mut self.debugs).recycle(),
125            annotations: take(&mut self.annotations).recycle(),
126            lookup_type: take(&mut self.lookup_type).recycle(),
127            lookup_function: take(&mut self.lookup_function).recycle(),
128            lookup_function_type: take(&mut self.lookup_function_type).recycle(),
129            constant_ids: take(&mut self.constant_ids).recycle(),
130            cached_constants: take(&mut self.cached_constants).recycle(),
131            global_variables: take(&mut self.global_variables).recycle(),
132            saved_cached: take(&mut self.saved_cached).recycle(),
133            temp_list: take(&mut self.temp_list).recycle(),
134        };
135
136        *self = fresh;
137
138        self.capabilities_used.insert(spirv::Capability::Shader);
139    }
140
141    /// Indicate that the code requires any one of the listed capabilities.
142    ///
143    /// If nothing in `capabilities` appears in the available capabilities
144    /// specified in the [`Options`] from which this `Writer` was created,
145    /// return an error. The `what` string is used in the error message to
146    /// explain what provoked the requirement. (If no available capabilities were
147    /// given, assume everything is available.)
148    ///
149    /// The first acceptable capability will be added to this `Writer`'s
150    /// [`capabilities_used`] table, and an `OpCapability` emitted for it in the
151    /// result. For this reason, more specific capabilities should be listed
152    /// before more general.
153    ///
154    /// [`capabilities_used`]: Writer::capabilities_used
155    pub(super) fn require_any(
156        &mut self,
157        what: &'static str,
158        capabilities: &[spirv::Capability],
159    ) -> Result<(), Error> {
160        match *capabilities {
161            [] => Ok(()),
162            [first, ..] => {
163                // Find the first acceptable capability, or return an error if
164                // there is none.
165                let selected = match self.capabilities_available {
166                    None => first,
167                    Some(ref available) => {
168                        match capabilities.iter().find(|cap| available.contains(cap)) {
169                            Some(&cap) => cap,
170                            None => {
171                                return Err(Error::MissingCapabilities(what, capabilities.to_vec()))
172                            }
173                        }
174                    }
175                };
176                self.capabilities_used.insert(selected);
177                Ok(())
178            }
179        }
180    }
181
182    /// Indicate that the code uses the given extension.
183    pub(super) fn use_extension(&mut self, extension: &'static str) {
184        self.extensions_used.insert(extension);
185    }
186
187    pub(super) fn get_type_id(&mut self, lookup_ty: LookupType) -> Word {
188        match self.lookup_type.entry(lookup_ty) {
189            Entry::Occupied(e) => *e.get(),
190            Entry::Vacant(e) => {
191                let local = match lookup_ty {
192                    LookupType::Handle(_handle) => unreachable!("Handles are populated at start"),
193                    LookupType::Local(local) => local,
194                };
195
196                let id = self.id_gen.next();
197                e.insert(id);
198                self.write_type_declaration_local(id, local);
199                id
200            }
201        }
202    }
203
204    pub(super) fn get_expression_lookup_type(&mut self, tr: &TypeResolution) -> LookupType {
205        match *tr {
206            TypeResolution::Handle(ty_handle) => LookupType::Handle(ty_handle),
207            TypeResolution::Value(ref inner) => {
208                LookupType::Local(LocalType::from_inner(inner).unwrap())
209            }
210        }
211    }
212
213    pub(super) fn get_expression_type_id(&mut self, tr: &TypeResolution) -> Word {
214        let lookup_ty = self.get_expression_lookup_type(tr);
215        self.get_type_id(lookup_ty)
216    }
217
218    pub(super) fn get_pointer_id(
219        &mut self,
220        handle: Handle<crate::Type>,
221        class: spirv::StorageClass,
222    ) -> Word {
223        self.get_type_id(LookupType::Local(LocalType::Pointer {
224            base: handle,
225            class,
226        }))
227    }
228
229    /// Return a SPIR-V type for a pointer to `resolution`.
230    ///
231    /// The given `resolution` must be one that we can represent
232    /// either as a `LocalType::Pointer` or `LocalType::LocalPointer`.
233    pub(super) fn get_resolution_pointer_id(
234        &mut self,
235        resolution: &TypeResolution,
236        class: spirv::StorageClass,
237    ) -> Word {
238        match *resolution {
239            TypeResolution::Handle(handle) => self.get_pointer_id(handle, class),
240            TypeResolution::Value(ref inner) => {
241                let base = NumericType::from_inner(inner).unwrap();
242                self.get_type_id(LookupType::Local(LocalType::LocalPointer { base, class }))
243            }
244        }
245    }
246
247    pub(super) fn get_uint_type_id(&mut self) -> Word {
248        let local_type = LocalType::Numeric(NumericType::Scalar(crate::Scalar::U32));
249        self.get_type_id(local_type.into())
250    }
251
252    pub(super) fn get_float_type_id(&mut self) -> Word {
253        let local_type = LocalType::Numeric(NumericType::Scalar(crate::Scalar::F32));
254        self.get_type_id(local_type.into())
255    }
256
257    pub(super) fn get_uint3_type_id(&mut self) -> Word {
258        let local_type = LocalType::Numeric(NumericType::Vector {
259            size: crate::VectorSize::Tri,
260            scalar: crate::Scalar::U32,
261        });
262        self.get_type_id(local_type.into())
263    }
264
265    pub(super) fn get_float_pointer_type_id(&mut self, class: spirv::StorageClass) -> Word {
266        let local_type = LocalType::LocalPointer {
267            base: NumericType::Scalar(crate::Scalar::F32),
268            class,
269        };
270        self.get_type_id(local_type.into())
271    }
272
273    pub(super) fn get_uint3_pointer_type_id(&mut self, class: spirv::StorageClass) -> Word {
274        let local_type = LocalType::LocalPointer {
275            base: NumericType::Vector {
276                size: crate::VectorSize::Tri,
277                scalar: crate::Scalar::U32,
278            },
279            class,
280        };
281        self.get_type_id(local_type.into())
282    }
283
284    pub(super) fn get_bool_type_id(&mut self) -> Word {
285        let local_type = LocalType::Numeric(NumericType::Scalar(crate::Scalar::BOOL));
286        self.get_type_id(local_type.into())
287    }
288
289    pub(super) fn get_bool3_type_id(&mut self) -> Word {
290        let local_type = LocalType::Numeric(NumericType::Vector {
291            size: crate::VectorSize::Tri,
292            scalar: crate::Scalar::BOOL,
293        });
294        self.get_type_id(local_type.into())
295    }
296
297    pub(super) fn decorate(&mut self, id: Word, decoration: spirv::Decoration, operands: &[Word]) {
298        self.annotations
299            .push(Instruction::decorate(id, decoration, operands));
300    }
301
302    fn write_function(
303        &mut self,
304        ir_function: &crate::Function,
305        info: &FunctionInfo,
306        ir_module: &crate::Module,
307        mut interface: Option<FunctionInterface>,
308        debug_info: &Option<DebugInfoInner>,
309    ) -> Result<Word, Error> {
310        log::trace!("Generating code for {:?}", ir_function.name);
311        let mut function = Function::default();
312
313        let prelude_id = self.id_gen.next();
314        let mut prelude = Block::new(prelude_id);
315        let mut ep_context = EntryPointContext {
316            argument_ids: Vec::new(),
317            results: Vec::new(),
318        };
319
320        let mut local_invocation_id = None;
321
322        let mut parameter_type_ids = Vec::with_capacity(ir_function.arguments.len());
323        for argument in ir_function.arguments.iter() {
324            let class = spirv::StorageClass::Input;
325            let handle_ty = ir_module.types[argument.ty].inner.is_handle();
326            let argument_type_id = match handle_ty {
327                true => self.get_pointer_id(argument.ty, spirv::StorageClass::UniformConstant),
328                false => self.get_type_id(LookupType::Handle(argument.ty)),
329            };
330
331            if let Some(ref mut iface) = interface {
332                let id = if let Some(ref binding) = argument.binding {
333                    let name = argument.name.as_deref();
334
335                    let varying_id = self.write_varying(
336                        ir_module,
337                        iface.stage,
338                        class,
339                        name,
340                        argument.ty,
341                        binding,
342                    )?;
343                    iface.varying_ids.push(varying_id);
344                    let id = self.id_gen.next();
345                    prelude
346                        .body
347                        .push(Instruction::load(argument_type_id, id, varying_id, None));
348
349                    if binding == &crate::Binding::BuiltIn(crate::BuiltIn::LocalInvocationId) {
350                        local_invocation_id = Some(id);
351                    }
352
353                    id
354                } else if let crate::TypeInner::Struct { ref members, .. } =
355                    ir_module.types[argument.ty].inner
356                {
357                    let struct_id = self.id_gen.next();
358                    let mut constituent_ids = Vec::with_capacity(members.len());
359                    for member in members {
360                        let type_id = self.get_type_id(LookupType::Handle(member.ty));
361                        let name = member.name.as_deref();
362                        let binding = member.binding.as_ref().unwrap();
363                        let varying_id = self.write_varying(
364                            ir_module,
365                            iface.stage,
366                            class,
367                            name,
368                            member.ty,
369                            binding,
370                        )?;
371                        iface.varying_ids.push(varying_id);
372                        let id = self.id_gen.next();
373                        prelude
374                            .body
375                            .push(Instruction::load(type_id, id, varying_id, None));
376                        constituent_ids.push(id);
377
378                        if binding == &crate::Binding::BuiltIn(crate::BuiltIn::GlobalInvocationId) {
379                            local_invocation_id = Some(id);
380                        }
381                    }
382                    prelude.body.push(Instruction::composite_construct(
383                        argument_type_id,
384                        struct_id,
385                        &constituent_ids,
386                    ));
387                    struct_id
388                } else {
389                    unreachable!("Missing argument binding on an entry point");
390                };
391                ep_context.argument_ids.push(id);
392            } else {
393                let argument_id = self.id_gen.next();
394                let instruction = Instruction::function_parameter(argument_type_id, argument_id);
395                if self.flags.contains(WriterFlags::DEBUG) {
396                    if let Some(ref name) = argument.name {
397                        self.debugs.push(Instruction::name(argument_id, name));
398                    }
399                }
400                function.parameters.push(FunctionArgument {
401                    instruction,
402                    handle_id: if handle_ty {
403                        let id = self.id_gen.next();
404                        prelude.body.push(Instruction::load(
405                            self.get_type_id(LookupType::Handle(argument.ty)),
406                            id,
407                            argument_id,
408                            None,
409                        ));
410                        id
411                    } else {
412                        0
413                    },
414                });
415                parameter_type_ids.push(argument_type_id);
416            };
417        }
418
419        let return_type_id = match ir_function.result {
420            Some(ref result) => {
421                if let Some(ref mut iface) = interface {
422                    let mut has_point_size = false;
423                    let class = spirv::StorageClass::Output;
424                    if let Some(ref binding) = result.binding {
425                        has_point_size |=
426                            *binding == crate::Binding::BuiltIn(crate::BuiltIn::PointSize);
427                        let type_id = self.get_type_id(LookupType::Handle(result.ty));
428                        let varying_id = self.write_varying(
429                            ir_module,
430                            iface.stage,
431                            class,
432                            None,
433                            result.ty,
434                            binding,
435                        )?;
436                        iface.varying_ids.push(varying_id);
437                        ep_context.results.push(ResultMember {
438                            id: varying_id,
439                            type_id,
440                            built_in: binding.to_built_in(),
441                        });
442                    } else if let crate::TypeInner::Struct { ref members, .. } =
443                        ir_module.types[result.ty].inner
444                    {
445                        for member in members {
446                            let type_id = self.get_type_id(LookupType::Handle(member.ty));
447                            let name = member.name.as_deref();
448                            let binding = member.binding.as_ref().unwrap();
449                            has_point_size |=
450                                *binding == crate::Binding::BuiltIn(crate::BuiltIn::PointSize);
451                            let varying_id = self.write_varying(
452                                ir_module,
453                                iface.stage,
454                                class,
455                                name,
456                                member.ty,
457                                binding,
458                            )?;
459                            iface.varying_ids.push(varying_id);
460                            ep_context.results.push(ResultMember {
461                                id: varying_id,
462                                type_id,
463                                built_in: binding.to_built_in(),
464                            });
465                        }
466                    } else {
467                        unreachable!("Missing result binding on an entry point");
468                    }
469
470                    if self.flags.contains(WriterFlags::FORCE_POINT_SIZE)
471                        && iface.stage == crate::ShaderStage::Vertex
472                        && !has_point_size
473                    {
474                        // add point size artificially
475                        let varying_id = self.id_gen.next();
476                        let pointer_type_id = self.get_float_pointer_type_id(class);
477                        Instruction::variable(pointer_type_id, varying_id, class, None)
478                            .to_words(&mut self.logical_layout.declarations);
479                        self.decorate(
480                            varying_id,
481                            spirv::Decoration::BuiltIn,
482                            &[spirv::BuiltIn::PointSize as u32],
483                        );
484                        iface.varying_ids.push(varying_id);
485
486                        let default_value_id = self.get_constant_scalar(crate::Literal::F32(1.0));
487                        prelude
488                            .body
489                            .push(Instruction::store(varying_id, default_value_id, None));
490                    }
491                    self.void_type
492                } else {
493                    self.get_type_id(LookupType::Handle(result.ty))
494                }
495            }
496            None => self.void_type,
497        };
498
499        let lookup_function_type = LookupFunctionType {
500            parameter_type_ids,
501            return_type_id,
502        };
503
504        let function_id = self.id_gen.next();
505        if self.flags.contains(WriterFlags::DEBUG) {
506            if let Some(ref name) = ir_function.name {
507                self.debugs.push(Instruction::name(function_id, name));
508            }
509        }
510
511        let function_type = self.get_function_type(lookup_function_type);
512        function.signature = Some(Instruction::function(
513            return_type_id,
514            function_id,
515            spirv::FunctionControl::empty(),
516            function_type,
517        ));
518
519        if interface.is_some() {
520            function.entry_point_context = Some(ep_context);
521        }
522
523        // fill up the `GlobalVariable::access_id`
524        for gv in self.global_variables.iter_mut() {
525            gv.reset_for_function();
526        }
527        for (handle, var) in ir_module.global_variables.iter() {
528            if info[handle].is_empty() {
529                continue;
530            }
531
532            let mut gv = self.global_variables[handle].clone();
533            if let Some(ref mut iface) = interface {
534                // Have to include global variables in the interface
535                if self.physical_layout.version >= 0x10400 {
536                    iface.varying_ids.push(gv.var_id);
537                }
538            }
539
540            // Handle globals are pre-emitted and should be loaded automatically.
541            //
542            // Any that are binding arrays we skip as we cannot load the array, we must load the result after indexing.
543            match ir_module.types[var.ty].inner {
544                crate::TypeInner::BindingArray { .. } => {
545                    gv.access_id = gv.var_id;
546                }
547                _ => {
548                    if var.space == crate::AddressSpace::Handle {
549                        let var_type_id = self.get_type_id(LookupType::Handle(var.ty));
550                        let id = self.id_gen.next();
551                        prelude
552                            .body
553                            .push(Instruction::load(var_type_id, id, gv.var_id, None));
554                        gv.access_id = gv.var_id;
555                        gv.handle_id = id;
556                    } else if global_needs_wrapper(ir_module, var) {
557                        let class = map_storage_class(var.space);
558                        let pointer_type_id = self.get_pointer_id(var.ty, class);
559                        let index_id = self.get_index_constant(0);
560                        let id = self.id_gen.next();
561                        prelude.body.push(Instruction::access_chain(
562                            pointer_type_id,
563                            id,
564                            gv.var_id,
565                            &[index_id],
566                        ));
567                        gv.access_id = id;
568                    } else {
569                        // by default, the variable ID is accessed as is
570                        gv.access_id = gv.var_id;
571                    };
572                }
573            }
574
575            // work around borrow checking in the presence of `self.xxx()` calls
576            self.global_variables[handle] = gv;
577        }
578
579        // Create a `BlockContext` for generating SPIR-V for the function's
580        // body.
581        let mut context = BlockContext {
582            ir_module,
583            ir_function,
584            fun_info: info,
585            function: &mut function,
586            // Re-use the cached expression table from prior functions.
587            cached: std::mem::take(&mut self.saved_cached),
588
589            // Steal the Writer's temp list for a bit.
590            temp_list: std::mem::take(&mut self.temp_list),
591            writer: self,
592            expression_constness: super::ExpressionConstnessTracker::from_arena(
593                &ir_function.expressions,
594            ),
595        };
596
597        // fill up the pre-emitted and const expressions
598        context.cached.reset(ir_function.expressions.len());
599        for (handle, expr) in ir_function.expressions.iter() {
600            if (expr.needs_pre_emit() && !matches!(*expr, crate::Expression::LocalVariable(_)))
601                || context.expression_constness.is_const(handle)
602            {
603                context.cache_expression_value(handle, &mut prelude)?;
604            }
605        }
606
607        for (handle, variable) in ir_function.local_variables.iter() {
608            let id = context.gen_id();
609
610            if context.writer.flags.contains(WriterFlags::DEBUG) {
611                if let Some(ref name) = variable.name {
612                    context.writer.debugs.push(Instruction::name(id, name));
613                }
614            }
615
616            let init_word = variable.init.map(|constant| context.cached[constant]);
617            let pointer_type_id = context
618                .writer
619                .get_pointer_id(variable.ty, spirv::StorageClass::Function);
620            let instruction = Instruction::variable(
621                pointer_type_id,
622                id,
623                spirv::StorageClass::Function,
624                init_word.or_else(|| match ir_module.types[variable.ty].inner {
625                    crate::TypeInner::RayQuery => None,
626                    _ => {
627                        let type_id = context.get_type_id(LookupType::Handle(variable.ty));
628                        Some(context.writer.write_constant_null(type_id))
629                    }
630                }),
631            );
632            context
633                .function
634                .variables
635                .insert(handle, LocalVariable { id, instruction });
636        }
637
638        for (handle, expr) in ir_function.expressions.iter() {
639            match *expr {
640                crate::Expression::LocalVariable(_) => {
641                    // Cache the `OpVariable` instruction we generated above as
642                    // the value of this expression.
643                    context.cache_expression_value(handle, &mut prelude)?;
644                }
645                crate::Expression::Access { base, .. }
646                | crate::Expression::AccessIndex { base, .. } => {
647                    // Count references to `base` by `Access` and `AccessIndex`
648                    // instructions. See `access_uses` for details.
649                    *context.function.access_uses.entry(base).or_insert(0) += 1;
650                }
651                _ => {}
652            }
653        }
654
655        let next_id = context.gen_id();
656
657        context
658            .function
659            .consume(prelude, Instruction::branch(next_id));
660
661        let workgroup_vars_init_exit_block_id =
662            match (context.writer.zero_initialize_workgroup_memory, interface) {
663                (
664                    super::ZeroInitializeWorkgroupMemoryMode::Polyfill,
665                    Some(
666                        ref mut interface @ FunctionInterface {
667                            stage: crate::ShaderStage::Compute,
668                            ..
669                        },
670                    ),
671                ) => context.writer.generate_workgroup_vars_init_block(
672                    next_id,
673                    ir_module,
674                    info,
675                    local_invocation_id,
676                    interface,
677                    context.function,
678                ),
679                _ => None,
680            };
681
682        let main_id = if let Some(exit_id) = workgroup_vars_init_exit_block_id {
683            exit_id
684        } else {
685            next_id
686        };
687
688        context.write_function_body(main_id, debug_info.as_ref())?;
689
690        // Consume the `BlockContext`, ending its borrows and letting the
691        // `Writer` steal back its cached expression table and temp_list.
692        let BlockContext {
693            cached, temp_list, ..
694        } = context;
695        self.saved_cached = cached;
696        self.temp_list = temp_list;
697
698        function.to_words(&mut self.logical_layout.function_definitions);
699        Instruction::function_end().to_words(&mut self.logical_layout.function_definitions);
700
701        Ok(function_id)
702    }
703
704    fn write_execution_mode(
705        &mut self,
706        function_id: Word,
707        mode: spirv::ExecutionMode,
708    ) -> Result<(), Error> {
709        //self.check(mode.required_capabilities())?;
710        Instruction::execution_mode(function_id, mode, &[])
711            .to_words(&mut self.logical_layout.execution_modes);
712        Ok(())
713    }
714
715    // TODO Move to instructions module
716    fn write_entry_point(
717        &mut self,
718        entry_point: &crate::EntryPoint,
719        info: &FunctionInfo,
720        ir_module: &crate::Module,
721        debug_info: &Option<DebugInfoInner>,
722    ) -> Result<Instruction, Error> {
723        let mut interface_ids = Vec::new();
724        let function_id = self.write_function(
725            &entry_point.function,
726            info,
727            ir_module,
728            Some(FunctionInterface {
729                varying_ids: &mut interface_ids,
730                stage: entry_point.stage,
731            }),
732            debug_info,
733        )?;
734
735        let exec_model = match entry_point.stage {
736            crate::ShaderStage::Vertex => spirv::ExecutionModel::Vertex,
737            crate::ShaderStage::Fragment => {
738                self.write_execution_mode(function_id, spirv::ExecutionMode::OriginUpperLeft)?;
739                if let Some(ref result) = entry_point.function.result {
740                    if contains_builtin(
741                        result.binding.as_ref(),
742                        result.ty,
743                        &ir_module.types,
744                        crate::BuiltIn::FragDepth,
745                    ) {
746                        self.write_execution_mode(
747                            function_id,
748                            spirv::ExecutionMode::DepthReplacing,
749                        )?;
750                    }
751                }
752                spirv::ExecutionModel::Fragment
753            }
754            crate::ShaderStage::Compute => {
755                let execution_mode = spirv::ExecutionMode::LocalSize;
756                //self.check(execution_mode.required_capabilities())?;
757                Instruction::execution_mode(
758                    function_id,
759                    execution_mode,
760                    &entry_point.workgroup_size,
761                )
762                .to_words(&mut self.logical_layout.execution_modes);
763                spirv::ExecutionModel::GLCompute
764            }
765        };
766        //self.check(exec_model.required_capabilities())?;
767
768        Ok(Instruction::entry_point(
769            exec_model,
770            function_id,
771            &entry_point.name,
772            interface_ids.as_slice(),
773        ))
774    }
775
776    fn make_scalar(&mut self, id: Word, scalar: crate::Scalar) -> Instruction {
777        use crate::ScalarKind as Sk;
778
779        let bits = (scalar.width * BITS_PER_BYTE) as u32;
780        match scalar.kind {
781            Sk::Sint | Sk::Uint => {
782                let signedness = if scalar.kind == Sk::Sint {
783                    super::instructions::Signedness::Signed
784                } else {
785                    super::instructions::Signedness::Unsigned
786                };
787                let cap = match bits {
788                    8 => Some(spirv::Capability::Int8),
789                    16 => Some(spirv::Capability::Int16),
790                    64 => Some(spirv::Capability::Int64),
791                    _ => None,
792                };
793                if let Some(cap) = cap {
794                    self.capabilities_used.insert(cap);
795                }
796                Instruction::type_int(id, bits, signedness)
797            }
798            Sk::Float => {
799                if bits == 64 {
800                    self.capabilities_used.insert(spirv::Capability::Float64);
801                }
802                Instruction::type_float(id, bits)
803            }
804            Sk::Bool => Instruction::type_bool(id),
805            Sk::AbstractInt | Sk::AbstractFloat => {
806                unreachable!("abstract types should never reach the backend");
807            }
808        }
809    }
810
811    fn request_type_capabilities(&mut self, inner: &crate::TypeInner) -> Result<(), Error> {
812        match *inner {
813            crate::TypeInner::Image {
814                dim,
815                arrayed,
816                class,
817            } => {
818                let sampled = match class {
819                    crate::ImageClass::Sampled { .. } => true,
820                    crate::ImageClass::Depth { .. } => true,
821                    crate::ImageClass::Storage { format, .. } => {
822                        self.request_image_format_capabilities(format.into())?;
823                        false
824                    }
825                };
826
827                match dim {
828                    crate::ImageDimension::D1 => {
829                        if sampled {
830                            self.require_any("sampled 1D images", &[spirv::Capability::Sampled1D])?;
831                        } else {
832                            self.require_any("1D storage images", &[spirv::Capability::Image1D])?;
833                        }
834                    }
835                    crate::ImageDimension::Cube if arrayed => {
836                        if sampled {
837                            self.require_any(
838                                "sampled cube array images",
839                                &[spirv::Capability::SampledCubeArray],
840                            )?;
841                        } else {
842                            self.require_any(
843                                "cube array storage images",
844                                &[spirv::Capability::ImageCubeArray],
845                            )?;
846                        }
847                    }
848                    _ => {}
849                }
850            }
851            crate::TypeInner::AccelerationStructure => {
852                self.require_any("Acceleration Structure", &[spirv::Capability::RayQueryKHR])?;
853            }
854            crate::TypeInner::RayQuery => {
855                self.require_any("Ray Query", &[spirv::Capability::RayQueryKHR])?;
856            }
857            crate::TypeInner::Atomic(crate::Scalar { width: 8, kind: _ }) => {
858                self.require_any("64 bit integer atomics", &[spirv::Capability::Int64Atomics])?;
859            }
860            crate::TypeInner::Atomic(crate::Scalar {
861                width: 4,
862                kind: crate::ScalarKind::Float,
863            }) => {
864                self.require_any(
865                    "32 bit floating-point atomics",
866                    &[spirv::Capability::AtomicFloat32AddEXT],
867                )?;
868                self.use_extension("SPV_EXT_shader_atomic_float_add");
869            }
870            _ => {}
871        }
872        Ok(())
873    }
874
875    fn write_numeric_type_declaration_local(&mut self, id: Word, numeric: NumericType) {
876        let instruction =
877            match numeric {
878                NumericType::Scalar(scalar) => self.make_scalar(id, scalar),
879                NumericType::Vector { size, scalar } => {
880                    let scalar_id = self.get_type_id(LookupType::Local(LocalType::Numeric(
881                        NumericType::Scalar(scalar),
882                    )));
883                    Instruction::type_vector(id, scalar_id, size)
884                }
885                NumericType::Matrix {
886                    columns,
887                    rows,
888                    scalar,
889                } => {
890                    let column_id = self.get_type_id(LookupType::Local(LocalType::Numeric(
891                        NumericType::Vector { size: rows, scalar },
892                    )));
893                    Instruction::type_matrix(id, column_id, columns)
894                }
895            };
896
897        instruction.to_words(&mut self.logical_layout.declarations);
898    }
899
900    fn write_type_declaration_local(&mut self, id: Word, local_ty: LocalType) {
901        let instruction = match local_ty {
902            LocalType::Numeric(numeric) => {
903                self.write_numeric_type_declaration_local(id, numeric);
904                return;
905            }
906            LocalType::LocalPointer { base, class } => {
907                let base_id = self.get_type_id(LookupType::Local(LocalType::Numeric(base)));
908                Instruction::type_pointer(id, class, base_id)
909            }
910            LocalType::Pointer { base, class } => {
911                let type_id = self.get_type_id(LookupType::Handle(base));
912                Instruction::type_pointer(id, class, type_id)
913            }
914            LocalType::Image(image) => {
915                let local_type = LocalType::Numeric(NumericType::Scalar(image.sampled_type));
916                let type_id = self.get_type_id(LookupType::Local(local_type));
917                Instruction::type_image(id, type_id, image.dim, image.flags, image.image_format)
918            }
919            LocalType::Sampler => Instruction::type_sampler(id),
920            LocalType::SampledImage { image_type_id } => {
921                Instruction::type_sampled_image(id, image_type_id)
922            }
923            LocalType::BindingArray { base, size } => {
924                let inner_ty = self.get_type_id(LookupType::Handle(base));
925                let scalar_id = self.get_constant_scalar(crate::Literal::U32(size));
926                Instruction::type_array(id, inner_ty, scalar_id)
927            }
928            LocalType::PointerToBindingArray { base, size, space } => {
929                let inner_ty =
930                    self.get_type_id(LookupType::Local(LocalType::BindingArray { base, size }));
931                let class = map_storage_class(space);
932                Instruction::type_pointer(id, class, inner_ty)
933            }
934            LocalType::AccelerationStructure => Instruction::type_acceleration_structure(id),
935            LocalType::RayQuery => Instruction::type_ray_query(id),
936        };
937
938        instruction.to_words(&mut self.logical_layout.declarations);
939    }
940
941    fn write_type_declaration_arena(
942        &mut self,
943        arena: &UniqueArena<crate::Type>,
944        handle: Handle<crate::Type>,
945    ) -> Result<Word, Error> {
946        let ty = &arena[handle];
947        // If it's a type that needs SPIR-V capabilities, request them now.
948        // This needs to happen regardless of the LocalType lookup succeeding,
949        // because some types which map to the same LocalType have different
950        // capability requirements. See https://github.com/gfx-rs/wgpu/issues/5569
951        self.request_type_capabilities(&ty.inner)?;
952        let id = if let Some(local) = LocalType::from_inner(&ty.inner) {
953            // This type can be represented as a `LocalType`, so check if we've
954            // already written an instruction for it. If not, do so now, with
955            // `write_type_declaration_local`.
956            match self.lookup_type.entry(LookupType::Local(local)) {
957                // We already have an id for this `LocalType`.
958                Entry::Occupied(e) => *e.get(),
959
960                // It's a type we haven't seen before.
961                Entry::Vacant(e) => {
962                    let id = self.id_gen.next();
963                    e.insert(id);
964
965                    self.write_type_declaration_local(id, local);
966
967                    id
968                }
969            }
970        } else {
971            use spirv::Decoration;
972
973            let id = self.id_gen.next();
974            let instruction = match ty.inner {
975                crate::TypeInner::Array { base, size, stride } => {
976                    self.decorate(id, Decoration::ArrayStride, &[stride]);
977
978                    let type_id = self.get_type_id(LookupType::Handle(base));
979                    match size {
980                        crate::ArraySize::Constant(length) => {
981                            let length_id = self.get_index_constant(length.get());
982                            Instruction::type_array(id, type_id, length_id)
983                        }
984                        crate::ArraySize::Pending(_) => unreachable!(),
985                        crate::ArraySize::Dynamic => Instruction::type_runtime_array(id, type_id),
986                    }
987                }
988                crate::TypeInner::BindingArray { base, size } => {
989                    let type_id = self.get_type_id(LookupType::Handle(base));
990                    match size {
991                        crate::ArraySize::Constant(length) => {
992                            let length_id = self.get_index_constant(length.get());
993                            Instruction::type_array(id, type_id, length_id)
994                        }
995                        crate::ArraySize::Pending(_) => unreachable!(),
996                        crate::ArraySize::Dynamic => Instruction::type_runtime_array(id, type_id),
997                    }
998                }
999                crate::TypeInner::Struct {
1000                    ref members,
1001                    span: _,
1002                } => {
1003                    let mut has_runtime_array = false;
1004                    let mut member_ids = Vec::with_capacity(members.len());
1005                    for (index, member) in members.iter().enumerate() {
1006                        let member_ty = &arena[member.ty];
1007                        match member_ty.inner {
1008                            crate::TypeInner::Array {
1009                                base: _,
1010                                size: crate::ArraySize::Dynamic,
1011                                stride: _,
1012                            } => {
1013                                has_runtime_array = true;
1014                            }
1015                            _ => (),
1016                        }
1017                        self.decorate_struct_member(id, index, member, arena)?;
1018                        let member_id = self.get_type_id(LookupType::Handle(member.ty));
1019                        member_ids.push(member_id);
1020                    }
1021                    if has_runtime_array {
1022                        self.decorate(id, Decoration::Block, &[]);
1023                    }
1024                    Instruction::type_struct(id, member_ids.as_slice())
1025                }
1026
1027                // These all have TypeLocal representations, so they should have been
1028                // handled by `write_type_declaration_local` above.
1029                crate::TypeInner::Scalar(_)
1030                | crate::TypeInner::Atomic(_)
1031                | crate::TypeInner::Vector { .. }
1032                | crate::TypeInner::Matrix { .. }
1033                | crate::TypeInner::Pointer { .. }
1034                | crate::TypeInner::ValuePointer { .. }
1035                | crate::TypeInner::Image { .. }
1036                | crate::TypeInner::Sampler { .. }
1037                | crate::TypeInner::AccelerationStructure
1038                | crate::TypeInner::RayQuery => unreachable!(),
1039            };
1040
1041            instruction.to_words(&mut self.logical_layout.declarations);
1042            id
1043        };
1044
1045        // Add this handle as a new alias for that type.
1046        self.lookup_type.insert(LookupType::Handle(handle), id);
1047
1048        if self.flags.contains(WriterFlags::DEBUG) {
1049            if let Some(ref name) = ty.name {
1050                self.debugs.push(Instruction::name(id, name));
1051            }
1052        }
1053
1054        Ok(id)
1055    }
1056
1057    fn request_image_format_capabilities(
1058        &mut self,
1059        format: spirv::ImageFormat,
1060    ) -> Result<(), Error> {
1061        use spirv::ImageFormat as If;
1062        match format {
1063            If::Rg32f
1064            | If::Rg16f
1065            | If::R11fG11fB10f
1066            | If::R16f
1067            | If::Rgba16
1068            | If::Rgb10A2
1069            | If::Rg16
1070            | If::Rg8
1071            | If::R16
1072            | If::R8
1073            | If::Rgba16Snorm
1074            | If::Rg16Snorm
1075            | If::Rg8Snorm
1076            | If::R16Snorm
1077            | If::R8Snorm
1078            | If::Rg32i
1079            | If::Rg16i
1080            | If::Rg8i
1081            | If::R16i
1082            | If::R8i
1083            | If::Rgb10a2ui
1084            | If::Rg32ui
1085            | If::Rg16ui
1086            | If::Rg8ui
1087            | If::R16ui
1088            | If::R8ui => self.require_any(
1089                "storage image format",
1090                &[spirv::Capability::StorageImageExtendedFormats],
1091            ),
1092            If::R64ui | If::R64i => {
1093                self.use_extension("SPV_EXT_shader_image_int64");
1094                self.require_any(
1095                    "64-bit integer storage image format",
1096                    &[spirv::Capability::Int64ImageEXT],
1097                )
1098            }
1099            If::Unknown
1100            | If::Rgba32f
1101            | If::Rgba16f
1102            | If::R32f
1103            | If::Rgba8
1104            | If::Rgba8Snorm
1105            | If::Rgba32i
1106            | If::Rgba16i
1107            | If::Rgba8i
1108            | If::R32i
1109            | If::Rgba32ui
1110            | If::Rgba16ui
1111            | If::Rgba8ui
1112            | If::R32ui => Ok(()),
1113        }
1114    }
1115
1116    pub(super) fn get_index_constant(&mut self, index: Word) -> Word {
1117        self.get_constant_scalar(crate::Literal::U32(index))
1118    }
1119
1120    pub(super) fn get_constant_scalar_with(
1121        &mut self,
1122        value: u8,
1123        scalar: crate::Scalar,
1124    ) -> Result<Word, Error> {
1125        Ok(
1126            self.get_constant_scalar(crate::Literal::new(value, scalar).ok_or(
1127                Error::Validation("Unexpected kind and/or width for Literal"),
1128            )?),
1129        )
1130    }
1131
1132    pub(super) fn get_constant_scalar(&mut self, value: crate::Literal) -> Word {
1133        let scalar = CachedConstant::Literal(value.into());
1134        if let Some(&id) = self.cached_constants.get(&scalar) {
1135            return id;
1136        }
1137        let id = self.id_gen.next();
1138        self.write_constant_scalar(id, &value, None);
1139        self.cached_constants.insert(scalar, id);
1140        id
1141    }
1142
1143    fn write_constant_scalar(
1144        &mut self,
1145        id: Word,
1146        value: &crate::Literal,
1147        debug_name: Option<&String>,
1148    ) {
1149        if self.flags.contains(WriterFlags::DEBUG) {
1150            if let Some(name) = debug_name {
1151                self.debugs.push(Instruction::name(id, name));
1152            }
1153        }
1154        let type_id = self.get_type_id(LookupType::Local(LocalType::Numeric(NumericType::Scalar(
1155            value.scalar(),
1156        ))));
1157        let instruction = match *value {
1158            crate::Literal::F64(value) => {
1159                let bits = value.to_bits();
1160                Instruction::constant_64bit(type_id, id, bits as u32, (bits >> 32) as u32)
1161            }
1162            crate::Literal::F32(value) => Instruction::constant_32bit(type_id, id, value.to_bits()),
1163            crate::Literal::U32(value) => Instruction::constant_32bit(type_id, id, value),
1164            crate::Literal::I32(value) => Instruction::constant_32bit(type_id, id, value as u32),
1165            crate::Literal::U64(value) => {
1166                Instruction::constant_64bit(type_id, id, value as u32, (value >> 32) as u32)
1167            }
1168            crate::Literal::I64(value) => {
1169                Instruction::constant_64bit(type_id, id, value as u32, (value >> 32) as u32)
1170            }
1171            crate::Literal::Bool(true) => Instruction::constant_true(type_id, id),
1172            crate::Literal::Bool(false) => Instruction::constant_false(type_id, id),
1173            crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
1174                unreachable!("Abstract types should not appear in IR presented to backends");
1175            }
1176        };
1177
1178        instruction.to_words(&mut self.logical_layout.declarations);
1179    }
1180
1181    pub(super) fn get_constant_composite(
1182        &mut self,
1183        ty: LookupType,
1184        constituent_ids: &[Word],
1185    ) -> Word {
1186        let composite = CachedConstant::Composite {
1187            ty,
1188            constituent_ids: constituent_ids.to_vec(),
1189        };
1190        if let Some(&id) = self.cached_constants.get(&composite) {
1191            return id;
1192        }
1193        let id = self.id_gen.next();
1194        self.write_constant_composite(id, ty, constituent_ids, None);
1195        self.cached_constants.insert(composite, id);
1196        id
1197    }
1198
1199    fn write_constant_composite(
1200        &mut self,
1201        id: Word,
1202        ty: LookupType,
1203        constituent_ids: &[Word],
1204        debug_name: Option<&String>,
1205    ) {
1206        if self.flags.contains(WriterFlags::DEBUG) {
1207            if let Some(name) = debug_name {
1208                self.debugs.push(Instruction::name(id, name));
1209            }
1210        }
1211        let type_id = self.get_type_id(ty);
1212        Instruction::constant_composite(type_id, id, constituent_ids)
1213            .to_words(&mut self.logical_layout.declarations);
1214    }
1215
1216    pub(super) fn get_constant_null(&mut self, type_id: Word) -> Word {
1217        let null = CachedConstant::ZeroValue(type_id);
1218        if let Some(&id) = self.cached_constants.get(&null) {
1219            return id;
1220        }
1221        let id = self.write_constant_null(type_id);
1222        self.cached_constants.insert(null, id);
1223        id
1224    }
1225
1226    pub(super) fn write_constant_null(&mut self, type_id: Word) -> Word {
1227        let null_id = self.id_gen.next();
1228        Instruction::constant_null(type_id, null_id)
1229            .to_words(&mut self.logical_layout.declarations);
1230        null_id
1231    }
1232
1233    fn write_constant_expr(
1234        &mut self,
1235        handle: Handle<crate::Expression>,
1236        ir_module: &crate::Module,
1237        mod_info: &ModuleInfo,
1238    ) -> Result<Word, Error> {
1239        let id = match ir_module.global_expressions[handle] {
1240            crate::Expression::Literal(literal) => self.get_constant_scalar(literal),
1241            crate::Expression::Constant(constant) => {
1242                let constant = &ir_module.constants[constant];
1243                self.constant_ids[constant.init]
1244            }
1245            crate::Expression::ZeroValue(ty) => {
1246                let type_id = self.get_type_id(LookupType::Handle(ty));
1247                self.get_constant_null(type_id)
1248            }
1249            crate::Expression::Compose { ty, ref components } => {
1250                let component_ids: Vec<_> = crate::proc::flatten_compose(
1251                    ty,
1252                    components,
1253                    &ir_module.global_expressions,
1254                    &ir_module.types,
1255                )
1256                .map(|component| self.constant_ids[component])
1257                .collect();
1258                self.get_constant_composite(LookupType::Handle(ty), component_ids.as_slice())
1259            }
1260            crate::Expression::Splat { size, value } => {
1261                let value_id = self.constant_ids[value];
1262                let component_ids = &[value_id; 4][..size as usize];
1263
1264                let ty = self.get_expression_lookup_type(&mod_info[handle]);
1265
1266                self.get_constant_composite(ty, component_ids)
1267            }
1268            _ => unreachable!(),
1269        };
1270
1271        self.constant_ids[handle] = id;
1272
1273        Ok(id)
1274    }
1275
1276    pub(super) fn write_barrier(&mut self, flags: crate::Barrier, block: &mut Block) {
1277        let memory_scope = if flags.contains(crate::Barrier::STORAGE) {
1278            spirv::Scope::Device
1279        } else {
1280            spirv::Scope::Workgroup
1281        };
1282        let mut semantics = spirv::MemorySemantics::ACQUIRE_RELEASE;
1283        semantics.set(
1284            spirv::MemorySemantics::UNIFORM_MEMORY,
1285            flags.contains(crate::Barrier::STORAGE),
1286        );
1287        semantics.set(
1288            spirv::MemorySemantics::WORKGROUP_MEMORY,
1289            flags.contains(crate::Barrier::WORK_GROUP),
1290        );
1291        let exec_scope_id = if flags.contains(crate::Barrier::SUB_GROUP) {
1292            self.get_index_constant(spirv::Scope::Subgroup as u32)
1293        } else {
1294            self.get_index_constant(spirv::Scope::Workgroup as u32)
1295        };
1296        let mem_scope_id = self.get_index_constant(memory_scope as u32);
1297        let semantics_id = self.get_index_constant(semantics.bits());
1298        block.body.push(Instruction::control_barrier(
1299            exec_scope_id,
1300            mem_scope_id,
1301            semantics_id,
1302        ));
1303    }
1304
1305    fn generate_workgroup_vars_init_block(
1306        &mut self,
1307        entry_id: Word,
1308        ir_module: &crate::Module,
1309        info: &FunctionInfo,
1310        local_invocation_id: Option<Word>,
1311        interface: &mut FunctionInterface,
1312        function: &mut Function,
1313    ) -> Option<Word> {
1314        let body = ir_module
1315            .global_variables
1316            .iter()
1317            .filter(|&(handle, var)| {
1318                !info[handle].is_empty() && var.space == crate::AddressSpace::WorkGroup
1319            })
1320            .map(|(handle, var)| {
1321                // It's safe to use `var_id` here, not `access_id`, because only
1322                // variables in the `Uniform` and `StorageBuffer` address spaces
1323                // get wrapped, and we're initializing `WorkGroup` variables.
1324                let var_id = self.global_variables[handle].var_id;
1325                let var_type_id = self.get_type_id(LookupType::Handle(var.ty));
1326                let init_word = self.get_constant_null(var_type_id);
1327                Instruction::store(var_id, init_word, None)
1328            })
1329            .collect::<Vec<_>>();
1330
1331        if body.is_empty() {
1332            return None;
1333        }
1334
1335        let uint3_type_id = self.get_uint3_type_id();
1336
1337        let mut pre_if_block = Block::new(entry_id);
1338
1339        let local_invocation_id = if let Some(local_invocation_id) = local_invocation_id {
1340            local_invocation_id
1341        } else {
1342            let varying_id = self.id_gen.next();
1343            let class = spirv::StorageClass::Input;
1344            let pointer_type_id = self.get_uint3_pointer_type_id(class);
1345
1346            Instruction::variable(pointer_type_id, varying_id, class, None)
1347                .to_words(&mut self.logical_layout.declarations);
1348
1349            self.decorate(
1350                varying_id,
1351                spirv::Decoration::BuiltIn,
1352                &[spirv::BuiltIn::LocalInvocationId as u32],
1353            );
1354
1355            interface.varying_ids.push(varying_id);
1356            let id = self.id_gen.next();
1357            pre_if_block
1358                .body
1359                .push(Instruction::load(uint3_type_id, id, varying_id, None));
1360
1361            id
1362        };
1363
1364        let zero_id = self.get_constant_null(uint3_type_id);
1365        let bool3_type_id = self.get_bool3_type_id();
1366
1367        let eq_id = self.id_gen.next();
1368        pre_if_block.body.push(Instruction::binary(
1369            spirv::Op::IEqual,
1370            bool3_type_id,
1371            eq_id,
1372            local_invocation_id,
1373            zero_id,
1374        ));
1375
1376        let condition_id = self.id_gen.next();
1377        let bool_type_id = self.get_bool_type_id();
1378        pre_if_block.body.push(Instruction::relational(
1379            spirv::Op::All,
1380            bool_type_id,
1381            condition_id,
1382            eq_id,
1383        ));
1384
1385        let merge_id = self.id_gen.next();
1386        pre_if_block.body.push(Instruction::selection_merge(
1387            merge_id,
1388            spirv::SelectionControl::NONE,
1389        ));
1390
1391        let accept_id = self.id_gen.next();
1392        function.consume(
1393            pre_if_block,
1394            Instruction::branch_conditional(condition_id, accept_id, merge_id),
1395        );
1396
1397        let accept_block = Block {
1398            label_id: accept_id,
1399            body,
1400        };
1401        function.consume(accept_block, Instruction::branch(merge_id));
1402
1403        let mut post_if_block = Block::new(merge_id);
1404
1405        self.write_barrier(crate::Barrier::WORK_GROUP, &mut post_if_block);
1406
1407        let next_id = self.id_gen.next();
1408        function.consume(post_if_block, Instruction::branch(next_id));
1409        Some(next_id)
1410    }
1411
1412    /// Generate an `OpVariable` for one value in an [`EntryPoint`]'s IO interface.
1413    ///
1414    /// The [`Binding`]s of the arguments and result of an [`EntryPoint`]'s
1415    /// [`Function`] describe a SPIR-V shader interface. In SPIR-V, the
1416    /// interface is represented by global variables in the `Input` and `Output`
1417    /// storage classes, with decorations indicating which builtin or location
1418    /// each variable corresponds to.
1419    ///
1420    /// This function emits a single global `OpVariable` for a single value from
1421    /// the interface, and adds appropriate decorations to indicate which
1422    /// builtin or location it represents, how it should be interpolated, and so
1423    /// on. The `class` argument gives the variable's SPIR-V storage class,
1424    /// which should be either [`Input`] or [`Output`].
1425    ///
1426    /// [`Binding`]: crate::Binding
1427    /// [`Function`]: crate::Function
1428    /// [`EntryPoint`]: crate::EntryPoint
1429    /// [`Input`]: spirv::StorageClass::Input
1430    /// [`Output`]: spirv::StorageClass::Output
1431    fn write_varying(
1432        &mut self,
1433        ir_module: &crate::Module,
1434        stage: crate::ShaderStage,
1435        class: spirv::StorageClass,
1436        debug_name: Option<&str>,
1437        ty: Handle<crate::Type>,
1438        binding: &crate::Binding,
1439    ) -> Result<Word, Error> {
1440        let id = self.id_gen.next();
1441        let pointer_type_id = self.get_pointer_id(ty, class);
1442        Instruction::variable(pointer_type_id, id, class, None)
1443            .to_words(&mut self.logical_layout.declarations);
1444
1445        if self
1446            .flags
1447            .contains(WriterFlags::DEBUG | WriterFlags::LABEL_VARYINGS)
1448        {
1449            if let Some(name) = debug_name {
1450                self.debugs.push(Instruction::name(id, name));
1451            }
1452        }
1453
1454        use spirv::{BuiltIn, Decoration};
1455
1456        match *binding {
1457            crate::Binding::Location {
1458                location,
1459                interpolation,
1460                sampling,
1461                second_blend_source,
1462            } => {
1463                self.decorate(id, Decoration::Location, &[location]);
1464
1465                let no_decorations =
1466                    // VUID-StandaloneSpirv-Flat-06202
1467                    // > The Flat, NoPerspective, Sample, and Centroid decorations
1468                    // > must not be used on variables with the Input storage class in a vertex shader
1469                    (class == spirv::StorageClass::Input && stage == crate::ShaderStage::Vertex) ||
1470                    // VUID-StandaloneSpirv-Flat-06201
1471                    // > The Flat, NoPerspective, Sample, and Centroid decorations
1472                    // > must not be used on variables with the Output storage class in a fragment shader
1473                    (class == spirv::StorageClass::Output && stage == crate::ShaderStage::Fragment);
1474
1475                if !no_decorations {
1476                    match interpolation {
1477                        // Perspective-correct interpolation is the default in SPIR-V.
1478                        None | Some(crate::Interpolation::Perspective) => (),
1479                        Some(crate::Interpolation::Flat) => {
1480                            self.decorate(id, Decoration::Flat, &[]);
1481                        }
1482                        Some(crate::Interpolation::Linear) => {
1483                            self.decorate(id, Decoration::NoPerspective, &[]);
1484                        }
1485                    }
1486                    match sampling {
1487                        // Center sampling is the default in SPIR-V.
1488                        None
1489                        | Some(
1490                            crate::Sampling::Center
1491                            | crate::Sampling::First
1492                            | crate::Sampling::Either,
1493                        ) => (),
1494                        Some(crate::Sampling::Centroid) => {
1495                            self.decorate(id, Decoration::Centroid, &[]);
1496                        }
1497                        Some(crate::Sampling::Sample) => {
1498                            self.require_any(
1499                                "per-sample interpolation",
1500                                &[spirv::Capability::SampleRateShading],
1501                            )?;
1502                            self.decorate(id, Decoration::Sample, &[]);
1503                        }
1504                    }
1505                }
1506                if second_blend_source {
1507                    self.decorate(id, Decoration::Index, &[1]);
1508                }
1509            }
1510            crate::Binding::BuiltIn(built_in) => {
1511                use crate::BuiltIn as Bi;
1512                let built_in = match built_in {
1513                    Bi::Position { invariant } => {
1514                        if invariant {
1515                            self.decorate(id, Decoration::Invariant, &[]);
1516                        }
1517
1518                        if class == spirv::StorageClass::Output {
1519                            BuiltIn::Position
1520                        } else {
1521                            BuiltIn::FragCoord
1522                        }
1523                    }
1524                    Bi::ViewIndex => {
1525                        self.require_any("`view_index` built-in", &[spirv::Capability::MultiView])?;
1526                        BuiltIn::ViewIndex
1527                    }
1528                    // vertex
1529                    Bi::BaseInstance => BuiltIn::BaseInstance,
1530                    Bi::BaseVertex => BuiltIn::BaseVertex,
1531                    Bi::ClipDistance => {
1532                        self.require_any(
1533                            "`clip_distance` built-in",
1534                            &[spirv::Capability::ClipDistance],
1535                        )?;
1536                        BuiltIn::ClipDistance
1537                    }
1538                    Bi::CullDistance => {
1539                        self.require_any(
1540                            "`cull_distance` built-in",
1541                            &[spirv::Capability::CullDistance],
1542                        )?;
1543                        BuiltIn::CullDistance
1544                    }
1545                    Bi::InstanceIndex => BuiltIn::InstanceIndex,
1546                    Bi::PointSize => BuiltIn::PointSize,
1547                    Bi::VertexIndex => BuiltIn::VertexIndex,
1548                    Bi::DrawID => BuiltIn::DrawIndex,
1549                    // fragment
1550                    Bi::FragDepth => BuiltIn::FragDepth,
1551                    Bi::PointCoord => BuiltIn::PointCoord,
1552                    Bi::FrontFacing => BuiltIn::FrontFacing,
1553                    Bi::PrimitiveIndex => {
1554                        self.require_any(
1555                            "`primitive_index` built-in",
1556                            &[spirv::Capability::Geometry],
1557                        )?;
1558                        BuiltIn::PrimitiveId
1559                    }
1560                    Bi::SampleIndex => {
1561                        self.require_any(
1562                            "`sample_index` built-in",
1563                            &[spirv::Capability::SampleRateShading],
1564                        )?;
1565
1566                        BuiltIn::SampleId
1567                    }
1568                    Bi::SampleMask => BuiltIn::SampleMask,
1569                    // compute
1570                    Bi::GlobalInvocationId => BuiltIn::GlobalInvocationId,
1571                    Bi::LocalInvocationId => BuiltIn::LocalInvocationId,
1572                    Bi::LocalInvocationIndex => BuiltIn::LocalInvocationIndex,
1573                    Bi::WorkGroupId => BuiltIn::WorkgroupId,
1574                    Bi::WorkGroupSize => BuiltIn::WorkgroupSize,
1575                    Bi::NumWorkGroups => BuiltIn::NumWorkgroups,
1576                    // Subgroup
1577                    Bi::NumSubgroups => {
1578                        self.require_any(
1579                            "`num_subgroups` built-in",
1580                            &[spirv::Capability::GroupNonUniform],
1581                        )?;
1582                        BuiltIn::NumSubgroups
1583                    }
1584                    Bi::SubgroupId => {
1585                        self.require_any(
1586                            "`subgroup_id` built-in",
1587                            &[spirv::Capability::GroupNonUniform],
1588                        )?;
1589                        BuiltIn::SubgroupId
1590                    }
1591                    Bi::SubgroupSize => {
1592                        self.require_any(
1593                            "`subgroup_size` built-in",
1594                            &[
1595                                spirv::Capability::GroupNonUniform,
1596                                spirv::Capability::SubgroupBallotKHR,
1597                            ],
1598                        )?;
1599                        BuiltIn::SubgroupSize
1600                    }
1601                    Bi::SubgroupInvocationId => {
1602                        self.require_any(
1603                            "`subgroup_invocation_id` built-in",
1604                            &[
1605                                spirv::Capability::GroupNonUniform,
1606                                spirv::Capability::SubgroupBallotKHR,
1607                            ],
1608                        )?;
1609                        BuiltIn::SubgroupLocalInvocationId
1610                    }
1611                };
1612
1613                self.decorate(id, Decoration::BuiltIn, &[built_in as u32]);
1614
1615                use crate::ScalarKind as Sk;
1616
1617                // Per the Vulkan spec, `VUID-StandaloneSpirv-Flat-04744`:
1618                //
1619                // > Any variable with integer or double-precision floating-
1620                // > point type and with Input storage class in a fragment
1621                // > shader, must be decorated Flat
1622                if class == spirv::StorageClass::Input && stage == crate::ShaderStage::Fragment {
1623                    let is_flat = match ir_module.types[ty].inner {
1624                        crate::TypeInner::Scalar(scalar)
1625                        | crate::TypeInner::Vector { scalar, .. } => match scalar.kind {
1626                            Sk::Uint | Sk::Sint | Sk::Bool => true,
1627                            Sk::Float => false,
1628                            Sk::AbstractInt | Sk::AbstractFloat => {
1629                                return Err(Error::Validation(
1630                                    "Abstract types should not appear in IR presented to backends",
1631                                ))
1632                            }
1633                        },
1634                        _ => false,
1635                    };
1636
1637                    if is_flat {
1638                        self.decorate(id, Decoration::Flat, &[]);
1639                    }
1640                }
1641            }
1642        }
1643
1644        Ok(id)
1645    }
1646
1647    fn write_global_variable(
1648        &mut self,
1649        ir_module: &crate::Module,
1650        global_variable: &crate::GlobalVariable,
1651    ) -> Result<Word, Error> {
1652        use spirv::Decoration;
1653
1654        let id = self.id_gen.next();
1655        let class = map_storage_class(global_variable.space);
1656
1657        //self.check(class.required_capabilities())?;
1658
1659        if self.flags.contains(WriterFlags::DEBUG) {
1660            if let Some(ref name) = global_variable.name {
1661                self.debugs.push(Instruction::name(id, name));
1662            }
1663        }
1664
1665        let storage_access = match global_variable.space {
1666            crate::AddressSpace::Storage { access } => Some(access),
1667            _ => match ir_module.types[global_variable.ty].inner {
1668                crate::TypeInner::Image {
1669                    class: crate::ImageClass::Storage { access, .. },
1670                    ..
1671                } => Some(access),
1672                _ => None,
1673            },
1674        };
1675        if let Some(storage_access) = storage_access {
1676            if !storage_access.contains(crate::StorageAccess::LOAD) {
1677                self.decorate(id, Decoration::NonReadable, &[]);
1678            }
1679            if !storage_access.contains(crate::StorageAccess::STORE) {
1680                self.decorate(id, Decoration::NonWritable, &[]);
1681            }
1682        }
1683
1684        // Note: we should be able to substitute `binding_array<Foo, 0>`,
1685        // but there is still code that tries to register the pre-substituted type,
1686        // and it is failing on 0.
1687        let mut substitute_inner_type_lookup = None;
1688        if let Some(ref res_binding) = global_variable.binding {
1689            self.decorate(id, Decoration::DescriptorSet, &[res_binding.group]);
1690            self.decorate(id, Decoration::Binding, &[res_binding.binding]);
1691
1692            if let Some(&BindingInfo {
1693                binding_array_size: Some(remapped_binding_array_size),
1694            }) = self.binding_map.get(res_binding)
1695            {
1696                if let crate::TypeInner::BindingArray { base, .. } =
1697                    ir_module.types[global_variable.ty].inner
1698                {
1699                    substitute_inner_type_lookup =
1700                        Some(LookupType::Local(LocalType::PointerToBindingArray {
1701                            base,
1702                            size: remapped_binding_array_size,
1703                            space: global_variable.space,
1704                        }))
1705                }
1706            }
1707        };
1708
1709        let init_word = global_variable
1710            .init
1711            .map(|constant| self.constant_ids[constant]);
1712        let inner_type_id = self.get_type_id(
1713            substitute_inner_type_lookup.unwrap_or(LookupType::Handle(global_variable.ty)),
1714        );
1715
1716        // generate the wrapping structure if needed
1717        let pointer_type_id = if global_needs_wrapper(ir_module, global_variable) {
1718            let wrapper_type_id = self.id_gen.next();
1719
1720            self.decorate(wrapper_type_id, Decoration::Block, &[]);
1721            let member = crate::StructMember {
1722                name: None,
1723                ty: global_variable.ty,
1724                binding: None,
1725                offset: 0,
1726            };
1727            self.decorate_struct_member(wrapper_type_id, 0, &member, &ir_module.types)?;
1728
1729            Instruction::type_struct(wrapper_type_id, &[inner_type_id])
1730                .to_words(&mut self.logical_layout.declarations);
1731
1732            let pointer_type_id = self.id_gen.next();
1733            Instruction::type_pointer(pointer_type_id, class, wrapper_type_id)
1734                .to_words(&mut self.logical_layout.declarations);
1735
1736            pointer_type_id
1737        } else {
1738            // This is a global variable in the Storage address space. The only
1739            // way it could have `global_needs_wrapper() == false` is if it has
1740            // a runtime-sized or binding array.
1741            // Runtime-sized arrays were decorated when iterating through struct content.
1742            // Now binding arrays require Block decorating.
1743            if let crate::AddressSpace::Storage { .. } = global_variable.space {
1744                match ir_module.types[global_variable.ty].inner {
1745                    crate::TypeInner::BindingArray { base, .. } => {
1746                        let ty = &ir_module.types[base];
1747                        let mut should_decorate = true;
1748                        // Check if the type has a runtime array.
1749                        // A normal runtime array gets validated out,
1750                        // so only structs can be with runtime arrays
1751                        if let crate::TypeInner::Struct { ref members, .. } = ty.inner {
1752                            // only the last member in a struct can be dynamically sized
1753                            if let Some(last_member) = members.last() {
1754                                if let &crate::TypeInner::Array {
1755                                    size: crate::ArraySize::Dynamic,
1756                                    ..
1757                                } = &ir_module.types[last_member.ty].inner
1758                                {
1759                                    should_decorate = false;
1760                                }
1761                            }
1762                        }
1763                        if should_decorate {
1764                            let decorated_id = self.get_type_id(LookupType::Handle(base));
1765                            self.decorate(decorated_id, Decoration::Block, &[]);
1766                        }
1767                    }
1768                    _ => (),
1769                };
1770            }
1771            if substitute_inner_type_lookup.is_some() {
1772                inner_type_id
1773            } else {
1774                self.get_pointer_id(global_variable.ty, class)
1775            }
1776        };
1777
1778        let init_word = match (global_variable.space, self.zero_initialize_workgroup_memory) {
1779            (crate::AddressSpace::Private, _)
1780            | (crate::AddressSpace::WorkGroup, super::ZeroInitializeWorkgroupMemoryMode::Native) => {
1781                init_word.or_else(|| Some(self.get_constant_null(inner_type_id)))
1782            }
1783            _ => init_word,
1784        };
1785
1786        Instruction::variable(pointer_type_id, id, class, init_word)
1787            .to_words(&mut self.logical_layout.declarations);
1788        Ok(id)
1789    }
1790
1791    /// Write the necessary decorations for a struct member.
1792    ///
1793    /// Emit decorations for the `index`'th member of the struct type
1794    /// designated by `struct_id`, described by `member`.
1795    fn decorate_struct_member(
1796        &mut self,
1797        struct_id: Word,
1798        index: usize,
1799        member: &crate::StructMember,
1800        arena: &UniqueArena<crate::Type>,
1801    ) -> Result<(), Error> {
1802        use spirv::Decoration;
1803
1804        self.annotations.push(Instruction::member_decorate(
1805            struct_id,
1806            index as u32,
1807            Decoration::Offset,
1808            &[member.offset],
1809        ));
1810
1811        if self.flags.contains(WriterFlags::DEBUG) {
1812            if let Some(ref name) = member.name {
1813                self.debugs
1814                    .push(Instruction::member_name(struct_id, index as u32, name));
1815            }
1816        }
1817
1818        // Matrices and (potentially nested) arrays of matrices both require decorations,
1819        // so "see through" any arrays to determine if they're needed.
1820        let mut member_array_subty_inner = &arena[member.ty].inner;
1821        while let crate::TypeInner::Array { base, .. } = *member_array_subty_inner {
1822            member_array_subty_inner = &arena[base].inner;
1823        }
1824
1825        if let crate::TypeInner::Matrix {
1826            columns: _,
1827            rows,
1828            scalar,
1829        } = *member_array_subty_inner
1830        {
1831            let byte_stride = Alignment::from(rows) * scalar.width as u32;
1832            self.annotations.push(Instruction::member_decorate(
1833                struct_id,
1834                index as u32,
1835                Decoration::ColMajor,
1836                &[],
1837            ));
1838            self.annotations.push(Instruction::member_decorate(
1839                struct_id,
1840                index as u32,
1841                Decoration::MatrixStride,
1842                &[byte_stride],
1843            ));
1844        }
1845
1846        Ok(())
1847    }
1848
1849    fn get_function_type(&mut self, lookup_function_type: LookupFunctionType) -> Word {
1850        match self
1851            .lookup_function_type
1852            .entry(lookup_function_type.clone())
1853        {
1854            Entry::Occupied(e) => *e.get(),
1855            Entry::Vacant(_) => {
1856                let id = self.id_gen.next();
1857                let instruction = Instruction::type_function(
1858                    id,
1859                    lookup_function_type.return_type_id,
1860                    &lookup_function_type.parameter_type_ids,
1861                );
1862                instruction.to_words(&mut self.logical_layout.declarations);
1863                self.lookup_function_type.insert(lookup_function_type, id);
1864                id
1865            }
1866        }
1867    }
1868
1869    fn write_physical_layout(&mut self) {
1870        self.physical_layout.bound = self.id_gen.0 + 1;
1871    }
1872
1873    fn write_logical_layout(
1874        &mut self,
1875        ir_module: &crate::Module,
1876        mod_info: &ModuleInfo,
1877        ep_index: Option<usize>,
1878        debug_info: &Option<DebugInfo>,
1879    ) -> Result<(), Error> {
1880        fn has_view_index_check(
1881            ir_module: &crate::Module,
1882            binding: Option<&crate::Binding>,
1883            ty: Handle<crate::Type>,
1884        ) -> bool {
1885            match ir_module.types[ty].inner {
1886                crate::TypeInner::Struct { ref members, .. } => members.iter().any(|member| {
1887                    has_view_index_check(ir_module, member.binding.as_ref(), member.ty)
1888                }),
1889                _ => binding == Some(&crate::Binding::BuiltIn(crate::BuiltIn::ViewIndex)),
1890            }
1891        }
1892
1893        let has_storage_buffers =
1894            ir_module
1895                .global_variables
1896                .iter()
1897                .any(|(_, var)| match var.space {
1898                    crate::AddressSpace::Storage { .. } => true,
1899                    _ => false,
1900                });
1901        let has_view_index = ir_module
1902            .entry_points
1903            .iter()
1904            .flat_map(|entry| entry.function.arguments.iter())
1905            .any(|arg| has_view_index_check(ir_module, arg.binding.as_ref(), arg.ty));
1906        let mut has_ray_query = ir_module.special_types.ray_desc.is_some()
1907            | ir_module.special_types.ray_intersection.is_some();
1908
1909        for (_, &crate::Type { ref inner, .. }) in ir_module.types.iter() {
1910            if let &crate::TypeInner::AccelerationStructure | &crate::TypeInner::RayQuery = inner {
1911                has_ray_query = true
1912            }
1913        }
1914
1915        if self.physical_layout.version < 0x10300 && has_storage_buffers {
1916            // enable the storage buffer class on < SPV-1.3
1917            Instruction::extension("SPV_KHR_storage_buffer_storage_class")
1918                .to_words(&mut self.logical_layout.extensions);
1919        }
1920        if has_view_index {
1921            Instruction::extension("SPV_KHR_multiview")
1922                .to_words(&mut self.logical_layout.extensions)
1923        }
1924        if has_ray_query {
1925            Instruction::extension("SPV_KHR_ray_query")
1926                .to_words(&mut self.logical_layout.extensions)
1927        }
1928        Instruction::type_void(self.void_type).to_words(&mut self.logical_layout.declarations);
1929        Instruction::ext_inst_import(self.gl450_ext_inst_id, "GLSL.std.450")
1930            .to_words(&mut self.logical_layout.ext_inst_imports);
1931
1932        let mut debug_info_inner = None;
1933        if self.flags.contains(WriterFlags::DEBUG) {
1934            if let Some(debug_info) = debug_info.as_ref() {
1935                let source_file_id = self.id_gen.next();
1936                self.debugs.push(Instruction::string(
1937                    &debug_info.file_name.display().to_string(),
1938                    source_file_id,
1939                ));
1940
1941                debug_info_inner = Some(DebugInfoInner {
1942                    source_code: debug_info.source_code,
1943                    source_file_id,
1944                });
1945                self.debugs.append(&mut Instruction::source_auto_continued(
1946                    debug_info.language,
1947                    0,
1948                    &debug_info_inner,
1949                ));
1950            }
1951        }
1952
1953        // write all types
1954        for (handle, _) in ir_module.types.iter() {
1955            self.write_type_declaration_arena(&ir_module.types, handle)?;
1956        }
1957
1958        // write all const-expressions as constants
1959        self.constant_ids
1960            .resize(ir_module.global_expressions.len(), 0);
1961        for (handle, _) in ir_module.global_expressions.iter() {
1962            self.write_constant_expr(handle, ir_module, mod_info)?;
1963        }
1964        debug_assert!(self.constant_ids.iter().all(|&id| id != 0));
1965
1966        // write the name of constants on their respective const-expression initializer
1967        if self.flags.contains(WriterFlags::DEBUG) {
1968            for (_, constant) in ir_module.constants.iter() {
1969                if let Some(ref name) = constant.name {
1970                    let id = self.constant_ids[constant.init];
1971                    self.debugs.push(Instruction::name(id, name));
1972                }
1973            }
1974        }
1975
1976        // write all global variables
1977        for (handle, var) in ir_module.global_variables.iter() {
1978            // If a single entry point was specified, only write `OpVariable` instructions
1979            // for the globals it actually uses. Emit dummies for the others,
1980            // to preserve the indices in `global_variables`.
1981            let gvar = match ep_index {
1982                Some(index) if mod_info.get_entry_point(index)[handle].is_empty() => {
1983                    GlobalVariable::dummy()
1984                }
1985                _ => {
1986                    let id = self.write_global_variable(ir_module, var)?;
1987                    GlobalVariable::new(id)
1988                }
1989            };
1990            self.global_variables.insert(handle, gvar);
1991        }
1992
1993        // write all functions
1994        for (handle, ir_function) in ir_module.functions.iter() {
1995            let info = &mod_info[handle];
1996            if let Some(index) = ep_index {
1997                let ep_info = mod_info.get_entry_point(index);
1998                // If this function uses globals that we omitted from the SPIR-V
1999                // because the entry point and its callees didn't use them,
2000                // then we must skip it.
2001                if !ep_info.dominates_global_use(info) {
2002                    log::info!("Skip function {:?}", ir_function.name);
2003                    continue;
2004                }
2005
2006                // Skip functions that that are not compatible with this entry point's stage.
2007                //
2008                // When validation is enabled, it rejects modules whose entry points try to call
2009                // incompatible functions, so if we got this far, then any functions incompatible
2010                // with our selected entry point must not be used.
2011                //
2012                // When validation is disabled, `fun_info.available_stages` is always just
2013                // `ShaderStages::all()`, so this will write all functions in the module, and
2014                // the downstream GLSL compiler will catch any problems.
2015                if !info.available_stages.contains(ep_info.available_stages) {
2016                    continue;
2017                }
2018            }
2019            let id = self.write_function(ir_function, info, ir_module, None, &debug_info_inner)?;
2020            self.lookup_function.insert(handle, id);
2021        }
2022
2023        // write all or one entry points
2024        for (index, ir_ep) in ir_module.entry_points.iter().enumerate() {
2025            if ep_index.is_some() && ep_index != Some(index) {
2026                continue;
2027            }
2028            let info = mod_info.get_entry_point(index);
2029            let ep_instruction =
2030                self.write_entry_point(ir_ep, info, ir_module, &debug_info_inner)?;
2031            ep_instruction.to_words(&mut self.logical_layout.entry_points);
2032        }
2033
2034        for capability in self.capabilities_used.iter() {
2035            Instruction::capability(*capability).to_words(&mut self.logical_layout.capabilities);
2036        }
2037        for extension in self.extensions_used.iter() {
2038            Instruction::extension(extension).to_words(&mut self.logical_layout.extensions);
2039        }
2040        if ir_module.entry_points.is_empty() {
2041            // SPIR-V doesn't like modules without entry points
2042            Instruction::capability(spirv::Capability::Linkage)
2043                .to_words(&mut self.logical_layout.capabilities);
2044        }
2045
2046        let addressing_model = spirv::AddressingModel::Logical;
2047        let memory_model = spirv::MemoryModel::GLSL450;
2048        //self.check(addressing_model.required_capabilities())?;
2049        //self.check(memory_model.required_capabilities())?;
2050
2051        Instruction::memory_model(addressing_model, memory_model)
2052            .to_words(&mut self.logical_layout.memory_model);
2053
2054        if self.flags.contains(WriterFlags::DEBUG) {
2055            for debug in self.debugs.iter() {
2056                debug.to_words(&mut self.logical_layout.debugs);
2057            }
2058        }
2059
2060        for annotation in self.annotations.iter() {
2061            annotation.to_words(&mut self.logical_layout.annotations);
2062        }
2063
2064        Ok(())
2065    }
2066
2067    pub fn write(
2068        &mut self,
2069        ir_module: &crate::Module,
2070        info: &ModuleInfo,
2071        pipeline_options: Option<&PipelineOptions>,
2072        debug_info: &Option<DebugInfo>,
2073        words: &mut Vec<Word>,
2074    ) -> Result<(), Error> {
2075        if !ir_module.overrides.is_empty() {
2076            return Err(Error::Override);
2077        }
2078
2079        self.reset();
2080
2081        // Try to find the entry point and corresponding index
2082        let ep_index = match pipeline_options {
2083            Some(po) => {
2084                let index = ir_module
2085                    .entry_points
2086                    .iter()
2087                    .position(|ep| po.shader_stage == ep.stage && po.entry_point == ep.name)
2088                    .ok_or(Error::EntryPointNotFound)?;
2089                Some(index)
2090            }
2091            None => None,
2092        };
2093
2094        self.write_logical_layout(ir_module, info, ep_index, debug_info)?;
2095        self.write_physical_layout();
2096
2097        self.physical_layout.in_words(words);
2098        self.logical_layout.in_words(words);
2099        Ok(())
2100    }
2101
2102    /// Return the set of capabilities the last module written used.
2103    pub const fn get_capabilities_used(&self) -> &crate::FastIndexSet<spirv::Capability> {
2104        &self.capabilities_used
2105    }
2106
2107    pub fn decorate_non_uniform_binding_array_access(&mut self, id: Word) -> Result<(), Error> {
2108        self.require_any("NonUniformEXT", &[spirv::Capability::ShaderNonUniform])?;
2109        self.use_extension("SPV_EXT_descriptor_indexing");
2110        self.decorate(id, spirv::Decoration::NonUniform, &[]);
2111        Ok(())
2112    }
2113}
2114
2115#[test]
2116fn test_write_physical_layout() {
2117    let mut writer = Writer::new(&Options::default()).unwrap();
2118    assert_eq!(writer.physical_layout.bound, 0);
2119    writer.write_physical_layout();
2120    assert_eq!(writer.physical_layout.bound, 3);
2121}