1mod 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
91struct Block {
103 label_id: Word,
104 body: Vec<Instruction>,
105}
106
107struct 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#[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 #[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 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#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)]
273enum LocalType {
274 Value {
276 vector_size: Option<crate::VectorSize>,
279 kind: crate::ScalarKind,
280 width: crate::Bytes,
281 pointer_space: Option<spirv::StorageClass>,
282 },
283 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 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#[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#[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 var_id: Word,
486
487 handle_id: Word,
491
492 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 fn reset_for_function(&mut self) {
525 self.handle_id = 0;
526 self.access_id = 0;
527 }
528}
529
530struct FunctionArgument {
531 instruction: Instruction,
533 handle_id: Word,
534}
535
536struct BlockContext<'w> {
538 writer: &'w mut Writer,
540
541 ir_module: &'w crate::Module,
543
544 ir_function: &'w crate::Function,
546
547 fun_info: &'w crate::valid::FunctionInfo,
550
551 function: &'w mut Function,
553
554 cached: CachedExpressions,
556
557 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 capabilities_available: Option<crate::FastHashSet<Capability>>,
599
600 capabilities_used: crate::FastIndexSet<Capability>,
605
606 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 lookup_type: crate::FastHashMap<LookupType, Word>,
617 lookup_function: crate::FastHashMap<Handle<crate::Function>, Word>,
618 lookup_function_type: crate::FastHashMap<LookupFunctionType, Word>,
619 constant_ids: Vec<Word>,
621 cached_constants: crate::FastHashMap<CachedConstant, Word>,
622 global_variables: Vec<GlobalVariable>,
623 binding_map: BindingMap,
624
625 saved_cached: CachedExpressions,
628
629 gl450_ext_inst_id: Word,
630
631 temp_list: Vec<Word>,
633}
634
635bitflags::bitflags! {
636 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
637 pub struct WriterFlags: u32 {
638 const DEBUG = 0x1;
640 const ADJUST_COORDINATE_SPACE = 0x2;
642 const LABEL_VARYINGS = 0x4;
646 const FORCE_POINT_SIZE = 0x8;
649 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 pub binding_array_size: Option<u32>,
660}
661
662pub type BindingMap = std::collections::BTreeMap<crate::ResourceBinding, BindingInfo>;
664
665#[derive(Clone, Copy, Debug, PartialEq, Eq)]
666pub enum ZeroInitializeWorkgroupMemoryMode {
667 Native,
669 Polyfill,
671 None,
672}
673
674#[derive(Debug, Clone)]
675pub struct Options<'a> {
676 pub lang_version: (u8, u8),
678
679 pub flags: WriterFlags,
681
682 pub binding_map: BindingMap,
684
685 pub capabilities: Option<crate::FastHashSet<Capability>>,
690
691 pub bounds_check_policies: BoundsCheckPolicies,
694
695 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#[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 pub shader_stage: crate::ShaderStage,
728 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}