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 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 let fresh = Writer {
104 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 id_gen,
113 void_type,
114 gl450_ext_inst_id,
115
116 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 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 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 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 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 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 if self.physical_layout.version >= 0x10400 {
595 iface.varying_ids.push(gv.var_id);
596 }
597 }
598
599 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 gv.access_id = gv.var_id;
631 };
632
633 self.global_variables[handle.index()] = gv;
635 }
636
637 let mut context = BlockContext {
640 ir_module,
641 ir_function,
642 fun_info: info,
643 function: &mut function,
644 cached: std::mem::take(&mut self.saved_cached),
646
647 temp_list: std::mem::take(&mut self.temp_list),
649 writer: self,
650 };
651
652 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 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 Instruction::execution_mode(function_id, mode, &[])
722 .to_words(&mut self.logical_layout.execution_modes);
723 Ok(())
724 }
725
726 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 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 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 match self.lookup_type.entry(LookupType::Local(local)) {
971 Entry::Occupied(e) => *e.get(),
973
974 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 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 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 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 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 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 (class == spirv::StorageClass::Input && stage == crate::ShaderStage::Vertex) ||
1435 (class == spirv::StorageClass::Output && stage == crate::ShaderStage::Fragment);
1439
1440 if !no_decorations {
1441 match interpolation {
1442 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 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 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 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 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 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 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 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 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 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 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 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 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 for (handle, _) in ir_module.types.iter() {
1831 self.write_type_declaration_arena(&ir_module.types, handle)?;
1832 }
1833
1834 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 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 for (handle, var) in ir_module.global_variables.iter() {
1854 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 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 !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 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 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 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 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 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}