1use alloc::{string::String, vec, vec::Vec};
2
3use hashbrown::hash_map::Entry;
4use spirv::Word;
5
6use super::{
7 block::DebugInfoInner,
8 helpers::{contains_builtin, global_needs_wrapper, map_storage_class},
9 Block, BlockContext, CachedConstant, CachedExpressions, DebugInfo, EntryPointContext, Error,
10 Function, FunctionArgument, GlobalVariable, IdGenerator, Instruction, LocalImageType,
11 LocalType, LocalVariable, LogicalLayout, LookupFunctionType, LookupType, NumericType, Options,
12 PhysicalLayout, PipelineOptions, ResultMember, Writer, WriterFlags, BITS_PER_BYTE,
13};
14use crate::{
15 arena::{Handle, HandleVec, UniqueArena},
16 back::spv::{BindingInfo, WrappedFunction},
17 proc::{Alignment, TypeResolution},
18 valid::{FunctionInfo, ModuleInfo},
19};
20
21struct FunctionInterface<'a> {
22 varying_ids: &'a mut Vec<Word>,
23 stage: crate::ShaderStage,
24}
25
26impl Function {
27 pub(super) fn to_words(&self, sink: &mut impl Extend<Word>) {
28 self.signature.as_ref().unwrap().to_words(sink);
29 for argument in self.parameters.iter() {
30 argument.instruction.to_words(sink);
31 }
32 for (index, block) in self.blocks.iter().enumerate() {
33 Instruction::label(block.label_id).to_words(sink);
34 if index == 0 {
35 for local_var in self.variables.values() {
36 local_var.instruction.to_words(sink);
37 }
38 for local_var in self.ray_query_initialization_tracker_variables.values() {
39 local_var.instruction.to_words(sink);
40 }
41 for local_var in self.ray_query_t_max_tracker_variables.values() {
42 local_var.instruction.to_words(sink);
43 }
44 for local_var in self.force_loop_bounding_vars.iter() {
45 local_var.instruction.to_words(sink);
46 }
47 for internal_var in self.spilled_composites.values() {
48 internal_var.instruction.to_words(sink);
49 }
50 }
51 for instruction in block.body.iter() {
52 instruction.to_words(sink);
53 }
54 }
55 Instruction::function_end().to_words(sink);
56 }
57}
58
59impl Writer {
60 pub fn new(options: &Options) -> Result<Self, Error> {
61 let (major, minor) = options.lang_version;
62 if major != 1 {
63 return Err(Error::UnsupportedVersion(major, minor));
64 }
65
66 let mut capabilities_used = crate::FastIndexSet::default();
67 capabilities_used.insert(spirv::Capability::Shader);
68
69 let mut id_gen = IdGenerator::default();
70 let gl450_ext_inst_id = id_gen.next();
71 let void_type = id_gen.next();
72
73 Ok(Writer {
74 physical_layout: PhysicalLayout::new(major, minor),
75 logical_layout: LogicalLayout::default(),
76 id_gen,
77 capabilities_available: options.capabilities.clone(),
78 capabilities_used,
79 extensions_used: crate::FastIndexSet::default(),
80 debug_strings: vec![],
81 debugs: vec![],
82 annotations: vec![],
83 flags: options.flags,
84 bounds_check_policies: options.bounds_check_policies,
85 zero_initialize_workgroup_memory: options.zero_initialize_workgroup_memory,
86 force_loop_bounding: options.force_loop_bounding,
87 ray_query_initialization_tracking: options.ray_query_initialization_tracking,
88 use_storage_input_output_16: options.use_storage_input_output_16,
89 void_type,
90 lookup_type: crate::FastHashMap::default(),
91 lookup_function: crate::FastHashMap::default(),
92 lookup_function_type: crate::FastHashMap::default(),
93 wrapped_functions: crate::FastHashMap::default(),
94 constant_ids: HandleVec::new(),
95 cached_constants: crate::FastHashMap::default(),
96 global_variables: HandleVec::new(),
97 fake_missing_bindings: options.fake_missing_bindings,
98 binding_map: options.binding_map.clone(),
99 saved_cached: CachedExpressions::default(),
100 gl450_ext_inst_id,
101 temp_list: Vec::new(),
102 ray_query_functions: crate::FastHashMap::default(),
103 io_f16_polyfills: super::f16_polyfill::F16IoPolyfill::new(
104 options.use_storage_input_output_16,
105 ),
106 debug_printf: None,
107 })
108 }
109
110 pub fn set_options(&mut self, options: &Options) -> Result<(), Error> {
111 let (major, minor) = options.lang_version;
112 if major != 1 {
113 return Err(Error::UnsupportedVersion(major, minor));
114 }
115 self.physical_layout = PhysicalLayout::new(major, minor);
116 self.capabilities_available = options.capabilities.clone();
117 self.flags = options.flags;
118 self.bounds_check_policies = options.bounds_check_policies;
119 self.zero_initialize_workgroup_memory = options.zero_initialize_workgroup_memory;
120 self.force_loop_bounding = options.force_loop_bounding;
121 self.use_storage_input_output_16 = options.use_storage_input_output_16;
122 self.binding_map = options.binding_map.clone();
123 self.io_f16_polyfills =
124 super::f16_polyfill::F16IoPolyfill::new(options.use_storage_input_output_16);
125 Ok(())
126 }
127
128 pub const fn lang_version(&self) -> (u8, u8) {
130 self.physical_layout.lang_version()
131 }
132
133 fn reset(&mut self) {
143 use super::recyclable::Recyclable;
144 use core::mem::take;
145
146 let mut id_gen = IdGenerator::default();
147 let gl450_ext_inst_id = id_gen.next();
148 let void_type = id_gen.next();
149
150 let fresh = Writer {
153 flags: self.flags,
155 bounds_check_policies: self.bounds_check_policies,
156 zero_initialize_workgroup_memory: self.zero_initialize_workgroup_memory,
157 force_loop_bounding: self.force_loop_bounding,
158 ray_query_initialization_tracking: self.ray_query_initialization_tracking,
159 use_storage_input_output_16: self.use_storage_input_output_16,
160 capabilities_available: take(&mut self.capabilities_available),
161 fake_missing_bindings: self.fake_missing_bindings,
162 binding_map: take(&mut self.binding_map),
163
164 id_gen,
166 void_type,
167 gl450_ext_inst_id,
168
169 capabilities_used: take(&mut self.capabilities_used).recycle(),
171 extensions_used: take(&mut self.extensions_used).recycle(),
172 physical_layout: self.physical_layout.clone().recycle(),
173 logical_layout: take(&mut self.logical_layout).recycle(),
174 debug_strings: take(&mut self.debug_strings).recycle(),
175 debugs: take(&mut self.debugs).recycle(),
176 annotations: take(&mut self.annotations).recycle(),
177 lookup_type: take(&mut self.lookup_type).recycle(),
178 lookup_function: take(&mut self.lookup_function).recycle(),
179 lookup_function_type: take(&mut self.lookup_function_type).recycle(),
180 wrapped_functions: take(&mut self.wrapped_functions).recycle(),
181 constant_ids: take(&mut self.constant_ids).recycle(),
182 cached_constants: take(&mut self.cached_constants).recycle(),
183 global_variables: take(&mut self.global_variables).recycle(),
184 saved_cached: take(&mut self.saved_cached).recycle(),
185 temp_list: take(&mut self.temp_list).recycle(),
186 ray_query_functions: take(&mut self.ray_query_functions).recycle(),
187 io_f16_polyfills: take(&mut self.io_f16_polyfills).recycle(),
188 debug_printf: None,
189 };
190
191 *self = fresh;
192
193 self.capabilities_used.insert(spirv::Capability::Shader);
194 }
195
196 pub(super) fn require_any(
211 &mut self,
212 what: &'static str,
213 capabilities: &[spirv::Capability],
214 ) -> Result<(), Error> {
215 match *capabilities {
216 [] => Ok(()),
217 [first, ..] => {
218 let selected = match self.capabilities_available {
221 None => first,
222 Some(ref available) => {
223 match capabilities
224 .iter()
225 .find(|cap| available.contains::<spirv::Capability>(cap))
227 {
228 Some(&cap) => cap,
229 None => {
230 return Err(Error::MissingCapabilities(what, capabilities.to_vec()))
231 }
232 }
233 }
234 };
235 self.capabilities_used.insert(selected);
236 Ok(())
237 }
238 }
239 }
240
241 pub(super) fn require_all(
260 &mut self,
261 capabilities: &[spirv::Capability],
262 ) -> Result<(), spirv::Capability> {
263 if let Some(ref available) = self.capabilities_available {
264 for requested in capabilities {
265 if !available.contains(requested) {
266 return Err(*requested);
267 }
268 }
269 }
270
271 for requested in capabilities {
272 self.capabilities_used.insert(*requested);
273 }
274
275 Ok(())
276 }
277
278 pub(super) fn use_extension(&mut self, extension: &'static str) {
280 self.extensions_used.insert(extension);
281 }
282
283 pub(super) fn get_type_id(&mut self, lookup_ty: LookupType) -> Word {
284 match self.lookup_type.entry(lookup_ty) {
285 Entry::Occupied(e) => *e.get(),
286 Entry::Vacant(e) => {
287 let local = match lookup_ty {
288 LookupType::Handle(_handle) => unreachable!("Handles are populated at start"),
289 LookupType::Local(local) => local,
290 };
291
292 let id = self.id_gen.next();
293 e.insert(id);
294 self.write_type_declaration_local(id, local);
295 id
296 }
297 }
298 }
299
300 pub(super) fn get_handle_type_id(&mut self, handle: Handle<crate::Type>) -> Word {
301 self.get_type_id(LookupType::Handle(handle))
302 }
303
304 pub(super) fn get_expression_lookup_type(&mut self, tr: &TypeResolution) -> LookupType {
305 match *tr {
306 TypeResolution::Handle(ty_handle) => LookupType::Handle(ty_handle),
307 TypeResolution::Value(ref inner) => {
308 let inner_local_type = self.localtype_from_inner(inner).unwrap();
309 LookupType::Local(inner_local_type)
310 }
311 }
312 }
313
314 pub(super) fn get_expression_type_id(&mut self, tr: &TypeResolution) -> Word {
315 let lookup_ty = self.get_expression_lookup_type(tr);
316 self.get_type_id(lookup_ty)
317 }
318
319 pub(super) fn get_localtype_id(&mut self, local: LocalType) -> Word {
320 self.get_type_id(LookupType::Local(local))
321 }
322
323 pub(super) fn get_pointer_type_id(&mut self, base: Word, class: spirv::StorageClass) -> Word {
324 self.get_type_id(LookupType::Local(LocalType::Pointer { base, class }))
325 }
326
327 pub(super) fn get_handle_pointer_type_id(
328 &mut self,
329 base: Handle<crate::Type>,
330 class: spirv::StorageClass,
331 ) -> Word {
332 let base_id = self.get_handle_type_id(base);
333 self.get_pointer_type_id(base_id, class)
334 }
335
336 pub(super) fn get_ray_query_pointer_id(&mut self) -> Word {
337 let rq_id = self.get_type_id(LookupType::Local(LocalType::RayQuery));
338 self.get_pointer_type_id(rq_id, spirv::StorageClass::Function)
339 }
340
341 pub(super) fn get_resolution_pointer_id(
346 &mut self,
347 resolution: &TypeResolution,
348 class: spirv::StorageClass,
349 ) -> Word {
350 let resolution_type_id = self.get_expression_type_id(resolution);
351 self.get_pointer_type_id(resolution_type_id, class)
352 }
353
354 pub(super) fn get_numeric_type_id(&mut self, numeric: NumericType) -> Word {
355 self.get_type_id(LocalType::Numeric(numeric).into())
356 }
357
358 pub(super) fn get_u32_type_id(&mut self) -> Word {
359 self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::U32))
360 }
361
362 pub(super) fn get_f32_type_id(&mut self) -> Word {
363 self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::F32))
364 }
365
366 pub(super) fn get_vec2u_type_id(&mut self) -> Word {
367 self.get_numeric_type_id(NumericType::Vector {
368 size: crate::VectorSize::Bi,
369 scalar: crate::Scalar::U32,
370 })
371 }
372
373 pub(super) fn get_vec2f_type_id(&mut self) -> Word {
374 self.get_numeric_type_id(NumericType::Vector {
375 size: crate::VectorSize::Bi,
376 scalar: crate::Scalar::F32,
377 })
378 }
379
380 pub(super) fn get_vec3u_type_id(&mut self) -> Word {
381 self.get_numeric_type_id(NumericType::Vector {
382 size: crate::VectorSize::Tri,
383 scalar: crate::Scalar::U32,
384 })
385 }
386
387 pub(super) fn get_f32_pointer_type_id(&mut self, class: spirv::StorageClass) -> Word {
388 let f32_id = self.get_f32_type_id();
389 self.get_pointer_type_id(f32_id, class)
390 }
391
392 pub(super) fn get_vec2u_pointer_type_id(&mut self, class: spirv::StorageClass) -> Word {
393 let vec2u_id = self.get_numeric_type_id(NumericType::Vector {
394 size: crate::VectorSize::Bi,
395 scalar: crate::Scalar::U32,
396 });
397 self.get_pointer_type_id(vec2u_id, class)
398 }
399
400 pub(super) fn get_vec3u_pointer_type_id(&mut self, class: spirv::StorageClass) -> Word {
401 let vec3u_id = self.get_numeric_type_id(NumericType::Vector {
402 size: crate::VectorSize::Tri,
403 scalar: crate::Scalar::U32,
404 });
405 self.get_pointer_type_id(vec3u_id, class)
406 }
407
408 pub(super) fn get_bool_type_id(&mut self) -> Word {
409 self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::BOOL))
410 }
411
412 pub(super) fn get_vec2_bool_type_id(&mut self) -> Word {
413 self.get_numeric_type_id(NumericType::Vector {
414 size: crate::VectorSize::Bi,
415 scalar: crate::Scalar::BOOL,
416 })
417 }
418
419 pub(super) fn get_vec3_bool_type_id(&mut self) -> Word {
420 self.get_numeric_type_id(NumericType::Vector {
421 size: crate::VectorSize::Tri,
422 scalar: crate::Scalar::BOOL,
423 })
424 }
425
426 pub(super) fn decorate(&mut self, id: Word, decoration: spirv::Decoration, operands: &[Word]) {
427 self.annotations
428 .push(Instruction::decorate(id, decoration, operands));
429 }
430
431 fn localtype_from_inner(&mut self, inner: &crate::TypeInner) -> Option<LocalType> {
439 Some(match *inner {
440 crate::TypeInner::Scalar(_)
441 | crate::TypeInner::Atomic(_)
442 | crate::TypeInner::Vector { .. }
443 | crate::TypeInner::Matrix { .. } => {
444 LocalType::Numeric(NumericType::from_inner(inner).unwrap())
447 }
448 crate::TypeInner::Pointer { base, space } => {
449 let base_type_id = self.get_handle_type_id(base);
450 LocalType::Pointer {
451 base: base_type_id,
452 class: map_storage_class(space),
453 }
454 }
455 crate::TypeInner::ValuePointer {
456 size,
457 scalar,
458 space,
459 } => {
460 let base_numeric_type = match size {
461 Some(size) => NumericType::Vector { size, scalar },
462 None => NumericType::Scalar(scalar),
463 };
464 LocalType::Pointer {
465 base: self.get_numeric_type_id(base_numeric_type),
466 class: map_storage_class(space),
467 }
468 }
469 crate::TypeInner::Image {
470 dim,
471 arrayed,
472 class,
473 } => LocalType::Image(LocalImageType::from_inner(dim, arrayed, class)),
474 crate::TypeInner::Sampler { comparison: _ } => LocalType::Sampler,
475 crate::TypeInner::AccelerationStructure { .. } => LocalType::AccelerationStructure,
476 crate::TypeInner::RayQuery { .. } => LocalType::RayQuery,
477 crate::TypeInner::Array { .. }
478 | crate::TypeInner::Struct { .. }
479 | crate::TypeInner::BindingArray { .. } => return None,
480 })
481 }
482
483 fn resolve_resource_binding(
489 &self,
490 res_binding: &crate::ResourceBinding,
491 ) -> Result<BindingInfo, Error> {
492 match self.binding_map.get(res_binding) {
493 Some(target) => Ok(*target),
494 None if self.fake_missing_bindings => Ok(BindingInfo {
495 descriptor_set: res_binding.group,
496 binding: res_binding.binding,
497 binding_array_size: None,
498 }),
499 None => Err(Error::MissingBinding(*res_binding)),
500 }
501 }
502
503 fn write_wrapped_functions(
506 &mut self,
507 ir_function: &crate::Function,
508 info: &FunctionInfo,
509 ir_module: &crate::Module,
510 ) -> Result<(), Error> {
511 log::trace!("Generating wrapped functions for {:?}", ir_function.name);
512
513 for (expr_handle, expr) in ir_function.expressions.iter() {
514 match *expr {
515 crate::Expression::Binary { op, left, right } => {
516 let expr_ty_inner = info[expr_handle].ty.inner_with(&ir_module.types);
517 if let Some(expr_ty) = NumericType::from_inner(expr_ty_inner) {
518 match (op, expr_ty.scalar().kind) {
519 (
525 crate::BinaryOperator::Divide | crate::BinaryOperator::Modulo,
526 crate::ScalarKind::Sint | crate::ScalarKind::Uint,
527 ) => {
528 self.write_wrapped_binary_op(
529 op,
530 expr_ty,
531 &info[left].ty,
532 &info[right].ty,
533 )?;
534 }
535 _ => {}
536 }
537 }
538 }
539 _ => {}
540 }
541 }
542
543 Ok(())
544 }
545
546 fn write_wrapped_binary_op(
566 &mut self,
567 op: crate::BinaryOperator,
568 return_type: NumericType,
569 left_type: &TypeResolution,
570 right_type: &TypeResolution,
571 ) -> Result<(), Error> {
572 let return_type_id = self.get_localtype_id(LocalType::Numeric(return_type));
573 let left_type_id = self.get_expression_type_id(left_type);
574 let right_type_id = self.get_expression_type_id(right_type);
575
576 let wrapped = WrappedFunction::BinaryOp {
578 op,
579 left_type_id,
580 right_type_id,
581 };
582 let function_id = match self.wrapped_functions.entry(wrapped) {
583 Entry::Occupied(_) => return Ok(()),
584 Entry::Vacant(e) => *e.insert(self.id_gen.next()),
585 };
586
587 let scalar = return_type.scalar();
588
589 if self.flags.contains(WriterFlags::DEBUG) {
590 let function_name = match op {
591 crate::BinaryOperator::Divide => "naga_div",
592 crate::BinaryOperator::Modulo => "naga_mod",
593 _ => unreachable!(),
594 };
595 self.debugs
596 .push(Instruction::name(function_id, function_name));
597 }
598 let mut function = Function::default();
599
600 let function_type_id = self.get_function_type(LookupFunctionType {
601 parameter_type_ids: vec![left_type_id, right_type_id],
602 return_type_id,
603 });
604 function.signature = Some(Instruction::function(
605 return_type_id,
606 function_id,
607 spirv::FunctionControl::empty(),
608 function_type_id,
609 ));
610
611 let lhs_id = self.id_gen.next();
612 let rhs_id = self.id_gen.next();
613 if self.flags.contains(WriterFlags::DEBUG) {
614 self.debugs.push(Instruction::name(lhs_id, "lhs"));
615 self.debugs.push(Instruction::name(rhs_id, "rhs"));
616 }
617 let left_par = Instruction::function_parameter(left_type_id, lhs_id);
618 let right_par = Instruction::function_parameter(right_type_id, rhs_id);
619 for instruction in [left_par, right_par] {
620 function.parameters.push(FunctionArgument {
621 instruction,
622 handle_id: 0,
623 });
624 }
625
626 let label_id = self.id_gen.next();
627 let mut block = Block::new(label_id);
628
629 let bool_type = return_type.with_scalar(crate::Scalar::BOOL);
630 let bool_type_id = self.get_numeric_type_id(bool_type);
631
632 let maybe_splat_const = |writer: &mut Self, const_id| match return_type {
633 NumericType::Scalar(_) => const_id,
634 NumericType::Vector { size, .. } => {
635 let constituent_ids = [const_id; crate::VectorSize::MAX];
636 writer.get_constant_composite(
637 LookupType::Local(LocalType::Numeric(return_type)),
638 &constituent_ids[..size as usize],
639 )
640 }
641 NumericType::Matrix { .. } => unreachable!(),
642 };
643
644 let const_zero_id = self.get_constant_scalar_with(0, scalar)?;
645 let composite_zero_id = maybe_splat_const(self, const_zero_id);
646 let rhs_eq_zero_id = self.id_gen.next();
647 block.body.push(Instruction::binary(
648 spirv::Op::IEqual,
649 bool_type_id,
650 rhs_eq_zero_id,
651 rhs_id,
652 composite_zero_id,
653 ));
654 let divisor_selector_id = match scalar.kind {
655 crate::ScalarKind::Sint => {
656 let (const_min_id, const_neg_one_id) = match scalar.width {
657 4 => Ok((
658 self.get_constant_scalar(crate::Literal::I32(i32::MIN)),
659 self.get_constant_scalar(crate::Literal::I32(-1i32)),
660 )),
661 8 => Ok((
662 self.get_constant_scalar(crate::Literal::I64(i64::MIN)),
663 self.get_constant_scalar(crate::Literal::I64(-1i64)),
664 )),
665 _ => Err(Error::Validation("Unexpected scalar width")),
666 }?;
667 let composite_min_id = maybe_splat_const(self, const_min_id);
668 let composite_neg_one_id = maybe_splat_const(self, const_neg_one_id);
669
670 let lhs_eq_int_min_id = self.id_gen.next();
671 block.body.push(Instruction::binary(
672 spirv::Op::IEqual,
673 bool_type_id,
674 lhs_eq_int_min_id,
675 lhs_id,
676 composite_min_id,
677 ));
678 let rhs_eq_neg_one_id = self.id_gen.next();
679 block.body.push(Instruction::binary(
680 spirv::Op::IEqual,
681 bool_type_id,
682 rhs_eq_neg_one_id,
683 rhs_id,
684 composite_neg_one_id,
685 ));
686 let lhs_eq_int_min_and_rhs_eq_neg_one_id = self.id_gen.next();
687 block.body.push(Instruction::binary(
688 spirv::Op::LogicalAnd,
689 bool_type_id,
690 lhs_eq_int_min_and_rhs_eq_neg_one_id,
691 lhs_eq_int_min_id,
692 rhs_eq_neg_one_id,
693 ));
694 let rhs_eq_zero_or_lhs_eq_int_min_and_rhs_eq_neg_one_id = self.id_gen.next();
695 block.body.push(Instruction::binary(
696 spirv::Op::LogicalOr,
697 bool_type_id,
698 rhs_eq_zero_or_lhs_eq_int_min_and_rhs_eq_neg_one_id,
699 rhs_eq_zero_id,
700 lhs_eq_int_min_and_rhs_eq_neg_one_id,
701 ));
702 rhs_eq_zero_or_lhs_eq_int_min_and_rhs_eq_neg_one_id
703 }
704 crate::ScalarKind::Uint => rhs_eq_zero_id,
705 _ => unreachable!(),
706 };
707
708 let const_one_id = self.get_constant_scalar_with(1, scalar)?;
709 let composite_one_id = maybe_splat_const(self, const_one_id);
710 let divisor_id = self.id_gen.next();
711 block.body.push(Instruction::select(
712 right_type_id,
713 divisor_id,
714 divisor_selector_id,
715 composite_one_id,
716 rhs_id,
717 ));
718 let op = match (op, scalar.kind) {
719 (crate::BinaryOperator::Divide, crate::ScalarKind::Sint) => spirv::Op::SDiv,
720 (crate::BinaryOperator::Divide, crate::ScalarKind::Uint) => spirv::Op::UDiv,
721 (crate::BinaryOperator::Modulo, crate::ScalarKind::Sint) => spirv::Op::SRem,
722 (crate::BinaryOperator::Modulo, crate::ScalarKind::Uint) => spirv::Op::UMod,
723 _ => unreachable!(),
724 };
725 let return_id = self.id_gen.next();
726 block.body.push(Instruction::binary(
727 op,
728 return_type_id,
729 return_id,
730 lhs_id,
731 divisor_id,
732 ));
733
734 function.consume(block, Instruction::return_value(return_id));
735 function.to_words(&mut self.logical_layout.function_definitions);
736 Ok(())
737 }
738
739 fn write_function(
740 &mut self,
741 ir_function: &crate::Function,
742 info: &FunctionInfo,
743 ir_module: &crate::Module,
744 mut interface: Option<FunctionInterface>,
745 debug_info: &Option<DebugInfoInner>,
746 ) -> Result<Word, Error> {
747 self.write_wrapped_functions(ir_function, info, ir_module)?;
748
749 log::trace!("Generating code for {:?}", ir_function.name);
750 let mut function = Function::default();
751
752 let prelude_id = self.id_gen.next();
753 let mut prelude = Block::new(prelude_id);
754 let mut ep_context = EntryPointContext {
755 argument_ids: Vec::new(),
756 results: Vec::new(),
757 };
758
759 let mut local_invocation_id = None;
760
761 let mut parameter_type_ids = Vec::with_capacity(ir_function.arguments.len());
762 for argument in ir_function.arguments.iter() {
763 let class = spirv::StorageClass::Input;
764 let handle_ty = ir_module.types[argument.ty].inner.is_handle();
765 let argument_type_id = if handle_ty {
766 self.get_handle_pointer_type_id(argument.ty, spirv::StorageClass::UniformConstant)
767 } else {
768 self.get_handle_type_id(argument.ty)
769 };
770
771 if let Some(ref mut iface) = interface {
772 let id = if let Some(ref binding) = argument.binding {
773 let name = argument.name.as_deref();
774
775 let varying_id = self.write_varying(
776 ir_module,
777 iface.stage,
778 class,
779 name,
780 argument.ty,
781 binding,
782 )?;
783 iface.varying_ids.push(varying_id);
784 let id = self.load_io_with_f16_polyfill(
785 &mut prelude.body,
786 varying_id,
787 argument_type_id,
788 );
789
790 if binding == &crate::Binding::BuiltIn(crate::BuiltIn::LocalInvocationId) {
791 local_invocation_id = Some(id);
792 }
793
794 id
795 } else if let crate::TypeInner::Struct { ref members, .. } =
796 ir_module.types[argument.ty].inner
797 {
798 let struct_id = self.id_gen.next();
799 let mut constituent_ids = Vec::with_capacity(members.len());
800 for member in members {
801 let type_id = self.get_handle_type_id(member.ty);
802 let name = member.name.as_deref();
803 let binding = member.binding.as_ref().unwrap();
804 let varying_id = self.write_varying(
805 ir_module,
806 iface.stage,
807 class,
808 name,
809 member.ty,
810 binding,
811 )?;
812 iface.varying_ids.push(varying_id);
813 let id =
814 self.load_io_with_f16_polyfill(&mut prelude.body, varying_id, type_id);
815 constituent_ids.push(id);
816
817 if binding == &crate::Binding::BuiltIn(crate::BuiltIn::LocalInvocationId) {
818 local_invocation_id = Some(id);
819 }
820 }
821 prelude.body.push(Instruction::composite_construct(
822 argument_type_id,
823 struct_id,
824 &constituent_ids,
825 ));
826 struct_id
827 } else {
828 unreachable!("Missing argument binding on an entry point");
829 };
830 ep_context.argument_ids.push(id);
831 } else {
832 let argument_id = self.id_gen.next();
833 let instruction = Instruction::function_parameter(argument_type_id, argument_id);
834 if self.flags.contains(WriterFlags::DEBUG) {
835 if let Some(ref name) = argument.name {
836 self.debugs.push(Instruction::name(argument_id, name));
837 }
838 }
839 function.parameters.push(FunctionArgument {
840 instruction,
841 handle_id: if handle_ty {
842 let id = self.id_gen.next();
843 prelude.body.push(Instruction::load(
844 self.get_handle_type_id(argument.ty),
845 id,
846 argument_id,
847 None,
848 ));
849 id
850 } else {
851 0
852 },
853 });
854 parameter_type_ids.push(argument_type_id);
855 };
856 }
857
858 let return_type_id = match ir_function.result {
859 Some(ref result) => {
860 if let Some(ref mut iface) = interface {
861 let mut has_point_size = false;
862 let class = spirv::StorageClass::Output;
863 if let Some(ref binding) = result.binding {
864 has_point_size |=
865 *binding == crate::Binding::BuiltIn(crate::BuiltIn::PointSize);
866 let type_id = self.get_handle_type_id(result.ty);
867 let varying_id = self.write_varying(
868 ir_module,
869 iface.stage,
870 class,
871 None,
872 result.ty,
873 binding,
874 )?;
875 iface.varying_ids.push(varying_id);
876 ep_context.results.push(ResultMember {
877 id: varying_id,
878 type_id,
879 built_in: binding.to_built_in(),
880 });
881 } else if let crate::TypeInner::Struct { ref members, .. } =
882 ir_module.types[result.ty].inner
883 {
884 for member in members {
885 let type_id = self.get_handle_type_id(member.ty);
886 let name = member.name.as_deref();
887 let binding = member.binding.as_ref().unwrap();
888 has_point_size |=
889 *binding == crate::Binding::BuiltIn(crate::BuiltIn::PointSize);
890 let varying_id = self.write_varying(
891 ir_module,
892 iface.stage,
893 class,
894 name,
895 member.ty,
896 binding,
897 )?;
898 iface.varying_ids.push(varying_id);
899 ep_context.results.push(ResultMember {
900 id: varying_id,
901 type_id,
902 built_in: binding.to_built_in(),
903 });
904 }
905 } else {
906 unreachable!("Missing result binding on an entry point");
907 }
908
909 if self.flags.contains(WriterFlags::FORCE_POINT_SIZE)
910 && iface.stage == crate::ShaderStage::Vertex
911 && !has_point_size
912 {
913 let varying_id = self.id_gen.next();
915 let pointer_type_id = self.get_f32_pointer_type_id(class);
916 Instruction::variable(pointer_type_id, varying_id, class, None)
917 .to_words(&mut self.logical_layout.declarations);
918 self.decorate(
919 varying_id,
920 spirv::Decoration::BuiltIn,
921 &[spirv::BuiltIn::PointSize as u32],
922 );
923 iface.varying_ids.push(varying_id);
924
925 let default_value_id = self.get_constant_scalar(crate::Literal::F32(1.0));
926 prelude
927 .body
928 .push(Instruction::store(varying_id, default_value_id, None));
929 }
930 self.void_type
931 } else {
932 self.get_handle_type_id(result.ty)
933 }
934 }
935 None => self.void_type,
936 };
937
938 let lookup_function_type = LookupFunctionType {
939 parameter_type_ids,
940 return_type_id,
941 };
942
943 let function_id = self.id_gen.next();
944 if self.flags.contains(WriterFlags::DEBUG) {
945 if let Some(ref name) = ir_function.name {
946 self.debugs.push(Instruction::name(function_id, name));
947 }
948 }
949
950 let function_type = self.get_function_type(lookup_function_type);
951 function.signature = Some(Instruction::function(
952 return_type_id,
953 function_id,
954 spirv::FunctionControl::empty(),
955 function_type,
956 ));
957
958 if interface.is_some() {
959 function.entry_point_context = Some(ep_context);
960 }
961
962 for gv in self.global_variables.iter_mut() {
964 gv.reset_for_function();
965 }
966 for (handle, var) in ir_module.global_variables.iter() {
967 if info[handle].is_empty() {
968 continue;
969 }
970
971 let mut gv = self.global_variables[handle].clone();
972 if let Some(ref mut iface) = interface {
973 if self.physical_layout.version >= 0x10400 {
975 iface.varying_ids.push(gv.var_id);
976 }
977 }
978
979 match ir_module.types[var.ty].inner {
983 crate::TypeInner::BindingArray { .. } => {
984 gv.access_id = gv.var_id;
985 }
986 _ => {
987 if var.space == crate::AddressSpace::Handle {
988 let var_type_id = self.get_handle_type_id(var.ty);
989 let id = self.id_gen.next();
990 prelude
991 .body
992 .push(Instruction::load(var_type_id, id, gv.var_id, None));
993 gv.access_id = gv.var_id;
994 gv.handle_id = id;
995 } else if global_needs_wrapper(ir_module, var) {
996 let class = map_storage_class(var.space);
997 let pointer_type_id = self.get_handle_pointer_type_id(var.ty, class);
998 let index_id = self.get_index_constant(0);
999 let id = self.id_gen.next();
1000 prelude.body.push(Instruction::access_chain(
1001 pointer_type_id,
1002 id,
1003 gv.var_id,
1004 &[index_id],
1005 ));
1006 gv.access_id = id;
1007 } else {
1008 gv.access_id = gv.var_id;
1010 };
1011 }
1012 }
1013
1014 self.global_variables[handle] = gv;
1016 }
1017
1018 let mut context = BlockContext {
1021 ir_module,
1022 ir_function,
1023 fun_info: info,
1024 function: &mut function,
1025 cached: core::mem::take(&mut self.saved_cached),
1027
1028 temp_list: core::mem::take(&mut self.temp_list),
1030 force_loop_bounding: self.force_loop_bounding,
1031 writer: self,
1032 expression_constness: super::ExpressionConstnessTracker::from_arena(
1033 &ir_function.expressions,
1034 ),
1035 ray_query_tracker_expr: crate::FastHashMap::default(),
1036 };
1037
1038 context.cached.reset(ir_function.expressions.len());
1040 for (handle, expr) in ir_function.expressions.iter() {
1041 if (expr.needs_pre_emit() && !matches!(*expr, crate::Expression::LocalVariable(_)))
1042 || context.expression_constness.is_const(handle)
1043 {
1044 context.cache_expression_value(handle, &mut prelude)?;
1045 }
1046 }
1047
1048 for (handle, variable) in ir_function.local_variables.iter() {
1049 let id = context.gen_id();
1050
1051 if context.writer.flags.contains(WriterFlags::DEBUG) {
1052 if let Some(ref name) = variable.name {
1053 context.writer.debugs.push(Instruction::name(id, name));
1054 }
1055 }
1056
1057 let init_word = variable.init.map(|constant| context.cached[constant]);
1058 let pointer_type_id = context
1059 .writer
1060 .get_handle_pointer_type_id(variable.ty, spirv::StorageClass::Function);
1061 let instruction = Instruction::variable(
1062 pointer_type_id,
1063 id,
1064 spirv::StorageClass::Function,
1065 init_word.or_else(|| match ir_module.types[variable.ty].inner {
1066 crate::TypeInner::RayQuery { .. } => None,
1067 _ => {
1068 let type_id = context.get_handle_type_id(variable.ty);
1069 Some(context.writer.write_constant_null(type_id))
1070 }
1071 }),
1072 );
1073 context
1074 .function
1075 .variables
1076 .insert(handle, LocalVariable { id, instruction });
1077
1078 if let crate::TypeInner::RayQuery { .. } = ir_module.types[variable.ty].inner {
1079 let u32_type_id = context.writer.get_u32_type_id();
1083 let ptr_u32_type_id = context
1084 .writer
1085 .get_pointer_type_id(u32_type_id, spirv::StorageClass::Function);
1086 let tracker_id = context.gen_id();
1087 let tracker_init_id = context
1088 .writer
1089 .get_constant_scalar(crate::Literal::U32(super::RayQueryPoint::empty().bits()));
1090 let tracker_instruction = Instruction::variable(
1091 ptr_u32_type_id,
1092 tracker_id,
1093 spirv::StorageClass::Function,
1094 Some(tracker_init_id),
1095 );
1096
1097 context
1098 .function
1099 .ray_query_initialization_tracker_variables
1100 .insert(
1101 handle,
1102 LocalVariable {
1103 id: tracker_id,
1104 instruction: tracker_instruction,
1105 },
1106 );
1107 let f32_type_id = context.writer.get_f32_type_id();
1108 let ptr_f32_type_id = context
1109 .writer
1110 .get_pointer_type_id(f32_type_id, spirv::StorageClass::Function);
1111 let t_max_tracker_id = context.gen_id();
1112 let t_max_tracker_init_id =
1113 context.writer.get_constant_scalar(crate::Literal::F32(0.0));
1114 let t_max_tracker_instruction = Instruction::variable(
1115 ptr_f32_type_id,
1116 t_max_tracker_id,
1117 spirv::StorageClass::Function,
1118 Some(t_max_tracker_init_id),
1119 );
1120
1121 context.function.ray_query_t_max_tracker_variables.insert(
1122 handle,
1123 LocalVariable {
1124 id: t_max_tracker_id,
1125 instruction: t_max_tracker_instruction,
1126 },
1127 );
1128 }
1129 }
1130
1131 for (handle, expr) in ir_function.expressions.iter() {
1132 match *expr {
1133 crate::Expression::LocalVariable(_) => {
1134 context.cache_expression_value(handle, &mut prelude)?;
1137 }
1138 crate::Expression::Access { base, .. }
1139 | crate::Expression::AccessIndex { base, .. } => {
1140 *context.function.access_uses.entry(base).or_insert(0) += 1;
1143 }
1144 _ => {}
1145 }
1146 }
1147
1148 let next_id = context.gen_id();
1149
1150 context
1151 .function
1152 .consume(prelude, Instruction::branch(next_id));
1153
1154 let workgroup_vars_init_exit_block_id =
1155 match (context.writer.zero_initialize_workgroup_memory, interface) {
1156 (
1157 super::ZeroInitializeWorkgroupMemoryMode::Polyfill,
1158 Some(
1159 ref mut interface @ FunctionInterface {
1160 stage:
1161 crate::ShaderStage::Compute
1162 | crate::ShaderStage::Mesh
1163 | crate::ShaderStage::Task,
1164 ..
1165 },
1166 ),
1167 ) => context.writer.generate_workgroup_vars_init_block(
1168 next_id,
1169 ir_module,
1170 info,
1171 local_invocation_id,
1172 interface,
1173 context.function,
1174 ),
1175 _ => None,
1176 };
1177
1178 let main_id = if let Some(exit_id) = workgroup_vars_init_exit_block_id {
1179 exit_id
1180 } else {
1181 next_id
1182 };
1183
1184 context.write_function_body(main_id, debug_info.as_ref())?;
1185
1186 let BlockContext {
1189 cached, temp_list, ..
1190 } = context;
1191 self.saved_cached = cached;
1192 self.temp_list = temp_list;
1193
1194 function.to_words(&mut self.logical_layout.function_definitions);
1195
1196 Ok(function_id)
1197 }
1198
1199 fn write_execution_mode(
1200 &mut self,
1201 function_id: Word,
1202 mode: spirv::ExecutionMode,
1203 ) -> Result<(), Error> {
1204 Instruction::execution_mode(function_id, mode, &[])
1206 .to_words(&mut self.logical_layout.execution_modes);
1207 Ok(())
1208 }
1209
1210 fn write_entry_point(
1212 &mut self,
1213 entry_point: &crate::EntryPoint,
1214 info: &FunctionInfo,
1215 ir_module: &crate::Module,
1216 debug_info: &Option<DebugInfoInner>,
1217 ) -> Result<Instruction, Error> {
1218 let mut interface_ids = Vec::new();
1219 let function_id = self.write_function(
1220 &entry_point.function,
1221 info,
1222 ir_module,
1223 Some(FunctionInterface {
1224 varying_ids: &mut interface_ids,
1225 stage: entry_point.stage,
1226 }),
1227 debug_info,
1228 )?;
1229
1230 let exec_model = match entry_point.stage {
1231 crate::ShaderStage::Vertex => spirv::ExecutionModel::Vertex,
1232 crate::ShaderStage::Fragment => {
1233 self.write_execution_mode(function_id, spirv::ExecutionMode::OriginUpperLeft)?;
1234 match entry_point.early_depth_test {
1235 Some(crate::EarlyDepthTest::Force) => {
1236 self.write_execution_mode(
1237 function_id,
1238 spirv::ExecutionMode::EarlyFragmentTests,
1239 )?;
1240 }
1241 Some(crate::EarlyDepthTest::Allow { conservative }) => {
1242 match conservative {
1247 crate::ConservativeDepth::GreaterEqual => self.write_execution_mode(
1248 function_id,
1249 spirv::ExecutionMode::DepthGreater,
1250 )?,
1251 crate::ConservativeDepth::LessEqual => self.write_execution_mode(
1252 function_id,
1253 spirv::ExecutionMode::DepthLess,
1254 )?,
1255 crate::ConservativeDepth::Unchanged => self.write_execution_mode(
1256 function_id,
1257 spirv::ExecutionMode::DepthUnchanged,
1258 )?,
1259 }
1260 }
1261 None => {}
1262 }
1263 if let Some(ref result) = entry_point.function.result {
1264 if contains_builtin(
1265 result.binding.as_ref(),
1266 result.ty,
1267 &ir_module.types,
1268 crate::BuiltIn::FragDepth,
1269 ) {
1270 self.write_execution_mode(
1271 function_id,
1272 spirv::ExecutionMode::DepthReplacing,
1273 )?;
1274 }
1275 }
1276 spirv::ExecutionModel::Fragment
1277 }
1278 crate::ShaderStage::Compute => {
1279 let execution_mode = spirv::ExecutionMode::LocalSize;
1280 Instruction::execution_mode(
1282 function_id,
1283 execution_mode,
1284 &entry_point.workgroup_size,
1285 )
1286 .to_words(&mut self.logical_layout.execution_modes);
1287 spirv::ExecutionModel::GLCompute
1288 }
1289 crate::ShaderStage::Task | crate::ShaderStage::Mesh => unreachable!(),
1290 };
1291 Ok(Instruction::entry_point(
1294 exec_model,
1295 function_id,
1296 &entry_point.name,
1297 interface_ids.as_slice(),
1298 ))
1299 }
1300
1301 fn make_scalar(&mut self, id: Word, scalar: crate::Scalar) -> Instruction {
1302 use crate::ScalarKind as Sk;
1303
1304 let bits = (scalar.width * BITS_PER_BYTE) as u32;
1305 match scalar.kind {
1306 Sk::Sint | Sk::Uint => {
1307 let signedness = if scalar.kind == Sk::Sint {
1308 super::instructions::Signedness::Signed
1309 } else {
1310 super::instructions::Signedness::Unsigned
1311 };
1312 let cap = match bits {
1313 8 => Some(spirv::Capability::Int8),
1314 16 => Some(spirv::Capability::Int16),
1315 64 => Some(spirv::Capability::Int64),
1316 _ => None,
1317 };
1318 if let Some(cap) = cap {
1319 self.capabilities_used.insert(cap);
1320 }
1321 Instruction::type_int(id, bits, signedness)
1322 }
1323 Sk::Float => {
1324 if bits == 64 {
1325 self.capabilities_used.insert(spirv::Capability::Float64);
1326 }
1327 if bits == 16 {
1328 self.capabilities_used.insert(spirv::Capability::Float16);
1329 self.capabilities_used
1330 .insert(spirv::Capability::StorageBuffer16BitAccess);
1331 self.capabilities_used
1332 .insert(spirv::Capability::UniformAndStorageBuffer16BitAccess);
1333 if self.use_storage_input_output_16 {
1334 self.capabilities_used
1335 .insert(spirv::Capability::StorageInputOutput16);
1336 }
1337 }
1338 Instruction::type_float(id, bits)
1339 }
1340 Sk::Bool => Instruction::type_bool(id),
1341 Sk::AbstractInt | Sk::AbstractFloat => {
1342 unreachable!("abstract types should never reach the backend");
1343 }
1344 }
1345 }
1346
1347 fn request_type_capabilities(&mut self, inner: &crate::TypeInner) -> Result<(), Error> {
1348 match *inner {
1349 crate::TypeInner::Image {
1350 dim,
1351 arrayed,
1352 class,
1353 } => {
1354 let sampled = match class {
1355 crate::ImageClass::Sampled { .. } => true,
1356 crate::ImageClass::Depth { .. } => true,
1357 crate::ImageClass::Storage { format, .. } => {
1358 self.request_image_format_capabilities(format.into())?;
1359 false
1360 }
1361 crate::ImageClass::External => unimplemented!(),
1362 };
1363
1364 match dim {
1365 crate::ImageDimension::D1 => {
1366 if sampled {
1367 self.require_any("sampled 1D images", &[spirv::Capability::Sampled1D])?;
1368 } else {
1369 self.require_any("1D storage images", &[spirv::Capability::Image1D])?;
1370 }
1371 }
1372 crate::ImageDimension::Cube if arrayed => {
1373 if sampled {
1374 self.require_any(
1375 "sampled cube array images",
1376 &[spirv::Capability::SampledCubeArray],
1377 )?;
1378 } else {
1379 self.require_any(
1380 "cube array storage images",
1381 &[spirv::Capability::ImageCubeArray],
1382 )?;
1383 }
1384 }
1385 _ => {}
1386 }
1387 }
1388 crate::TypeInner::AccelerationStructure { .. } => {
1389 self.require_any("Acceleration Structure", &[spirv::Capability::RayQueryKHR])?;
1390 }
1391 crate::TypeInner::RayQuery { .. } => {
1392 self.require_any("Ray Query", &[spirv::Capability::RayQueryKHR])?;
1393 }
1394 crate::TypeInner::Atomic(crate::Scalar { width: 8, kind: _ }) => {
1395 self.require_any("64 bit integer atomics", &[spirv::Capability::Int64Atomics])?;
1396 }
1397 crate::TypeInner::Atomic(crate::Scalar {
1398 width: 4,
1399 kind: crate::ScalarKind::Float,
1400 }) => {
1401 self.require_any(
1402 "32 bit floating-point atomics",
1403 &[spirv::Capability::AtomicFloat32AddEXT],
1404 )?;
1405 self.use_extension("SPV_EXT_shader_atomic_float_add");
1406 }
1407 crate::TypeInner::Matrix {
1409 scalar: crate::Scalar::F16,
1410 ..
1411 }
1412 | crate::TypeInner::Vector {
1413 scalar: crate::Scalar::F16,
1414 ..
1415 }
1416 | crate::TypeInner::Scalar(crate::Scalar::F16) => {
1417 self.require_any("16 bit floating-point", &[spirv::Capability::Float16])?;
1418 self.use_extension("SPV_KHR_16bit_storage");
1419 }
1420 _ => {}
1421 }
1422 Ok(())
1423 }
1424
1425 fn write_numeric_type_declaration_local(&mut self, id: Word, numeric: NumericType) {
1426 let instruction = match numeric {
1427 NumericType::Scalar(scalar) => self.make_scalar(id, scalar),
1428 NumericType::Vector { size, scalar } => {
1429 let scalar_id = self.get_numeric_type_id(NumericType::Scalar(scalar));
1430 Instruction::type_vector(id, scalar_id, size)
1431 }
1432 NumericType::Matrix {
1433 columns,
1434 rows,
1435 scalar,
1436 } => {
1437 let column_id =
1438 self.get_numeric_type_id(NumericType::Vector { size: rows, scalar });
1439 Instruction::type_matrix(id, column_id, columns)
1440 }
1441 };
1442
1443 instruction.to_words(&mut self.logical_layout.declarations);
1444 }
1445
1446 fn write_type_declaration_local(&mut self, id: Word, local_ty: LocalType) {
1447 let instruction = match local_ty {
1448 LocalType::Numeric(numeric) => {
1449 self.write_numeric_type_declaration_local(id, numeric);
1450 return;
1451 }
1452 LocalType::Pointer { base, class } => Instruction::type_pointer(id, class, base),
1453 LocalType::Image(image) => {
1454 let local_type = LocalType::Numeric(NumericType::Scalar(image.sampled_type));
1455 let type_id = self.get_localtype_id(local_type);
1456 Instruction::type_image(id, type_id, image.dim, image.flags, image.image_format)
1457 }
1458 LocalType::Sampler => Instruction::type_sampler(id),
1459 LocalType::SampledImage { image_type_id } => {
1460 Instruction::type_sampled_image(id, image_type_id)
1461 }
1462 LocalType::BindingArray { base, size } => {
1463 let inner_ty = self.get_handle_type_id(base);
1464 let scalar_id = self.get_constant_scalar(crate::Literal::U32(size));
1465 Instruction::type_array(id, inner_ty, scalar_id)
1466 }
1467 LocalType::AccelerationStructure => Instruction::type_acceleration_structure(id),
1468 LocalType::RayQuery => Instruction::type_ray_query(id),
1469 };
1470
1471 instruction.to_words(&mut self.logical_layout.declarations);
1472 }
1473
1474 fn write_type_declaration_arena(
1475 &mut self,
1476 module: &crate::Module,
1477 handle: Handle<crate::Type>,
1478 ) -> Result<Word, Error> {
1479 let ty = &module.types[handle];
1480 self.request_type_capabilities(&ty.inner)?;
1485 let id = if let Some(local) = self.localtype_from_inner(&ty.inner) {
1486 match self.lookup_type.entry(LookupType::Local(local)) {
1490 Entry::Occupied(e) => *e.get(),
1492
1493 Entry::Vacant(e) => {
1495 let id = self.id_gen.next();
1496 e.insert(id);
1497
1498 self.write_type_declaration_local(id, local);
1499
1500 id
1501 }
1502 }
1503 } else {
1504 use spirv::Decoration;
1505
1506 let id = self.id_gen.next();
1507 let instruction = match ty.inner {
1508 crate::TypeInner::Array { base, size, stride } => {
1509 self.decorate(id, Decoration::ArrayStride, &[stride]);
1510
1511 let type_id = self.get_handle_type_id(base);
1512 match size.resolve(module.to_ctx())? {
1513 crate::proc::IndexableLength::Known(length) => {
1514 let length_id = self.get_index_constant(length);
1515 Instruction::type_array(id, type_id, length_id)
1516 }
1517 crate::proc::IndexableLength::Dynamic => {
1518 Instruction::type_runtime_array(id, type_id)
1519 }
1520 }
1521 }
1522 crate::TypeInner::BindingArray { base, size } => {
1523 let type_id = self.get_handle_type_id(base);
1524 match size.resolve(module.to_ctx())? {
1525 crate::proc::IndexableLength::Known(length) => {
1526 let length_id = self.get_index_constant(length);
1527 Instruction::type_array(id, type_id, length_id)
1528 }
1529 crate::proc::IndexableLength::Dynamic => {
1530 Instruction::type_runtime_array(id, type_id)
1531 }
1532 }
1533 }
1534 crate::TypeInner::Struct {
1535 ref members,
1536 span: _,
1537 } => {
1538 let mut has_runtime_array = false;
1539 let mut member_ids = Vec::with_capacity(members.len());
1540 for (index, member) in members.iter().enumerate() {
1541 let member_ty = &module.types[member.ty];
1542 match member_ty.inner {
1543 crate::TypeInner::Array {
1544 base: _,
1545 size: crate::ArraySize::Dynamic,
1546 stride: _,
1547 } => {
1548 has_runtime_array = true;
1549 }
1550 _ => (),
1551 }
1552 self.decorate_struct_member(id, index, member, &module.types)?;
1553 let member_id = self.get_handle_type_id(member.ty);
1554 member_ids.push(member_id);
1555 }
1556 if has_runtime_array {
1557 self.decorate(id, Decoration::Block, &[]);
1558 }
1559 Instruction::type_struct(id, member_ids.as_slice())
1560 }
1561
1562 crate::TypeInner::Scalar(_)
1565 | crate::TypeInner::Atomic(_)
1566 | crate::TypeInner::Vector { .. }
1567 | crate::TypeInner::Matrix { .. }
1568 | crate::TypeInner::Pointer { .. }
1569 | crate::TypeInner::ValuePointer { .. }
1570 | crate::TypeInner::Image { .. }
1571 | crate::TypeInner::Sampler { .. }
1572 | crate::TypeInner::AccelerationStructure { .. }
1573 | crate::TypeInner::RayQuery { .. } => unreachable!(),
1574 };
1575
1576 instruction.to_words(&mut self.logical_layout.declarations);
1577 id
1578 };
1579
1580 self.lookup_type.insert(LookupType::Handle(handle), id);
1582
1583 if self.flags.contains(WriterFlags::DEBUG) {
1584 if let Some(ref name) = ty.name {
1585 self.debugs.push(Instruction::name(id, name));
1586 }
1587 }
1588
1589 Ok(id)
1590 }
1591
1592 fn request_image_format_capabilities(
1593 &mut self,
1594 format: spirv::ImageFormat,
1595 ) -> Result<(), Error> {
1596 use spirv::ImageFormat as If;
1597 match format {
1598 If::Rg32f
1599 | If::Rg16f
1600 | If::R11fG11fB10f
1601 | If::R16f
1602 | If::Rgba16
1603 | If::Rgb10A2
1604 | If::Rg16
1605 | If::Rg8
1606 | If::R16
1607 | If::R8
1608 | If::Rgba16Snorm
1609 | If::Rg16Snorm
1610 | If::Rg8Snorm
1611 | If::R16Snorm
1612 | If::R8Snorm
1613 | If::Rg32i
1614 | If::Rg16i
1615 | If::Rg8i
1616 | If::R16i
1617 | If::R8i
1618 | If::Rgb10a2ui
1619 | If::Rg32ui
1620 | If::Rg16ui
1621 | If::Rg8ui
1622 | If::R16ui
1623 | If::R8ui => self.require_any(
1624 "storage image format",
1625 &[spirv::Capability::StorageImageExtendedFormats],
1626 ),
1627 If::R64ui | If::R64i => {
1628 self.use_extension("SPV_EXT_shader_image_int64");
1629 self.require_any(
1630 "64-bit integer storage image format",
1631 &[spirv::Capability::Int64ImageEXT],
1632 )
1633 }
1634 If::Unknown
1635 | If::Rgba32f
1636 | If::Rgba16f
1637 | If::R32f
1638 | If::Rgba8
1639 | If::Rgba8Snorm
1640 | If::Rgba32i
1641 | If::Rgba16i
1642 | If::Rgba8i
1643 | If::R32i
1644 | If::Rgba32ui
1645 | If::Rgba16ui
1646 | If::Rgba8ui
1647 | If::R32ui => Ok(()),
1648 }
1649 }
1650
1651 pub(super) fn get_index_constant(&mut self, index: Word) -> Word {
1652 self.get_constant_scalar(crate::Literal::U32(index))
1653 }
1654
1655 pub(super) fn get_constant_scalar_with(
1656 &mut self,
1657 value: u8,
1658 scalar: crate::Scalar,
1659 ) -> Result<Word, Error> {
1660 Ok(
1661 self.get_constant_scalar(crate::Literal::new(value, scalar).ok_or(
1662 Error::Validation("Unexpected kind and/or width for Literal"),
1663 )?),
1664 )
1665 }
1666
1667 pub(super) fn get_constant_scalar(&mut self, value: crate::Literal) -> Word {
1668 let scalar = CachedConstant::Literal(value.into());
1669 if let Some(&id) = self.cached_constants.get(&scalar) {
1670 return id;
1671 }
1672 let id = self.id_gen.next();
1673 self.write_constant_scalar(id, &value, None);
1674 self.cached_constants.insert(scalar, id);
1675 id
1676 }
1677
1678 fn write_constant_scalar(
1679 &mut self,
1680 id: Word,
1681 value: &crate::Literal,
1682 debug_name: Option<&String>,
1683 ) {
1684 if self.flags.contains(WriterFlags::DEBUG) {
1685 if let Some(name) = debug_name {
1686 self.debugs.push(Instruction::name(id, name));
1687 }
1688 }
1689 let type_id = self.get_numeric_type_id(NumericType::Scalar(value.scalar()));
1690 let instruction = match *value {
1691 crate::Literal::F64(value) => {
1692 let bits = value.to_bits();
1693 Instruction::constant_64bit(type_id, id, bits as u32, (bits >> 32) as u32)
1694 }
1695 crate::Literal::F32(value) => Instruction::constant_32bit(type_id, id, value.to_bits()),
1696 crate::Literal::F16(value) => {
1697 let low = value.to_bits();
1698 Instruction::constant_16bit(type_id, id, low as u32)
1699 }
1700 crate::Literal::U32(value) => Instruction::constant_32bit(type_id, id, value),
1701 crate::Literal::I32(value) => Instruction::constant_32bit(type_id, id, value as u32),
1702 crate::Literal::U64(value) => {
1703 Instruction::constant_64bit(type_id, id, value as u32, (value >> 32) as u32)
1704 }
1705 crate::Literal::I64(value) => {
1706 Instruction::constant_64bit(type_id, id, value as u32, (value >> 32) as u32)
1707 }
1708 crate::Literal::Bool(true) => Instruction::constant_true(type_id, id),
1709 crate::Literal::Bool(false) => Instruction::constant_false(type_id, id),
1710 crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
1711 unreachable!("Abstract types should not appear in IR presented to backends");
1712 }
1713 };
1714
1715 instruction.to_words(&mut self.logical_layout.declarations);
1716 }
1717
1718 pub(super) fn get_constant_composite(
1719 &mut self,
1720 ty: LookupType,
1721 constituent_ids: &[Word],
1722 ) -> Word {
1723 let composite = CachedConstant::Composite {
1724 ty,
1725 constituent_ids: constituent_ids.to_vec(),
1726 };
1727 if let Some(&id) = self.cached_constants.get(&composite) {
1728 return id;
1729 }
1730 let id = self.id_gen.next();
1731 self.write_constant_composite(id, ty, constituent_ids, None);
1732 self.cached_constants.insert(composite, id);
1733 id
1734 }
1735
1736 fn write_constant_composite(
1737 &mut self,
1738 id: Word,
1739 ty: LookupType,
1740 constituent_ids: &[Word],
1741 debug_name: Option<&String>,
1742 ) {
1743 if self.flags.contains(WriterFlags::DEBUG) {
1744 if let Some(name) = debug_name {
1745 self.debugs.push(Instruction::name(id, name));
1746 }
1747 }
1748 let type_id = self.get_type_id(ty);
1749 Instruction::constant_composite(type_id, id, constituent_ids)
1750 .to_words(&mut self.logical_layout.declarations);
1751 }
1752
1753 pub(super) fn get_constant_null(&mut self, type_id: Word) -> Word {
1754 let null = CachedConstant::ZeroValue(type_id);
1755 if let Some(&id) = self.cached_constants.get(&null) {
1756 return id;
1757 }
1758 let id = self.write_constant_null(type_id);
1759 self.cached_constants.insert(null, id);
1760 id
1761 }
1762
1763 pub(super) fn write_constant_null(&mut self, type_id: Word) -> Word {
1764 let null_id = self.id_gen.next();
1765 Instruction::constant_null(type_id, null_id)
1766 .to_words(&mut self.logical_layout.declarations);
1767 null_id
1768 }
1769
1770 fn write_constant_expr(
1771 &mut self,
1772 handle: Handle<crate::Expression>,
1773 ir_module: &crate::Module,
1774 mod_info: &ModuleInfo,
1775 ) -> Result<Word, Error> {
1776 let id = match ir_module.global_expressions[handle] {
1777 crate::Expression::Literal(literal) => self.get_constant_scalar(literal),
1778 crate::Expression::Constant(constant) => {
1779 let constant = &ir_module.constants[constant];
1780 self.constant_ids[constant.init]
1781 }
1782 crate::Expression::ZeroValue(ty) => {
1783 let type_id = self.get_handle_type_id(ty);
1784 self.get_constant_null(type_id)
1785 }
1786 crate::Expression::Compose { ty, ref components } => {
1787 let component_ids: Vec<_> = crate::proc::flatten_compose(
1788 ty,
1789 components,
1790 &ir_module.global_expressions,
1791 &ir_module.types,
1792 )
1793 .map(|component| self.constant_ids[component])
1794 .collect();
1795 self.get_constant_composite(LookupType::Handle(ty), component_ids.as_slice())
1796 }
1797 crate::Expression::Splat { size, value } => {
1798 let value_id = self.constant_ids[value];
1799 let component_ids = &[value_id; 4][..size as usize];
1800
1801 let ty = self.get_expression_lookup_type(&mod_info[handle]);
1802
1803 self.get_constant_composite(ty, component_ids)
1804 }
1805 _ => {
1806 return Err(Error::Override);
1807 }
1808 };
1809
1810 self.constant_ids[handle] = id;
1811
1812 Ok(id)
1813 }
1814
1815 pub(super) fn write_control_barrier(&mut self, flags: crate::Barrier, block: &mut Block) {
1816 let memory_scope = if flags.contains(crate::Barrier::STORAGE) {
1817 spirv::Scope::Device
1818 } else if flags.contains(crate::Barrier::SUB_GROUP) {
1819 spirv::Scope::Subgroup
1820 } else {
1821 spirv::Scope::Workgroup
1822 };
1823 let mut semantics = spirv::MemorySemantics::ACQUIRE_RELEASE;
1824 semantics.set(
1825 spirv::MemorySemantics::UNIFORM_MEMORY,
1826 flags.contains(crate::Barrier::STORAGE),
1827 );
1828 semantics.set(
1829 spirv::MemorySemantics::WORKGROUP_MEMORY,
1830 flags.contains(crate::Barrier::WORK_GROUP),
1831 );
1832 semantics.set(
1833 spirv::MemorySemantics::SUBGROUP_MEMORY,
1834 flags.contains(crate::Barrier::SUB_GROUP),
1835 );
1836 semantics.set(
1837 spirv::MemorySemantics::IMAGE_MEMORY,
1838 flags.contains(crate::Barrier::TEXTURE),
1839 );
1840 let exec_scope_id = if flags.contains(crate::Barrier::SUB_GROUP) {
1841 self.get_index_constant(spirv::Scope::Subgroup as u32)
1842 } else {
1843 self.get_index_constant(spirv::Scope::Workgroup as u32)
1844 };
1845 let mem_scope_id = self.get_index_constant(memory_scope as u32);
1846 let semantics_id = self.get_index_constant(semantics.bits());
1847 block.body.push(Instruction::control_barrier(
1848 exec_scope_id,
1849 mem_scope_id,
1850 semantics_id,
1851 ));
1852 }
1853
1854 pub(super) fn write_memory_barrier(&mut self, flags: crate::Barrier, block: &mut Block) {
1855 let mut semantics = spirv::MemorySemantics::ACQUIRE_RELEASE;
1856 semantics.set(
1857 spirv::MemorySemantics::UNIFORM_MEMORY,
1858 flags.contains(crate::Barrier::STORAGE),
1859 );
1860 semantics.set(
1861 spirv::MemorySemantics::WORKGROUP_MEMORY,
1862 flags.contains(crate::Barrier::WORK_GROUP),
1863 );
1864 semantics.set(
1865 spirv::MemorySemantics::SUBGROUP_MEMORY,
1866 flags.contains(crate::Barrier::SUB_GROUP),
1867 );
1868 semantics.set(
1869 spirv::MemorySemantics::IMAGE_MEMORY,
1870 flags.contains(crate::Barrier::TEXTURE),
1871 );
1872 let mem_scope_id = if flags.contains(crate::Barrier::STORAGE) {
1873 self.get_index_constant(spirv::Scope::Device as u32)
1874 } else if flags.contains(crate::Barrier::SUB_GROUP) {
1875 self.get_index_constant(spirv::Scope::Subgroup as u32)
1876 } else {
1877 self.get_index_constant(spirv::Scope::Workgroup as u32)
1878 };
1879 let semantics_id = self.get_index_constant(semantics.bits());
1880 block
1881 .body
1882 .push(Instruction::memory_barrier(mem_scope_id, semantics_id));
1883 }
1884
1885 fn generate_workgroup_vars_init_block(
1886 &mut self,
1887 entry_id: Word,
1888 ir_module: &crate::Module,
1889 info: &FunctionInfo,
1890 local_invocation_id: Option<Word>,
1891 interface: &mut FunctionInterface,
1892 function: &mut Function,
1893 ) -> Option<Word> {
1894 let body = ir_module
1895 .global_variables
1896 .iter()
1897 .filter(|&(handle, var)| {
1898 !info[handle].is_empty() && var.space == crate::AddressSpace::WorkGroup
1899 })
1900 .map(|(handle, var)| {
1901 let var_id = self.global_variables[handle].var_id;
1905 let var_type_id = self.get_handle_type_id(var.ty);
1906 let init_word = self.get_constant_null(var_type_id);
1907 Instruction::store(var_id, init_word, None)
1908 })
1909 .collect::<Vec<_>>();
1910
1911 if body.is_empty() {
1912 return None;
1913 }
1914
1915 let uint3_type_id = self.get_vec3u_type_id();
1916
1917 let mut pre_if_block = Block::new(entry_id);
1918
1919 let local_invocation_id = if let Some(local_invocation_id) = local_invocation_id {
1920 local_invocation_id
1921 } else {
1922 let varying_id = self.id_gen.next();
1923 let class = spirv::StorageClass::Input;
1924 let pointer_type_id = self.get_vec3u_pointer_type_id(class);
1925
1926 Instruction::variable(pointer_type_id, varying_id, class, None)
1927 .to_words(&mut self.logical_layout.declarations);
1928
1929 self.decorate(
1930 varying_id,
1931 spirv::Decoration::BuiltIn,
1932 &[spirv::BuiltIn::LocalInvocationId as u32],
1933 );
1934
1935 interface.varying_ids.push(varying_id);
1936 let id = self.id_gen.next();
1937 pre_if_block
1938 .body
1939 .push(Instruction::load(uint3_type_id, id, varying_id, None));
1940
1941 id
1942 };
1943
1944 let zero_id = self.get_constant_null(uint3_type_id);
1945 let bool3_type_id = self.get_vec3_bool_type_id();
1946
1947 let eq_id = self.id_gen.next();
1948 pre_if_block.body.push(Instruction::binary(
1949 spirv::Op::IEqual,
1950 bool3_type_id,
1951 eq_id,
1952 local_invocation_id,
1953 zero_id,
1954 ));
1955
1956 let condition_id = self.id_gen.next();
1957 let bool_type_id = self.get_bool_type_id();
1958 pre_if_block.body.push(Instruction::relational(
1959 spirv::Op::All,
1960 bool_type_id,
1961 condition_id,
1962 eq_id,
1963 ));
1964
1965 let merge_id = self.id_gen.next();
1966 pre_if_block.body.push(Instruction::selection_merge(
1967 merge_id,
1968 spirv::SelectionControl::NONE,
1969 ));
1970
1971 let accept_id = self.id_gen.next();
1972 function.consume(
1973 pre_if_block,
1974 Instruction::branch_conditional(condition_id, accept_id, merge_id),
1975 );
1976
1977 let accept_block = Block {
1978 label_id: accept_id,
1979 body,
1980 };
1981 function.consume(accept_block, Instruction::branch(merge_id));
1982
1983 let mut post_if_block = Block::new(merge_id);
1984
1985 self.write_control_barrier(crate::Barrier::WORK_GROUP, &mut post_if_block);
1986
1987 let next_id = self.id_gen.next();
1988 function.consume(post_if_block, Instruction::branch(next_id));
1989 Some(next_id)
1990 }
1991
1992 fn write_varying(
2012 &mut self,
2013 ir_module: &crate::Module,
2014 stage: crate::ShaderStage,
2015 class: spirv::StorageClass,
2016 debug_name: Option<&str>,
2017 ty: Handle<crate::Type>,
2018 binding: &crate::Binding,
2019 ) -> Result<Word, Error> {
2020 use crate::TypeInner;
2021
2022 let id = self.id_gen.next();
2023 let ty_inner = &ir_module.types[ty].inner;
2024 let needs_polyfill = self.needs_f16_polyfill(ty_inner);
2025
2026 let pointer_type_id = if needs_polyfill {
2027 let f32_value_local =
2028 super::f16_polyfill::F16IoPolyfill::create_polyfill_type(ty_inner)
2029 .expect("needs_polyfill returned true but create_polyfill_type returned None");
2030
2031 let f32_type_id = self.get_localtype_id(f32_value_local);
2032 let ptr_id = self.get_pointer_type_id(f32_type_id, class);
2033 self.io_f16_polyfills.register_io_var(id, f32_type_id);
2034
2035 ptr_id
2036 } else {
2037 self.get_handle_pointer_type_id(ty, class)
2038 };
2039
2040 Instruction::variable(pointer_type_id, id, class, None)
2041 .to_words(&mut self.logical_layout.declarations);
2042
2043 if self
2044 .flags
2045 .contains(WriterFlags::DEBUG | WriterFlags::LABEL_VARYINGS)
2046 {
2047 if let Some(name) = debug_name {
2048 self.debugs.push(Instruction::name(id, name));
2049 }
2050 }
2051
2052 use spirv::{BuiltIn, Decoration};
2053
2054 match *binding {
2055 crate::Binding::Location {
2056 location,
2057 interpolation,
2058 sampling,
2059 blend_src,
2060 per_primitive: _,
2061 } => {
2062 self.decorate(id, Decoration::Location, &[location]);
2063
2064 let no_decorations =
2065 (class == spirv::StorageClass::Input && stage == crate::ShaderStage::Vertex) ||
2069 (class == spirv::StorageClass::Output && stage == crate::ShaderStage::Fragment);
2073
2074 if !no_decorations {
2075 match interpolation {
2076 None | Some(crate::Interpolation::Perspective) => (),
2078 Some(crate::Interpolation::Flat) => {
2079 self.decorate(id, Decoration::Flat, &[]);
2080 }
2081 Some(crate::Interpolation::Linear) => {
2082 self.decorate(id, Decoration::NoPerspective, &[]);
2083 }
2084 }
2085 match sampling {
2086 None
2088 | Some(
2089 crate::Sampling::Center
2090 | crate::Sampling::First
2091 | crate::Sampling::Either,
2092 ) => (),
2093 Some(crate::Sampling::Centroid) => {
2094 self.decorate(id, Decoration::Centroid, &[]);
2095 }
2096 Some(crate::Sampling::Sample) => {
2097 self.require_any(
2098 "per-sample interpolation",
2099 &[spirv::Capability::SampleRateShading],
2100 )?;
2101 self.decorate(id, Decoration::Sample, &[]);
2102 }
2103 }
2104 }
2105 if let Some(blend_src) = blend_src {
2106 self.decorate(id, Decoration::Index, &[blend_src]);
2107 }
2108 }
2109 crate::Binding::BuiltIn(built_in) => {
2110 use crate::BuiltIn as Bi;
2111 let built_in = match built_in {
2112 Bi::Position { invariant } => {
2113 if invariant {
2114 self.decorate(id, Decoration::Invariant, &[]);
2115 }
2116
2117 if class == spirv::StorageClass::Output {
2118 BuiltIn::Position
2119 } else {
2120 BuiltIn::FragCoord
2121 }
2122 }
2123 Bi::ViewIndex => {
2124 self.require_any("`view_index` built-in", &[spirv::Capability::MultiView])?;
2125 BuiltIn::ViewIndex
2126 }
2127 Bi::BaseInstance => BuiltIn::BaseInstance,
2129 Bi::BaseVertex => BuiltIn::BaseVertex,
2130 Bi::ClipDistance => {
2131 self.require_any(
2132 "`clip_distance` built-in",
2133 &[spirv::Capability::ClipDistance],
2134 )?;
2135 BuiltIn::ClipDistance
2136 }
2137 Bi::CullDistance => {
2138 self.require_any(
2139 "`cull_distance` built-in",
2140 &[spirv::Capability::CullDistance],
2141 )?;
2142 BuiltIn::CullDistance
2143 }
2144 Bi::InstanceIndex => BuiltIn::InstanceIndex,
2145 Bi::PointSize => BuiltIn::PointSize,
2146 Bi::VertexIndex => BuiltIn::VertexIndex,
2147 Bi::DrawID => BuiltIn::DrawIndex,
2148 Bi::FragDepth => BuiltIn::FragDepth,
2150 Bi::PointCoord => BuiltIn::PointCoord,
2151 Bi::FrontFacing => BuiltIn::FrontFacing,
2152 Bi::PrimitiveIndex => {
2153 self.require_any(
2154 "`primitive_index` built-in",
2155 &[spirv::Capability::Geometry],
2156 )?;
2157 BuiltIn::PrimitiveId
2158 }
2159 Bi::Barycentric => {
2160 self.require_any(
2161 "`barycentric` built-in",
2162 &[spirv::Capability::FragmentBarycentricKHR],
2163 )?;
2164 self.use_extension("SPV_KHR_fragment_shader_barycentric");
2165 BuiltIn::BaryCoordKHR
2166 }
2167 Bi::SampleIndex => {
2168 self.require_any(
2169 "`sample_index` built-in",
2170 &[spirv::Capability::SampleRateShading],
2171 )?;
2172
2173 BuiltIn::SampleId
2174 }
2175 Bi::SampleMask => BuiltIn::SampleMask,
2176 Bi::GlobalInvocationId => BuiltIn::GlobalInvocationId,
2178 Bi::LocalInvocationId => BuiltIn::LocalInvocationId,
2179 Bi::LocalInvocationIndex => BuiltIn::LocalInvocationIndex,
2180 Bi::WorkGroupId => BuiltIn::WorkgroupId,
2181 Bi::WorkGroupSize => BuiltIn::WorkgroupSize,
2182 Bi::NumWorkGroups => BuiltIn::NumWorkgroups,
2183 Bi::NumSubgroups => {
2185 self.require_any(
2186 "`num_subgroups` built-in",
2187 &[spirv::Capability::GroupNonUniform],
2188 )?;
2189 BuiltIn::NumSubgroups
2190 }
2191 Bi::SubgroupId => {
2192 self.require_any(
2193 "`subgroup_id` built-in",
2194 &[spirv::Capability::GroupNonUniform],
2195 )?;
2196 BuiltIn::SubgroupId
2197 }
2198 Bi::SubgroupSize => {
2199 self.require_any(
2200 "`subgroup_size` built-in",
2201 &[
2202 spirv::Capability::GroupNonUniform,
2203 spirv::Capability::SubgroupBallotKHR,
2204 ],
2205 )?;
2206 BuiltIn::SubgroupSize
2207 }
2208 Bi::SubgroupInvocationId => {
2209 self.require_any(
2210 "`subgroup_invocation_id` built-in",
2211 &[
2212 spirv::Capability::GroupNonUniform,
2213 spirv::Capability::SubgroupBallotKHR,
2214 ],
2215 )?;
2216 BuiltIn::SubgroupLocalInvocationId
2217 }
2218 Bi::MeshTaskSize
2219 | Bi::CullPrimitive
2220 | Bi::PointIndex
2221 | Bi::LineIndices
2222 | Bi::TriangleIndices
2223 | Bi::VertexCount
2224 | Bi::PrimitiveCount
2225 | Bi::Vertices
2226 | Bi::Primitives => unreachable!(),
2227 };
2228
2229 self.decorate(id, Decoration::BuiltIn, &[built_in as u32]);
2230
2231 use crate::ScalarKind as Sk;
2232
2233 if class == spirv::StorageClass::Input && stage == crate::ShaderStage::Fragment {
2239 let is_flat = match ir_module.types[ty].inner {
2240 TypeInner::Scalar(scalar) | TypeInner::Vector { scalar, .. } => match scalar
2241 .kind
2242 {
2243 Sk::Uint | Sk::Sint | Sk::Bool => true,
2244 Sk::Float => false,
2245 Sk::AbstractInt | Sk::AbstractFloat => {
2246 return Err(Error::Validation(
2247 "Abstract types should not appear in IR presented to backends",
2248 ))
2249 }
2250 },
2251 _ => false,
2252 };
2253
2254 if is_flat {
2255 self.decorate(id, Decoration::Flat, &[]);
2256 }
2257 }
2258 }
2259 }
2260
2261 Ok(id)
2262 }
2263
2264 pub(super) fn load_io_with_f16_polyfill(
2267 &mut self,
2268 body: &mut Vec<Instruction>,
2269 varying_id: Word,
2270 target_type_id: Word,
2271 ) -> Word {
2272 let tmp = self.id_gen.next();
2273 if let Some(f32_ty) = self.io_f16_polyfills.get_f32_io_type(varying_id) {
2274 body.push(Instruction::load(f32_ty, tmp, varying_id, None));
2275 let converted = self.id_gen.next();
2276 super::f16_polyfill::F16IoPolyfill::emit_f32_to_f16_conversion(
2277 tmp,
2278 target_type_id,
2279 converted,
2280 body,
2281 );
2282 converted
2283 } else {
2284 body.push(Instruction::load(target_type_id, tmp, varying_id, None));
2285 tmp
2286 }
2287 }
2288
2289 pub(super) fn store_io_with_f16_polyfill(
2291 &mut self,
2292 body: &mut Vec<Instruction>,
2293 varying_id: Word,
2294 value_id: Word,
2295 ) {
2296 if let Some(f32_ty) = self.io_f16_polyfills.get_f32_io_type(varying_id) {
2297 let converted = self.id_gen.next();
2298 super::f16_polyfill::F16IoPolyfill::emit_f16_to_f32_conversion(
2299 value_id, f32_ty, converted, body,
2300 );
2301 body.push(Instruction::store(varying_id, converted, None));
2302 } else {
2303 body.push(Instruction::store(varying_id, value_id, None));
2304 }
2305 }
2306
2307 fn write_global_variable(
2308 &mut self,
2309 ir_module: &crate::Module,
2310 global_variable: &crate::GlobalVariable,
2311 ) -> Result<Word, Error> {
2312 use spirv::Decoration;
2313
2314 let id = self.id_gen.next();
2315 let class = map_storage_class(global_variable.space);
2316
2317 if self.flags.contains(WriterFlags::DEBUG) {
2320 if let Some(ref name) = global_variable.name {
2321 self.debugs.push(Instruction::name(id, name));
2322 }
2323 }
2324
2325 let storage_access = match global_variable.space {
2326 crate::AddressSpace::Storage { access } => Some(access),
2327 _ => match ir_module.types[global_variable.ty].inner {
2328 crate::TypeInner::Image {
2329 class: crate::ImageClass::Storage { access, .. },
2330 ..
2331 } => Some(access),
2332 _ => None,
2333 },
2334 };
2335 if let Some(storage_access) = storage_access {
2336 if !storage_access.contains(crate::StorageAccess::LOAD) {
2337 self.decorate(id, Decoration::NonReadable, &[]);
2338 }
2339 if !storage_access.contains(crate::StorageAccess::STORE) {
2340 self.decorate(id, Decoration::NonWritable, &[]);
2341 }
2342 }
2343
2344 let mut substitute_inner_type_lookup = None;
2348 if let Some(ref res_binding) = global_variable.binding {
2349 let bind_target = self.resolve_resource_binding(res_binding)?;
2350 self.decorate(id, Decoration::DescriptorSet, &[bind_target.descriptor_set]);
2351 self.decorate(id, Decoration::Binding, &[bind_target.binding]);
2352
2353 if let Some(remapped_binding_array_size) = bind_target.binding_array_size {
2354 if let crate::TypeInner::BindingArray { base, .. } =
2355 ir_module.types[global_variable.ty].inner
2356 {
2357 let binding_array_type_id =
2358 self.get_type_id(LookupType::Local(LocalType::BindingArray {
2359 base,
2360 size: remapped_binding_array_size,
2361 }));
2362 substitute_inner_type_lookup = Some(LookupType::Local(LocalType::Pointer {
2363 base: binding_array_type_id,
2364 class,
2365 }));
2366 }
2367 }
2368 };
2369
2370 let init_word = global_variable
2371 .init
2372 .map(|constant| self.constant_ids[constant]);
2373 let inner_type_id = self.get_type_id(
2374 substitute_inner_type_lookup.unwrap_or(LookupType::Handle(global_variable.ty)),
2375 );
2376
2377 let pointer_type_id = if global_needs_wrapper(ir_module, global_variable) {
2379 let wrapper_type_id = self.id_gen.next();
2380
2381 self.decorate(wrapper_type_id, Decoration::Block, &[]);
2382 let member = crate::StructMember {
2383 name: None,
2384 ty: global_variable.ty,
2385 binding: None,
2386 offset: 0,
2387 };
2388 self.decorate_struct_member(wrapper_type_id, 0, &member, &ir_module.types)?;
2389
2390 Instruction::type_struct(wrapper_type_id, &[inner_type_id])
2391 .to_words(&mut self.logical_layout.declarations);
2392
2393 let pointer_type_id = self.id_gen.next();
2394 Instruction::type_pointer(pointer_type_id, class, wrapper_type_id)
2395 .to_words(&mut self.logical_layout.declarations);
2396
2397 pointer_type_id
2398 } else {
2399 if let crate::AddressSpace::Storage { .. } = global_variable.space {
2405 match ir_module.types[global_variable.ty].inner {
2406 crate::TypeInner::BindingArray { base, .. } => {
2407 let ty = &ir_module.types[base];
2408 let mut should_decorate = true;
2409 if let crate::TypeInner::Struct { ref members, .. } = ty.inner {
2413 if let Some(last_member) = members.last() {
2415 if let &crate::TypeInner::Array {
2416 size: crate::ArraySize::Dynamic,
2417 ..
2418 } = &ir_module.types[last_member.ty].inner
2419 {
2420 should_decorate = false;
2421 }
2422 }
2423 }
2424 if should_decorate {
2425 let decorated_id = self.get_handle_type_id(base);
2426 self.decorate(decorated_id, Decoration::Block, &[]);
2427 }
2428 }
2429 _ => (),
2430 };
2431 }
2432 if substitute_inner_type_lookup.is_some() {
2433 inner_type_id
2434 } else {
2435 self.get_handle_pointer_type_id(global_variable.ty, class)
2436 }
2437 };
2438
2439 let init_word = match (global_variable.space, self.zero_initialize_workgroup_memory) {
2440 (crate::AddressSpace::Private, _)
2441 | (crate::AddressSpace::WorkGroup, super::ZeroInitializeWorkgroupMemoryMode::Native) => {
2442 init_word.or_else(|| Some(self.get_constant_null(inner_type_id)))
2443 }
2444 _ => init_word,
2445 };
2446
2447 Instruction::variable(pointer_type_id, id, class, init_word)
2448 .to_words(&mut self.logical_layout.declarations);
2449 Ok(id)
2450 }
2451
2452 fn decorate_struct_member(
2457 &mut self,
2458 struct_id: Word,
2459 index: usize,
2460 member: &crate::StructMember,
2461 arena: &UniqueArena<crate::Type>,
2462 ) -> Result<(), Error> {
2463 use spirv::Decoration;
2464
2465 self.annotations.push(Instruction::member_decorate(
2466 struct_id,
2467 index as u32,
2468 Decoration::Offset,
2469 &[member.offset],
2470 ));
2471
2472 if self.flags.contains(WriterFlags::DEBUG) {
2473 if let Some(ref name) = member.name {
2474 self.debugs
2475 .push(Instruction::member_name(struct_id, index as u32, name));
2476 }
2477 }
2478
2479 let mut member_array_subty_inner = &arena[member.ty].inner;
2482 while let crate::TypeInner::Array { base, .. } = *member_array_subty_inner {
2483 member_array_subty_inner = &arena[base].inner;
2484 }
2485
2486 if let crate::TypeInner::Matrix {
2487 columns: _,
2488 rows,
2489 scalar,
2490 } = *member_array_subty_inner
2491 {
2492 let byte_stride = Alignment::from(rows) * scalar.width as u32;
2493 self.annotations.push(Instruction::member_decorate(
2494 struct_id,
2495 index as u32,
2496 Decoration::ColMajor,
2497 &[],
2498 ));
2499 self.annotations.push(Instruction::member_decorate(
2500 struct_id,
2501 index as u32,
2502 Decoration::MatrixStride,
2503 &[byte_stride],
2504 ));
2505 }
2506
2507 Ok(())
2508 }
2509
2510 pub(super) fn get_function_type(&mut self, lookup_function_type: LookupFunctionType) -> Word {
2511 match self
2512 .lookup_function_type
2513 .entry(lookup_function_type.clone())
2514 {
2515 Entry::Occupied(e) => *e.get(),
2516 Entry::Vacant(_) => {
2517 let id = self.id_gen.next();
2518 let instruction = Instruction::type_function(
2519 id,
2520 lookup_function_type.return_type_id,
2521 &lookup_function_type.parameter_type_ids,
2522 );
2523 instruction.to_words(&mut self.logical_layout.declarations);
2524 self.lookup_function_type.insert(lookup_function_type, id);
2525 id
2526 }
2527 }
2528 }
2529
2530 fn write_physical_layout(&mut self) {
2531 self.physical_layout.bound = self.id_gen.0 + 1;
2532 }
2533
2534 fn write_logical_layout(
2535 &mut self,
2536 ir_module: &crate::Module,
2537 mod_info: &ModuleInfo,
2538 ep_index: Option<usize>,
2539 debug_info: &Option<DebugInfo>,
2540 ) -> Result<(), Error> {
2541 fn has_view_index_check(
2542 ir_module: &crate::Module,
2543 binding: Option<&crate::Binding>,
2544 ty: Handle<crate::Type>,
2545 ) -> bool {
2546 match ir_module.types[ty].inner {
2547 crate::TypeInner::Struct { ref members, .. } => members.iter().any(|member| {
2548 has_view_index_check(ir_module, member.binding.as_ref(), member.ty)
2549 }),
2550 _ => binding == Some(&crate::Binding::BuiltIn(crate::BuiltIn::ViewIndex)),
2551 }
2552 }
2553
2554 let has_storage_buffers =
2555 ir_module
2556 .global_variables
2557 .iter()
2558 .any(|(_, var)| match var.space {
2559 crate::AddressSpace::Storage { .. } => true,
2560 _ => false,
2561 });
2562 let has_view_index = ir_module
2563 .entry_points
2564 .iter()
2565 .flat_map(|entry| entry.function.arguments.iter())
2566 .any(|arg| has_view_index_check(ir_module, arg.binding.as_ref(), arg.ty));
2567 let mut has_ray_query = ir_module.special_types.ray_desc.is_some()
2568 | ir_module.special_types.ray_intersection.is_some();
2569 let has_vertex_return = ir_module.special_types.ray_vertex_return.is_some();
2570
2571 for (_, &crate::Type { ref inner, .. }) in ir_module.types.iter() {
2572 if let &crate::TypeInner::AccelerationStructure { .. }
2574 | &crate::TypeInner::RayQuery { .. } = inner
2575 {
2576 has_ray_query = true
2577 }
2578 }
2579
2580 if self.physical_layout.version < 0x10300 && has_storage_buffers {
2581 Instruction::extension("SPV_KHR_storage_buffer_storage_class")
2583 .to_words(&mut self.logical_layout.extensions);
2584 }
2585 if has_view_index {
2586 Instruction::extension("SPV_KHR_multiview")
2587 .to_words(&mut self.logical_layout.extensions)
2588 }
2589 if has_ray_query {
2590 Instruction::extension("SPV_KHR_ray_query")
2591 .to_words(&mut self.logical_layout.extensions)
2592 }
2593 if has_vertex_return {
2594 Instruction::extension("SPV_KHR_ray_tracing_position_fetch")
2595 .to_words(&mut self.logical_layout.extensions);
2596 }
2597 Instruction::type_void(self.void_type).to_words(&mut self.logical_layout.declarations);
2598 Instruction::ext_inst_import(self.gl450_ext_inst_id, "GLSL.std.450")
2599 .to_words(&mut self.logical_layout.ext_inst_imports);
2600
2601 let mut debug_info_inner = None;
2602 if self.flags.contains(WriterFlags::DEBUG) {
2603 if let Some(debug_info) = debug_info.as_ref() {
2604 let source_file_id = self.id_gen.next();
2605 self.debugs
2606 .push(Instruction::string(debug_info.file_name, source_file_id));
2607
2608 debug_info_inner = Some(DebugInfoInner {
2609 source_code: debug_info.source_code,
2610 source_file_id,
2611 });
2612 self.debugs.append(&mut Instruction::source_auto_continued(
2613 debug_info.language,
2614 0,
2615 &debug_info_inner,
2616 ));
2617 }
2618 }
2619
2620 for (handle, _) in ir_module.types.iter() {
2622 self.write_type_declaration_arena(ir_module, handle)?;
2623 }
2624
2625 self.constant_ids
2627 .resize(ir_module.global_expressions.len(), 0);
2628 for (handle, _) in ir_module.global_expressions.iter() {
2629 self.write_constant_expr(handle, ir_module, mod_info)?;
2630 }
2631 debug_assert!(self.constant_ids.iter().all(|&id| id != 0));
2632
2633 if self.flags.contains(WriterFlags::DEBUG) {
2635 for (_, constant) in ir_module.constants.iter() {
2636 if let Some(ref name) = constant.name {
2637 let id = self.constant_ids[constant.init];
2638 self.debugs.push(Instruction::name(id, name));
2639 }
2640 }
2641 }
2642
2643 for (handle, var) in ir_module.global_variables.iter() {
2645 let gvar = match ep_index {
2649 Some(index) if mod_info.get_entry_point(index)[handle].is_empty() => {
2650 GlobalVariable::dummy()
2651 }
2652 _ => {
2653 let id = self.write_global_variable(ir_module, var)?;
2654 GlobalVariable::new(id)
2655 }
2656 };
2657 self.global_variables.insert(handle, gvar);
2658 }
2659
2660 for (handle, ir_function) in ir_module.functions.iter() {
2662 let info = &mod_info[handle];
2663 if let Some(index) = ep_index {
2664 let ep_info = mod_info.get_entry_point(index);
2665 if !ep_info.dominates_global_use(info) {
2669 log::debug!("Skip function {:?}", ir_function.name);
2670 continue;
2671 }
2672
2673 if !info.available_stages.contains(ep_info.available_stages) {
2683 continue;
2684 }
2685 }
2686 let id = self.write_function(ir_function, info, ir_module, None, &debug_info_inner)?;
2687 self.lookup_function.insert(handle, id);
2688 }
2689
2690 for (index, ir_ep) in ir_module.entry_points.iter().enumerate() {
2692 if ep_index.is_some() && ep_index != Some(index) {
2693 continue;
2694 }
2695 let info = mod_info.get_entry_point(index);
2696 let ep_instruction =
2697 self.write_entry_point(ir_ep, info, ir_module, &debug_info_inner)?;
2698 ep_instruction.to_words(&mut self.logical_layout.entry_points);
2699 }
2700
2701 for capability in self.capabilities_used.iter() {
2702 Instruction::capability(*capability).to_words(&mut self.logical_layout.capabilities);
2703 }
2704 for extension in self.extensions_used.iter() {
2705 Instruction::extension(extension).to_words(&mut self.logical_layout.extensions);
2706 }
2707 if ir_module.entry_points.is_empty() {
2708 Instruction::capability(spirv::Capability::Linkage)
2710 .to_words(&mut self.logical_layout.capabilities);
2711 }
2712
2713 let addressing_model = spirv::AddressingModel::Logical;
2714 let memory_model = spirv::MemoryModel::GLSL450;
2715 Instruction::memory_model(addressing_model, memory_model)
2719 .to_words(&mut self.logical_layout.memory_model);
2720
2721 for debug_string in self.debug_strings.iter() {
2722 debug_string.to_words(&mut self.logical_layout.debugs);
2723 }
2724
2725 if self.flags.contains(WriterFlags::DEBUG) {
2726 for debug in self.debugs.iter() {
2727 debug.to_words(&mut self.logical_layout.debugs);
2728 }
2729 }
2730
2731 for annotation in self.annotations.iter() {
2732 annotation.to_words(&mut self.logical_layout.annotations);
2733 }
2734
2735 Ok(())
2736 }
2737
2738 pub fn write(
2739 &mut self,
2740 ir_module: &crate::Module,
2741 info: &ModuleInfo,
2742 pipeline_options: Option<&PipelineOptions>,
2743 debug_info: &Option<DebugInfo>,
2744 words: &mut Vec<Word>,
2745 ) -> Result<(), Error> {
2746 self.reset();
2747
2748 let ep_index = match pipeline_options {
2750 Some(po) => {
2751 let index = ir_module
2752 .entry_points
2753 .iter()
2754 .position(|ep| po.shader_stage == ep.stage && po.entry_point == ep.name)
2755 .ok_or(Error::EntryPointNotFound)?;
2756 Some(index)
2757 }
2758 None => None,
2759 };
2760
2761 self.write_logical_layout(ir_module, info, ep_index, debug_info)?;
2762 self.write_physical_layout();
2763
2764 self.physical_layout.in_words(words);
2765 self.logical_layout.in_words(words);
2766 Ok(())
2767 }
2768
2769 pub const fn get_capabilities_used(&self) -> &crate::FastIndexSet<spirv::Capability> {
2771 &self.capabilities_used
2772 }
2773
2774 pub fn decorate_non_uniform_binding_array_access(&mut self, id: Word) -> Result<(), Error> {
2775 self.require_any("NonUniformEXT", &[spirv::Capability::ShaderNonUniform])?;
2776 self.use_extension("SPV_EXT_descriptor_indexing");
2777 self.decorate(id, spirv::Decoration::NonUniform, &[]);
2778 Ok(())
2779 }
2780
2781 pub(super) fn needs_f16_polyfill(&self, ty_inner: &crate::TypeInner) -> bool {
2782 self.io_f16_polyfills.needs_polyfill(ty_inner)
2783 }
2784
2785 pub(super) fn write_debug_printf(
2786 &mut self,
2787 block: &mut Block,
2788 string: &str,
2789 format_params: &[Word],
2790 ) {
2791 if self.debug_printf.is_none() {
2792 self.use_extension("SPV_KHR_non_semantic_info");
2793 let import_id = self.id_gen.next();
2794 Instruction::ext_inst_import(import_id, "NonSemantic.DebugPrintf")
2795 .to_words(&mut self.logical_layout.ext_inst_imports);
2796 self.debug_printf = Some(import_id)
2797 }
2798
2799 let import_id = self.debug_printf.unwrap();
2800
2801 let string_id = self.id_gen.next();
2802 self.debug_strings
2803 .push(Instruction::string(string, string_id));
2804
2805 let mut operands = Vec::with_capacity(1 + format_params.len());
2806 operands.push(string_id);
2807 operands.extend(format_params.iter());
2808
2809 let print_id = self.id_gen.next();
2810 block.body.push(Instruction::ext_inst(
2811 import_id,
2812 1,
2813 self.void_type,
2814 print_id,
2815 &operands,
2816 ));
2817 }
2818}
2819
2820#[test]
2821fn test_write_physical_layout() {
2822 let mut writer = Writer::new(&Options::default()).unwrap();
2823 assert_eq!(writer.physical_layout.bound, 0);
2824 writer.write_physical_layout();
2825 assert_eq!(writer.physical_layout.bound, 3);
2826}