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