naga/back/spv/
mod.rs

1/*!
2Backend for [SPIR-V][spv] (Standard Portable Intermediate Representation).
3
4[spv]: https://www.khronos.org/registry/SPIR-V/
5*/
6
7mod block;
8mod helpers;
9mod image;
10mod index;
11mod instructions;
12mod layout;
13mod ray;
14mod recyclable;
15mod selection;
16mod writer;
17
18pub use spirv::Capability;
19
20use crate::arena::Handle;
21use crate::proc::{BoundsCheckPolicies, TypeResolution};
22
23use spirv::Word;
24use std::ops;
25use thiserror::Error;
26
27#[derive(Clone)]
28struct PhysicalLayout {
29    magic_number: Word,
30    version: Word,
31    generator: Word,
32    bound: Word,
33    instruction_schema: Word,
34}
35
36#[derive(Default)]
37struct LogicalLayout {
38    capabilities: Vec<Word>,
39    extensions: Vec<Word>,
40    ext_inst_imports: Vec<Word>,
41    memory_model: Vec<Word>,
42    entry_points: Vec<Word>,
43    execution_modes: Vec<Word>,
44    debugs: Vec<Word>,
45    annotations: Vec<Word>,
46    declarations: Vec<Word>,
47    function_declarations: Vec<Word>,
48    function_definitions: Vec<Word>,
49}
50
51struct Instruction {
52    op: spirv::Op,
53    wc: u32,
54    type_id: Option<Word>,
55    result_id: Option<Word>,
56    operands: Vec<Word>,
57}
58
59const BITS_PER_BYTE: crate::Bytes = 8;
60
61#[derive(Clone, Debug, Error)]
62pub enum Error {
63    #[error("The requested entry point couldn't be found")]
64    EntryPointNotFound,
65    #[error("target SPIRV-{0}.{1} is not supported")]
66    UnsupportedVersion(u8, u8),
67    #[error("using {0} requires at least one of the capabilities {1:?}, but none are available")]
68    MissingCapabilities(&'static str, Vec<Capability>),
69    #[error("unimplemented {0}")]
70    FeatureNotImplemented(&'static str),
71    #[error("module is not validated properly: {0}")]
72    Validation(&'static str),
73}
74
75#[derive(Default)]
76struct IdGenerator(Word);
77
78impl IdGenerator {
79    fn next(&mut self) -> Word {
80        self.0 += 1;
81        self.0
82    }
83}
84
85#[derive(Debug, Clone)]
86pub struct DebugInfo<'a> {
87    pub source_code: &'a str,
88    pub file_name: &'a str,
89}
90
91/// A SPIR-V block to which we are still adding instructions.
92///
93/// A `Block` represents a SPIR-V block that does not yet have a termination
94/// instruction like `OpBranch` or `OpReturn`.
95///
96/// The `OpLabel` that starts the block is implicit. It will be emitted based on
97/// `label_id` when we write the block to a `LogicalLayout`.
98///
99/// To terminate a `Block`, pass the block and the termination instruction to
100/// `Function::consume`. This takes ownership of the `Block` and transforms it
101/// into a `TerminatedBlock`.
102struct Block {
103    label_id: Word,
104    body: Vec<Instruction>,
105}
106
107/// A SPIR-V block that ends with a termination instruction.
108struct TerminatedBlock {
109    label_id: Word,
110    body: Vec<Instruction>,
111}
112
113impl Block {
114    const fn new(label_id: Word) -> Self {
115        Block {
116            label_id,
117            body: Vec::new(),
118        }
119    }
120}
121
122struct LocalVariable {
123    id: Word,
124    instruction: Instruction,
125}
126
127struct ResultMember {
128    id: Word,
129    type_id: Word,
130    built_in: Option<crate::BuiltIn>,
131}
132
133struct EntryPointContext {
134    argument_ids: Vec<Word>,
135    results: Vec<ResultMember>,
136}
137
138#[derive(Default)]
139struct Function {
140    signature: Option<Instruction>,
141    parameters: Vec<FunctionArgument>,
142    variables: crate::FastHashMap<Handle<crate::LocalVariable>, LocalVariable>,
143    blocks: Vec<TerminatedBlock>,
144    entry_point_context: Option<EntryPointContext>,
145}
146
147impl Function {
148    fn consume(&mut self, mut block: Block, termination: Instruction) {
149        block.body.push(termination);
150        self.blocks.push(TerminatedBlock {
151            label_id: block.label_id,
152            body: block.body,
153        })
154    }
155
156    fn parameter_id(&self, index: u32) -> Word {
157        match self.entry_point_context {
158            Some(ref context) => context.argument_ids[index as usize],
159            None => self.parameters[index as usize]
160                .instruction
161                .result_id
162                .unwrap(),
163        }
164    }
165}
166
167/// Characteristics of a SPIR-V `OpTypeImage` type.
168///
169/// SPIR-V requires non-composite types to be unique, including images. Since we
170/// use `LocalType` for this deduplication, it's essential that `LocalImageType`
171/// be equal whenever the corresponding `OpTypeImage`s would be. To reduce the
172/// likelihood of mistakes, we use fields that correspond exactly to the
173/// operands of an `OpTypeImage` instruction, using the actual SPIR-V types
174/// where practical.
175#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)]
176struct LocalImageType {
177    sampled_type: crate::ScalarKind,
178    dim: spirv::Dim,
179    flags: ImageTypeFlags,
180    image_format: spirv::ImageFormat,
181}
182
183bitflags::bitflags! {
184    /// Flags corresponding to the boolean(-ish) parameters to OpTypeImage.
185    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
186    pub struct ImageTypeFlags: u8 {
187        const DEPTH = 0x1;
188        const ARRAYED = 0x2;
189        const MULTISAMPLED = 0x4;
190        const SAMPLED = 0x8;
191    }
192}
193
194impl LocalImageType {
195    /// Construct a `LocalImageType` from the fields of a `TypeInner::Image`.
196    fn from_inner(dim: crate::ImageDimension, arrayed: bool, class: crate::ImageClass) -> Self {
197        let make_flags = |multi: bool, other: ImageTypeFlags| -> ImageTypeFlags {
198            let mut flags = other;
199            flags.set(ImageTypeFlags::ARRAYED, arrayed);
200            flags.set(ImageTypeFlags::MULTISAMPLED, multi);
201            flags
202        };
203
204        let dim = spirv::Dim::from(dim);
205
206        match class {
207            crate::ImageClass::Sampled { kind, multi } => LocalImageType {
208                sampled_type: kind,
209                dim,
210                flags: make_flags(multi, ImageTypeFlags::SAMPLED),
211                image_format: spirv::ImageFormat::Unknown,
212            },
213            crate::ImageClass::Depth { multi } => LocalImageType {
214                sampled_type: crate::ScalarKind::Float,
215                dim,
216                flags: make_flags(multi, ImageTypeFlags::DEPTH | ImageTypeFlags::SAMPLED),
217                image_format: spirv::ImageFormat::Unknown,
218            },
219            crate::ImageClass::Storage { format, access: _ } => LocalImageType {
220                sampled_type: crate::ScalarKind::from(format),
221                dim,
222                flags: make_flags(false, ImageTypeFlags::empty()),
223                image_format: format.into(),
224            },
225        }
226    }
227}
228
229/// A SPIR-V type constructed during code generation.
230///
231/// This is the variant of [`LookupType`] used to represent types that might not
232/// be available in the arena. Variants are present here for one of two reasons:
233///
234/// -   They represent types synthesized during code generation, as explained
235///     in the documentation for [`LookupType`].
236///
237/// -   They represent types for which SPIR-V forbids duplicate `OpType...`
238///     instructions, requiring deduplication.
239///
240/// This is not a complete copy of [`TypeInner`]: for example, SPIR-V generation
241/// never synthesizes new struct types, so `LocalType` has nothing for that.
242///
243/// Each `LocalType` variant should be handled identically to its analogous
244/// `TypeInner` variant. You can use the [`make_local`] function to help with
245/// this, by converting everything possible to a `LocalType` before inspecting
246/// it.
247///
248/// ## `Localtype` equality and SPIR-V `OpType` uniqueness
249///
250/// The definition of `Eq` on `LocalType` is carefully chosen to help us follow
251/// certain SPIR-V rules. SPIR-V ยง2.8 requires some classes of `OpType...`
252/// instructions to be unique; for example, you can't have two `OpTypeInt 32 1`
253/// instructions in the same module. All 32-bit signed integers must use the
254/// same type id.
255///
256/// All SPIR-V types that must be unique can be represented as a `LocalType`,
257/// and two `LocalType`s are always `Eq` if SPIR-V would require them to use the
258/// same `OpType...` instruction. This lets us avoid duplicates by recording the
259/// ids of the type instructions we've already generated in a hash table,
260/// [`Writer::lookup_type`], keyed by `LocalType`.
261///
262/// As another example, [`LocalImageType`], stored in the `LocalType::Image`
263/// variant, is designed to help us deduplicate `OpTypeImage` instructions. See
264/// its documentation for details.
265///
266/// `LocalType` also includes variants like `Pointer` that do not need to be
267/// unique - but it is harmless to avoid the duplication.
268///
269/// As it always must, the `Hash` implementation respects the `Eq` relation.
270///
271/// [`TypeInner`]: crate::TypeInner
272#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)]
273enum LocalType {
274    /// A scalar, vector, or pointer to one of those.
275    Value {
276        /// If `None`, this represents a scalar type. If `Some`, this represents
277        /// a vector type of the given size.
278        vector_size: Option<crate::VectorSize>,
279        kind: crate::ScalarKind,
280        width: crate::Bytes,
281        pointer_space: Option<spirv::StorageClass>,
282    },
283    /// A matrix of floating-point values.
284    Matrix {
285        columns: crate::VectorSize,
286        rows: crate::VectorSize,
287        width: crate::Bytes,
288    },
289    Pointer {
290        base: Handle<crate::Type>,
291        class: spirv::StorageClass,
292    },
293    Image(LocalImageType),
294    SampledImage {
295        image_type_id: Word,
296    },
297    Sampler,
298    /// Equivalent to a [`LocalType::Pointer`] whose `base` is a Naga IR [`BindingArray`]. SPIR-V
299    /// permits duplicated `OpTypePointer` ids, so it's fine to have two different [`LocalType`]
300    /// representations for pointer types.
301    ///
302    /// [`BindingArray`]: crate::TypeInner::BindingArray
303    PointerToBindingArray {
304        base: Handle<crate::Type>,
305        size: u32,
306        space: crate::AddressSpace,
307    },
308    BindingArray {
309        base: Handle<crate::Type>,
310        size: u32,
311    },
312    AccelerationStructure,
313    RayQuery,
314}
315
316/// A type encountered during SPIR-V generation.
317///
318/// In the process of writing SPIR-V, we need to synthesize various types for
319/// intermediate results and such: pointer types, vector/matrix component types,
320/// or even booleans, which usually appear in SPIR-V code even when they're not
321/// used by the module source.
322///
323/// However, we can't use `crate::Type` or `crate::TypeInner` for these, as the
324/// type arena may not contain what we need (it only contains types used
325/// directly by other parts of the IR), and the IR module is immutable, so we
326/// can't add anything to it.
327///
328/// So for local use in the SPIR-V writer, we use this type, which holds either
329/// a handle into the arena, or a [`LocalType`] containing something synthesized
330/// locally.
331///
332/// This is very similar to the [`proc::TypeResolution`] enum, with `LocalType`
333/// playing the role of `TypeInner`. However, `LocalType` also has other
334/// properties needed for SPIR-V generation; see the description of
335/// [`LocalType`] for details.
336///
337/// [`proc::TypeResolution`]: crate::proc::TypeResolution
338#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)]
339enum LookupType {
340    Handle(Handle<crate::Type>),
341    Local(LocalType),
342}
343
344impl From<LocalType> for LookupType {
345    fn from(local: LocalType) -> Self {
346        Self::Local(local)
347    }
348}
349
350#[derive(Debug, PartialEq, Clone, Hash, Eq)]
351struct LookupFunctionType {
352    parameter_type_ids: Vec<Word>,
353    return_type_id: Word,
354}
355
356fn make_local(inner: &crate::TypeInner) -> Option<LocalType> {
357    Some(match *inner {
358        crate::TypeInner::Scalar { kind, width } | crate::TypeInner::Atomic { kind, width } => {
359            LocalType::Value {
360                vector_size: None,
361                kind,
362                width,
363                pointer_space: None,
364            }
365        }
366        crate::TypeInner::Vector { size, kind, width } => LocalType::Value {
367            vector_size: Some(size),
368            kind,
369            width,
370            pointer_space: None,
371        },
372        crate::TypeInner::Matrix {
373            columns,
374            rows,
375            width,
376        } => LocalType::Matrix {
377            columns,
378            rows,
379            width,
380        },
381        crate::TypeInner::Pointer { base, space } => LocalType::Pointer {
382            base,
383            class: helpers::map_storage_class(space),
384        },
385        crate::TypeInner::ValuePointer {
386            size,
387            kind,
388            width,
389            space,
390        } => LocalType::Value {
391            vector_size: size,
392            kind,
393            width,
394            pointer_space: Some(helpers::map_storage_class(space)),
395        },
396        crate::TypeInner::Image {
397            dim,
398            arrayed,
399            class,
400        } => LocalType::Image(LocalImageType::from_inner(dim, arrayed, class)),
401        crate::TypeInner::Sampler { comparison: _ } => LocalType::Sampler,
402        crate::TypeInner::AccelerationStructure => LocalType::AccelerationStructure,
403        crate::TypeInner::RayQuery => LocalType::RayQuery,
404        crate::TypeInner::Array { .. }
405        | crate::TypeInner::Struct { .. }
406        | crate::TypeInner::BindingArray { .. } => return None,
407    })
408}
409
410#[derive(Debug)]
411enum Dimension {
412    Scalar,
413    Vector,
414    Matrix,
415}
416
417/// A map from evaluated [`Expression`](crate::Expression)s to their SPIR-V ids.
418///
419/// When we emit code to evaluate a given `Expression`, we record the
420/// SPIR-V id of its value here, under its `Handle<Expression>` index.
421///
422/// A `CachedExpressions` value can be indexed by a `Handle<Expression>` value.
423///
424/// [emit]: index.html#expression-evaluation-time-and-scope
425#[derive(Default)]
426struct CachedExpressions {
427    ids: Vec<Word>,
428}
429impl CachedExpressions {
430    fn reset(&mut self, length: usize) {
431        self.ids.clear();
432        self.ids.resize(length, 0);
433    }
434}
435impl ops::Index<Handle<crate::Expression>> for CachedExpressions {
436    type Output = Word;
437    fn index(&self, h: Handle<crate::Expression>) -> &Word {
438        let id = &self.ids[h.index()];
439        if *id == 0 {
440            unreachable!("Expression {:?} is not cached!", h);
441        }
442        id
443    }
444}
445impl ops::IndexMut<Handle<crate::Expression>> for CachedExpressions {
446    fn index_mut(&mut self, h: Handle<crate::Expression>) -> &mut Word {
447        let id = &mut self.ids[h.index()];
448        if *id != 0 {
449            unreachable!("Expression {:?} is already cached!", h);
450        }
451        id
452    }
453}
454impl recyclable::Recyclable for CachedExpressions {
455    fn recycle(self) -> Self {
456        CachedExpressions {
457            ids: self.ids.recycle(),
458        }
459    }
460}
461
462#[derive(Eq, Hash, PartialEq)]
463enum CachedConstant {
464    Literal(crate::Literal),
465    Composite {
466        ty: LookupType,
467        constituent_ids: Vec<Word>,
468    },
469}
470
471#[derive(Clone)]
472struct GlobalVariable {
473    /// ID of the OpVariable that declares the global.
474    ///
475    /// If you need the variable's value, use [`access_id`] instead of this
476    /// field. If we wrapped the Naga IR `GlobalVariable`'s type in a struct to
477    /// comply with Vulkan's requirements, then this points to the `OpVariable`
478    /// with the synthesized struct type, whereas `access_id` points to the
479    /// field of said struct that holds the variable's actual value.
480    ///
481    /// This is used to compute the `access_id` pointer in function prologues,
482    /// and used for `ArrayLength` expressions, which do need the struct.
483    ///
484    /// [`access_id`]: GlobalVariable::access_id
485    var_id: Word,
486
487    /// For `AddressSpace::Handle` variables, this ID is recorded in the function
488    /// prelude block (and reset before every function) as `OpLoad` of the variable.
489    /// It is then used for all the global ops, such as `OpImageSample`.
490    handle_id: Word,
491
492    /// Actual ID used to access this variable.
493    /// For wrapped buffer variables, this ID is `OpAccessChain` into the
494    /// wrapper. Otherwise, the same as `var_id`.
495    ///
496    /// Vulkan requires that globals in the `StorageBuffer` and `Uniform` storage
497    /// classes must be structs with the `Block` decoration, but WGSL and Naga IR
498    /// make no such requirement. So for such variables, we generate a wrapper struct
499    /// type with a single element of the type given by Naga, generate an
500    /// `OpAccessChain` for that member in the function prelude, and use that pointer
501    /// to refer to the global in the function body. This is the id of that access,
502    /// updated for each function in `write_function`.
503    access_id: Word,
504}
505
506impl GlobalVariable {
507    const fn dummy() -> Self {
508        Self {
509            var_id: 0,
510            handle_id: 0,
511            access_id: 0,
512        }
513    }
514
515    const fn new(id: Word) -> Self {
516        Self {
517            var_id: id,
518            handle_id: 0,
519            access_id: 0,
520        }
521    }
522
523    /// Prepare `self` for use within a single function.
524    fn reset_for_function(&mut self) {
525        self.handle_id = 0;
526        self.access_id = 0;
527    }
528}
529
530struct FunctionArgument {
531    /// Actual instruction of the argument.
532    instruction: Instruction,
533    handle_id: Word,
534}
535
536/// General information needed to emit SPIR-V for Naga statements.
537struct BlockContext<'w> {
538    /// The writer handling the module to which this code belongs.
539    writer: &'w mut Writer,
540
541    /// The [`Module`](crate::Module) for which we're generating code.
542    ir_module: &'w crate::Module,
543
544    /// The [`Function`](crate::Function) for which we're generating code.
545    ir_function: &'w crate::Function,
546
547    /// Information module validation produced about
548    /// [`ir_function`](BlockContext::ir_function).
549    fun_info: &'w crate::valid::FunctionInfo,
550
551    /// The [`spv::Function`](Function) to which we are contributing SPIR-V instructions.
552    function: &'w mut Function,
553
554    /// SPIR-V ids for expressions we've evaluated.
555    cached: CachedExpressions,
556
557    /// The `Writer`'s temporary vector, for convenience.
558    temp_list: Vec<Word>,
559}
560
561impl BlockContext<'_> {
562    fn gen_id(&mut self) -> Word {
563        self.writer.id_gen.next()
564    }
565
566    fn get_type_id(&mut self, lookup_type: LookupType) -> Word {
567        self.writer.get_type_id(lookup_type)
568    }
569
570    fn get_expression_type_id(&mut self, tr: &TypeResolution) -> Word {
571        self.writer.get_expression_type_id(tr)
572    }
573
574    fn get_index_constant(&mut self, index: Word) -> Word {
575        self.writer.get_constant_scalar(crate::Literal::U32(index))
576    }
577
578    fn get_scope_constant(&mut self, scope: Word) -> Word {
579        self.writer
580            .get_constant_scalar(crate::Literal::I32(scope as _))
581    }
582}
583
584#[derive(Clone, Copy, Default)]
585struct LoopContext {
586    continuing_id: Option<Word>,
587    break_id: Option<Word>,
588}
589
590pub struct Writer {
591    physical_layout: PhysicalLayout,
592    logical_layout: LogicalLayout,
593    id_gen: IdGenerator,
594
595    /// The set of capabilities modules are permitted to use.
596    ///
597    /// This is initialized from `Options::capabilities`.
598    capabilities_available: Option<crate::FastHashSet<Capability>>,
599
600    /// The set of capabilities used by this module.
601    ///
602    /// If `capabilities_available` is `Some`, then this is always a subset of
603    /// that.
604    capabilities_used: crate::FastIndexSet<Capability>,
605
606    /// The set of spirv extensions used.
607    extensions_used: crate::FastIndexSet<&'static str>,
608
609    debugs: Vec<Instruction>,
610    annotations: Vec<Instruction>,
611    flags: WriterFlags,
612    bounds_check_policies: BoundsCheckPolicies,
613    zero_initialize_workgroup_memory: ZeroInitializeWorkgroupMemoryMode,
614    void_type: Word,
615    //TODO: convert most of these into vectors, addressable by handle indices
616    lookup_type: crate::FastHashMap<LookupType, Word>,
617    lookup_function: crate::FastHashMap<Handle<crate::Function>, Word>,
618    lookup_function_type: crate::FastHashMap<LookupFunctionType, Word>,
619    /// Indexed by const-expression handle indexes
620    constant_ids: Vec<Word>,
621    cached_constants: crate::FastHashMap<CachedConstant, Word>,
622    global_variables: Vec<GlobalVariable>,
623    binding_map: BindingMap,
624
625    // Cached expressions are only meaningful within a BlockContext, but we
626    // retain the table here between functions to save heap allocations.
627    saved_cached: CachedExpressions,
628
629    gl450_ext_inst_id: Word,
630
631    // Just a temporary list of SPIR-V ids
632    temp_list: Vec<Word>,
633}
634
635bitflags::bitflags! {
636    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
637    pub struct WriterFlags: u32 {
638        /// Include debug labels for everything.
639        const DEBUG = 0x1;
640        /// Flip Y coordinate of `BuiltIn::Position` output.
641        const ADJUST_COORDINATE_SPACE = 0x2;
642        /// Emit `OpName` for input/output locations.
643        /// Contrary to spec, some drivers treat it as semantic, not allowing
644        /// any conflicts.
645        const LABEL_VARYINGS = 0x4;
646        /// Emit `PointSize` output builtin to vertex shaders, which is
647        /// required for drawing with `PointList` topology.
648        const FORCE_POINT_SIZE = 0x8;
649        /// Clamp `BuiltIn::FragDepth` output between 0 and 1.
650        const CLAMP_FRAG_DEPTH = 0x10;
651    }
652}
653
654#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
655#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
656#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
657pub struct BindingInfo {
658    /// If the binding is an unsized binding array, this overrides the size.
659    pub binding_array_size: Option<u32>,
660}
661
662// Using `BTreeMap` instead of `HashMap` so that we can hash itself.
663pub type BindingMap = std::collections::BTreeMap<crate::ResourceBinding, BindingInfo>;
664
665#[derive(Clone, Copy, Debug, PartialEq, Eq)]
666pub enum ZeroInitializeWorkgroupMemoryMode {
667    /// Via `VK_KHR_zero_initialize_workgroup_memory` or Vulkan 1.3
668    Native,
669    /// Via assignments + barrier
670    Polyfill,
671    None,
672}
673
674#[derive(Debug, Clone)]
675pub struct Options<'a> {
676    /// (Major, Minor) target version of the SPIR-V.
677    pub lang_version: (u8, u8),
678
679    /// Configuration flags for the writer.
680    pub flags: WriterFlags,
681
682    /// Map of resources to information about the binding.
683    pub binding_map: BindingMap,
684
685    /// If given, the set of capabilities modules are allowed to use. Code that
686    /// requires capabilities beyond these is rejected with an error.
687    ///
688    /// If this is `None`, all capabilities are permitted.
689    pub capabilities: Option<crate::FastHashSet<Capability>>,
690
691    /// How should generate code handle array, vector, matrix, or image texel
692    /// indices that are out of range?
693    pub bounds_check_policies: BoundsCheckPolicies,
694
695    /// Dictates the way workgroup variables should be zero initialized
696    pub zero_initialize_workgroup_memory: ZeroInitializeWorkgroupMemoryMode,
697
698    pub debug_info: Option<DebugInfo<'a>>,
699}
700
701impl<'a> Default for Options<'a> {
702    fn default() -> Self {
703        let mut flags = WriterFlags::ADJUST_COORDINATE_SPACE
704            | WriterFlags::LABEL_VARYINGS
705            | WriterFlags::CLAMP_FRAG_DEPTH;
706        if cfg!(debug_assertions) {
707            flags |= WriterFlags::DEBUG;
708        }
709        Options {
710            lang_version: (1, 0),
711            flags,
712            binding_map: BindingMap::default(),
713            capabilities: None,
714            bounds_check_policies: crate::proc::BoundsCheckPolicies::default(),
715            zero_initialize_workgroup_memory: ZeroInitializeWorkgroupMemoryMode::Polyfill,
716            debug_info: None,
717        }
718    }
719}
720
721// A subset of options meant to be changed per pipeline.
722#[derive(Debug, Clone, PartialEq, Eq, Hash)]
723#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
724#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
725pub struct PipelineOptions {
726    /// The stage of the entry point.
727    pub shader_stage: crate::ShaderStage,
728    /// The name of the entry point.
729    ///
730    /// If no entry point that matches is found while creating a [`Writer`], a error will be thrown.
731    pub entry_point: String,
732}
733
734pub fn write_vec(
735    module: &crate::Module,
736    info: &crate::valid::ModuleInfo,
737    options: &Options,
738    pipeline_options: Option<&PipelineOptions>,
739) -> Result<Vec<u32>, Error> {
740    let mut words: Vec<u32> = Vec::new();
741    let mut w = Writer::new(options)?;
742
743    w.write(
744        module,
745        info,
746        pipeline_options,
747        &options.debug_info,
748        &mut words,
749    )?;
750    Ok(words)
751}