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