naga/back/spv/
writer.rs

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