1use alloc::{format, string::String, vec, vec::Vec};
2
3use arrayvec::ArrayVec;
4use hashbrown::hash_map::Entry;
5use spirv::Word;
6
7use super::{
8 block::DebugInfoInner,
9 helpers::{contains_builtin, global_needs_wrapper, map_storage_class},
10 Block, BlockContext, CachedConstant, CachedExpressions, CooperativeType, DebugInfo,
11 EntryPointContext, Error, Function, FunctionArgument, GlobalVariable, IdGenerator, Instruction,
12 LocalImageType, LocalType, LocalVariable, LogicalLayout, LookupFunctionType, LookupType,
13 NumericType, Options, PhysicalLayout, PipelineOptions, ResultMember, Writer, WriterFlags,
14 BITS_PER_BYTE,
15};
16use crate::{
17 arena::{Handle, HandleVec, UniqueArena},
18 back::spv::{
19 helpers::{is_uniform_matcx2_struct_member_access, BindingDecorations},
20 BindingInfo, Std140CompatTypeInfo, WrappedFunction,
21 },
22 common::ForDebugWithTypes as _,
23 proc::{Alignment, TypeResolution},
24 valid::{FunctionInfo, ModuleInfo},
25};
26
27pub struct FunctionInterface<'a> {
28 pub varying_ids: &'a mut Vec<Word>,
29 pub stage: crate::ShaderStage,
30 pub task_payload: Option<Handle<crate::GlobalVariable>>,
31 pub mesh_info: Option<crate::MeshStageInfo>,
32 pub workgroup_size: [u32; 3],
33}
34
35impl Function {
36 pub(super) fn to_words(&self, sink: &mut impl Extend<Word>) {
37 self.signature.as_ref().unwrap().to_words(sink);
38 for argument in self.parameters.iter() {
39 argument.instruction.to_words(sink);
40 }
41 for (index, block) in self.blocks.iter().enumerate() {
42 Instruction::label(block.label_id).to_words(sink);
43 if index == 0 {
44 for local_var in self.variables.values() {
45 local_var.instruction.to_words(sink);
46 }
47 for local_var in self.ray_query_initialization_tracker_variables.values() {
48 local_var.instruction.to_words(sink);
49 }
50 for local_var in self.ray_query_t_max_tracker_variables.values() {
51 local_var.instruction.to_words(sink);
52 }
53 for local_var in self.force_loop_bounding_vars.iter() {
54 local_var.instruction.to_words(sink);
55 }
56 for internal_var in self.spilled_composites.values() {
57 internal_var.instruction.to_words(sink);
58 }
59 }
60 for instruction in block.body.iter() {
61 instruction.to_words(sink);
62 }
63 }
64 Instruction::function_end().to_words(sink);
65 }
66}
67
68impl Writer {
69 pub fn new(options: &Options) -> Result<Self, Error> {
70 let (major, minor) = options.lang_version;
71 if major != 1 {
72 return Err(Error::UnsupportedVersion(major, minor));
73 }
74
75 let mut capabilities_used = crate::FastIndexSet::default();
76 capabilities_used.insert(spirv::Capability::Shader);
77
78 let mut id_gen = IdGenerator::default();
79 let gl450_ext_inst_id = id_gen.next();
80 let void_type = id_gen.next();
81
82 Ok(Writer {
83 physical_layout: PhysicalLayout::new(major, minor),
84 logical_layout: LogicalLayout::default(),
85 id_gen,
86 capabilities_available: options.capabilities.clone(),
87 capabilities_used,
88 extensions_used: crate::FastIndexSet::default(),
89 debug_strings: vec![],
90 debugs: vec![],
91 annotations: vec![],
92 flags: options.flags,
93 bounds_check_policies: options.bounds_check_policies,
94 zero_initialize_workgroup_memory: options.zero_initialize_workgroup_memory,
95 force_loop_bounding: options.force_loop_bounding,
96 ray_query_initialization_tracking: options.ray_query_initialization_tracking,
97 trace_ray_argument_validation: options.trace_ray_argument_validation,
98 use_storage_input_output_16: options.use_storage_input_output_16,
99 emit_int_div_checks: options.emit_int_div_checks,
100 void_type,
101 tuple_of_u32s_ty_id: None,
102 lookup_type: crate::FastHashMap::default(),
103 lookup_function: crate::FastHashMap::default(),
104 lookup_function_type: crate::FastHashMap::default(),
105 wrapped_functions: crate::FastHashMap::default(),
106 constant_ids: HandleVec::new(),
107 cached_constants: crate::FastHashMap::default(),
108 global_variables: HandleVec::new(),
109 std140_compat_uniform_types: crate::FastHashMap::default(),
110 fake_missing_bindings: options.fake_missing_bindings,
111 binding_map: options.binding_map.clone(),
112 saved_cached: CachedExpressions::default(),
113 gl450_ext_inst_id,
114 temp_list: Vec::new(),
115 ray_query_functions: crate::FastHashMap::default(),
116 ray_tracing_functions: crate::FastHashMap::default(),
117 has_ray_tracing_pipeline: false,
118 io_f16_polyfills: super::f16_polyfill::F16IoPolyfill::new(
119 options.use_storage_input_output_16,
120 ),
121 debug_printf: None,
122 task_dispatch_limits: options.task_dispatch_limits,
123 mesh_shader_primitive_indices_clamp: options.mesh_shader_primitive_indices_clamp,
124 })
125 }
126
127 pub fn set_options(&mut self, options: &Options) -> Result<(), Error> {
128 let (major, minor) = options.lang_version;
129 if major != 1 {
130 return Err(Error::UnsupportedVersion(major, minor));
131 }
132 self.physical_layout = PhysicalLayout::new(major, minor);
133 self.capabilities_available = options.capabilities.clone();
134 self.flags = options.flags;
135 self.bounds_check_policies = options.bounds_check_policies;
136 self.zero_initialize_workgroup_memory = options.zero_initialize_workgroup_memory;
137 self.force_loop_bounding = options.force_loop_bounding;
138 self.use_storage_input_output_16 = options.use_storage_input_output_16;
139 self.binding_map = options.binding_map.clone();
140 self.io_f16_polyfills =
141 super::f16_polyfill::F16IoPolyfill::new(options.use_storage_input_output_16);
142 self.task_dispatch_limits = options.task_dispatch_limits;
143 self.mesh_shader_primitive_indices_clamp = options.mesh_shader_primitive_indices_clamp;
144 Ok(())
145 }
146
147 pub const fn lang_version(&self) -> (u8, u8) {
149 self.physical_layout.lang_version()
150 }
151
152 fn reset(&mut self) {
162 use super::reclaimable::Reclaimable;
163 use core::mem::take;
164
165 let mut id_gen = IdGenerator::default();
166 let gl450_ext_inst_id = id_gen.next();
167 let void_type = id_gen.next();
168
169 let fresh = Writer {
172 flags: self.flags,
174 bounds_check_policies: self.bounds_check_policies,
175 zero_initialize_workgroup_memory: self.zero_initialize_workgroup_memory,
176 force_loop_bounding: self.force_loop_bounding,
177 ray_query_initialization_tracking: self.ray_query_initialization_tracking,
178 trace_ray_argument_validation: self.trace_ray_argument_validation,
179 use_storage_input_output_16: self.use_storage_input_output_16,
180 capabilities_available: take(&mut self.capabilities_available),
181 fake_missing_bindings: self.fake_missing_bindings,
182 binding_map: take(&mut self.binding_map),
183 task_dispatch_limits: self.task_dispatch_limits,
184 mesh_shader_primitive_indices_clamp: self.mesh_shader_primitive_indices_clamp,
185 emit_int_div_checks: self.emit_int_div_checks,
186
187 id_gen,
189 void_type,
190 tuple_of_u32s_ty_id: None,
191 gl450_ext_inst_id,
192
193 capabilities_used: take(&mut self.capabilities_used).reclaim(),
195 extensions_used: take(&mut self.extensions_used).reclaim(),
196 physical_layout: self.physical_layout.clone().reclaim(),
197 logical_layout: take(&mut self.logical_layout).reclaim(),
198 debug_strings: take(&mut self.debug_strings).reclaim(),
199 debugs: take(&mut self.debugs).reclaim(),
200 annotations: take(&mut self.annotations).reclaim(),
201 lookup_type: take(&mut self.lookup_type).reclaim(),
202 lookup_function: take(&mut self.lookup_function).reclaim(),
203 lookup_function_type: take(&mut self.lookup_function_type).reclaim(),
204 wrapped_functions: take(&mut self.wrapped_functions).reclaim(),
205 constant_ids: take(&mut self.constant_ids).reclaim(),
206 cached_constants: take(&mut self.cached_constants).reclaim(),
207 global_variables: take(&mut self.global_variables).reclaim(),
208 std140_compat_uniform_types: take(&mut self.std140_compat_uniform_types).reclaim(),
209 saved_cached: take(&mut self.saved_cached).reclaim(),
210 temp_list: take(&mut self.temp_list).reclaim(),
211 ray_query_functions: take(&mut self.ray_query_functions).reclaim(),
212 ray_tracing_functions: take(&mut self.ray_tracing_functions).reclaim(),
213 has_ray_tracing_pipeline: false,
214 io_f16_polyfills: take(&mut self.io_f16_polyfills).reclaim(),
215 debug_printf: None,
216 };
217
218 *self = fresh;
219
220 self.capabilities_used.insert(spirv::Capability::Shader);
221 }
222
223 pub(super) fn require_any(
238 &mut self,
239 what: &'static str,
240 capabilities: &[spirv::Capability],
241 ) -> Result<(), Error> {
242 match *capabilities {
243 [] => Ok(()),
244 [first, ..] => {
245 let selected = match self.capabilities_available {
248 None => first,
249 Some(ref available) => {
250 match capabilities
251 .iter()
252 .find(|cap| available.contains::<spirv::Capability>(cap))
254 {
255 Some(&cap) => cap,
256 None => {
257 return Err(Error::MissingCapabilities(what, capabilities.to_vec()))
258 }
259 }
260 }
261 };
262 self.capabilities_used.insert(selected);
263 Ok(())
264 }
265 }
266 }
267
268 pub(super) fn require_all(
287 &mut self,
288 capabilities: &[spirv::Capability],
289 ) -> Result<(), spirv::Capability> {
290 if let Some(ref available) = self.capabilities_available {
291 for requested in capabilities {
292 if !available.contains(requested) {
293 return Err(*requested);
294 }
295 }
296 }
297
298 for requested in capabilities {
299 self.capabilities_used.insert(*requested);
300 }
301
302 Ok(())
303 }
304
305 pub(super) fn use_extension(&mut self, extension: &'static str) {
307 self.extensions_used.insert(extension);
308 }
309
310 pub(super) fn get_type_id(&mut self, lookup_ty: LookupType) -> Word {
311 match self.lookup_type.entry(lookup_ty) {
312 Entry::Occupied(e) => *e.get(),
313 Entry::Vacant(e) => {
314 let local = match lookup_ty {
315 LookupType::Handle(_handle) => unreachable!("Handles are populated at start"),
316 LookupType::Local(local) => local,
317 };
318
319 let id = self.id_gen.next();
320 e.insert(id);
321 self.write_type_declaration_local(id, local);
322 id
323 }
324 }
325 }
326
327 pub(super) fn get_handle_type_id(&mut self, handle: Handle<crate::Type>) -> Word {
328 self.get_type_id(LookupType::Handle(handle))
329 }
330
331 pub(super) fn get_expression_lookup_type(&mut self, tr: &TypeResolution) -> LookupType {
332 match *tr {
333 TypeResolution::Handle(ty_handle) => LookupType::Handle(ty_handle),
334 TypeResolution::Value(ref inner) => {
335 let inner_local_type = self.localtype_from_inner(inner).unwrap();
336 LookupType::Local(inner_local_type)
337 }
338 }
339 }
340
341 pub(super) fn get_expression_type_id(&mut self, tr: &TypeResolution) -> Word {
342 let lookup_ty = self.get_expression_lookup_type(tr);
343 self.get_type_id(lookup_ty)
344 }
345
346 pub(super) fn get_localtype_id(&mut self, local: LocalType) -> Word {
347 self.get_type_id(LookupType::Local(local))
348 }
349
350 pub(super) fn get_pointer_type_id(&mut self, base: Word, class: spirv::StorageClass) -> Word {
351 self.get_type_id(LookupType::Local(LocalType::Pointer { base, class }))
352 }
353
354 pub(super) fn get_handle_pointer_type_id(
355 &mut self,
356 base: Handle<crate::Type>,
357 class: spirv::StorageClass,
358 ) -> Word {
359 let base_id = self.get_handle_type_id(base);
360 self.get_pointer_type_id(base_id, class)
361 }
362
363 pub(super) fn get_ray_query_pointer_id(&mut self) -> Word {
364 let rq_id = self.get_type_id(LookupType::Local(LocalType::RayQuery));
365 self.get_pointer_type_id(rq_id, spirv::StorageClass::Function)
366 }
367
368 pub(super) fn get_resolution_pointer_id(
373 &mut self,
374 resolution: &TypeResolution,
375 class: spirv::StorageClass,
376 ) -> Word {
377 let resolution_type_id = self.get_expression_type_id(resolution);
378 self.get_pointer_type_id(resolution_type_id, class)
379 }
380
381 pub(super) fn get_numeric_type_id(&mut self, numeric: NumericType) -> Word {
382 self.get_type_id(LocalType::Numeric(numeric).into())
383 }
384
385 pub(super) fn get_u32_type_id(&mut self) -> Word {
386 self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::U32))
387 }
388
389 pub(super) fn get_f32_type_id(&mut self) -> Word {
390 self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::F32))
391 }
392
393 pub(super) fn get_vec2u_type_id(&mut self) -> Word {
394 self.get_numeric_type_id(NumericType::Vector {
395 size: crate::VectorSize::Bi,
396 scalar: crate::Scalar::U32,
397 })
398 }
399
400 pub(super) fn get_vec2f_type_id(&mut self) -> Word {
401 self.get_numeric_type_id(NumericType::Vector {
402 size: crate::VectorSize::Bi,
403 scalar: crate::Scalar::F32,
404 })
405 }
406
407 pub(super) fn get_vec3u_type_id(&mut self) -> Word {
408 self.get_numeric_type_id(NumericType::Vector {
409 size: crate::VectorSize::Tri,
410 scalar: crate::Scalar::U32,
411 })
412 }
413
414 pub(super) fn get_f32_pointer_type_id(&mut self, class: spirv::StorageClass) -> Word {
415 let f32_id = self.get_f32_type_id();
416 self.get_pointer_type_id(f32_id, class)
417 }
418
419 pub(super) fn get_vec2u_pointer_type_id(&mut self, class: spirv::StorageClass) -> Word {
420 let vec2u_id = self.get_numeric_type_id(NumericType::Vector {
421 size: crate::VectorSize::Bi,
422 scalar: crate::Scalar::U32,
423 });
424 self.get_pointer_type_id(vec2u_id, class)
425 }
426
427 pub(super) fn get_bool_type_id(&mut self) -> Word {
428 self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::BOOL))
429 }
430
431 pub(super) fn get_vec2_bool_type_id(&mut self) -> Word {
432 self.get_numeric_type_id(NumericType::Vector {
433 size: crate::VectorSize::Bi,
434 scalar: crate::Scalar::BOOL,
435 })
436 }
437
438 pub(super) fn get_vec3_bool_type_id(&mut self) -> Word {
439 self.get_numeric_type_id(NumericType::Vector {
440 size: crate::VectorSize::Tri,
441 scalar: crate::Scalar::BOOL,
442 })
443 }
444
445 pub(super) fn get_tuple_of_u32s_ty_id(&mut self) -> Word {
450 if let Some(val) = self.tuple_of_u32s_ty_id {
451 val
452 } else {
453 let id = self.id_gen.next();
454 let u32_id = self.get_u32_type_id();
455 let ins = Instruction::type_struct(id, &[u32_id, u32_id]);
456 ins.to_words(&mut self.logical_layout.declarations);
457 self.tuple_of_u32s_ty_id = Some(id);
458 id
459 }
460 }
461
462 pub(super) fn decorate(&mut self, id: Word, decoration: spirv::Decoration, operands: &[Word]) {
463 self.annotations
464 .push(Instruction::decorate(id, decoration, operands));
465 }
466
467 fn localtype_from_inner(&mut self, inner: &crate::TypeInner) -> Option<LocalType> {
475 Some(match *inner {
476 crate::TypeInner::Scalar(_)
477 | crate::TypeInner::Atomic(_)
478 | crate::TypeInner::Vector { .. }
479 | crate::TypeInner::Matrix { .. } => {
480 LocalType::Numeric(NumericType::from_inner(inner).unwrap())
483 }
484 crate::TypeInner::CooperativeMatrix { .. } => {
485 LocalType::Cooperative(CooperativeType::from_inner(inner).unwrap())
486 }
487 crate::TypeInner::Pointer { base, space } => {
488 let base_type_id = self.get_handle_type_id(base);
489 LocalType::Pointer {
490 base: base_type_id,
491 class: map_storage_class(space),
492 }
493 }
494 crate::TypeInner::ValuePointer {
495 size,
496 scalar,
497 space,
498 } => {
499 let base_numeric_type = match size {
500 Some(size) => NumericType::Vector { size, scalar },
501 None => NumericType::Scalar(scalar),
502 };
503 LocalType::Pointer {
504 base: self.get_numeric_type_id(base_numeric_type),
505 class: map_storage_class(space),
506 }
507 }
508 crate::TypeInner::Image {
509 dim,
510 arrayed,
511 class,
512 } => LocalType::Image(LocalImageType::from_inner(dim, arrayed, class)),
513 crate::TypeInner::Sampler { comparison: _ } => LocalType::Sampler,
514 crate::TypeInner::AccelerationStructure { .. } => LocalType::AccelerationStructure,
515 crate::TypeInner::RayQuery { .. } => LocalType::RayQuery,
516 crate::TypeInner::Array { .. }
517 | crate::TypeInner::Struct { .. }
518 | crate::TypeInner::BindingArray { .. } => return None,
519 })
520 }
521
522 pub(super) fn resolve_resource_binding(
528 &self,
529 res_binding: &crate::ResourceBinding,
530 ) -> Result<BindingInfo, Error> {
531 match self.binding_map.get(res_binding) {
532 Some(target) => Ok(*target),
533 None if self.fake_missing_bindings => Ok(BindingInfo {
534 descriptor_set: res_binding.group,
535 binding: res_binding.binding,
536 binding_array_size: None,
537 external_texture: false,
538 }),
539 None => Err(Error::MissingBinding(*res_binding)),
540 }
541 }
542
543 fn write_wrapped_functions(
546 &mut self,
547 ir_function: &crate::Function,
548 info: &FunctionInfo,
549 ir_module: &crate::Module,
550 ) -> Result<(), Error> {
551 log::trace!("Generating wrapped functions for {:?}", ir_function.name);
552
553 for (expr_handle, expr) in ir_function.expressions.iter() {
554 match *expr {
555 crate::Expression::Binary { op, left, right } => {
556 let expr_ty_inner = info[expr_handle].ty.inner_with(&ir_module.types);
557 if let Some(expr_ty) = NumericType::from_inner(expr_ty_inner) {
558 match (op, expr_ty.scalar().kind) {
559 (
572 crate::BinaryOperator::Divide | crate::BinaryOperator::Modulo,
573 crate::ScalarKind::Sint | crate::ScalarKind::Uint,
574 ) if self.emit_int_div_checks
575 || matches!(
576 (op, expr_ty.scalar().kind),
577 (crate::BinaryOperator::Modulo, crate::ScalarKind::Sint)
578 ) =>
579 {
580 self.write_wrapped_binary_op(
581 op,
582 expr_ty,
583 &info[left].ty,
584 &info[right].ty,
585 )?;
586 }
587 _ => {}
588 }
589 }
590 }
591 crate::Expression::Load { pointer } => {
592 if let crate::TypeInner::Pointer {
593 base: pointer_type,
594 space: crate::AddressSpace::Uniform,
595 } = *info[pointer].ty.inner_with(&ir_module.types)
596 {
597 if self.std140_compat_uniform_types.contains_key(&pointer_type) {
598 self.write_wrapped_convert_from_std140_compat_type(
601 ir_module,
602 pointer_type,
603 )?;
604 }
605 }
606 }
607 crate::Expression::Access { base, .. } => {
608 if let crate::TypeInner::Pointer {
609 base: base_type,
610 space: crate::AddressSpace::Uniform,
611 } = *info[base].ty.inner_with(&ir_module.types)
612 {
613 if let crate::TypeInner::Matrix {
616 rows: crate::VectorSize::Bi,
617 ..
618 } = ir_module.types[base_type].inner
619 {
620 self.write_wrapped_matcx2_get_column(ir_module, base_type)?;
621 if !is_uniform_matcx2_struct_member_access(
625 ir_function,
626 info,
627 ir_module,
628 base,
629 ) {
630 self.write_wrapped_convert_from_std140_compat_type(
631 ir_module, base_type,
632 )?;
633 }
634 }
635 }
636 }
637 _ => {}
638 }
639 }
640
641 Ok(())
642 }
643
644 fn write_wrapped_binary_op(
664 &mut self,
665 op: crate::BinaryOperator,
666 return_type: NumericType,
667 left_type: &TypeResolution,
668 right_type: &TypeResolution,
669 ) -> Result<(), Error> {
670 let return_type_id = self.get_localtype_id(LocalType::Numeric(return_type));
671 let left_type_id = self.get_expression_type_id(left_type);
672 let right_type_id = self.get_expression_type_id(right_type);
673
674 let wrapped = WrappedFunction::BinaryOp {
676 op,
677 left_type_id,
678 right_type_id,
679 };
680 let function_id = match self.wrapped_functions.entry(wrapped) {
681 Entry::Occupied(_) => return Ok(()),
682 Entry::Vacant(e) => *e.insert(self.id_gen.next()),
683 };
684
685 let scalar = return_type.scalar();
686
687 if self.flags.contains(WriterFlags::DEBUG) {
688 let function_name = match op {
689 crate::BinaryOperator::Divide => "naga_div",
690 crate::BinaryOperator::Modulo => "naga_mod",
691 _ => unreachable!(),
692 };
693 self.debugs
694 .push(Instruction::name(function_id, function_name));
695 }
696 let mut function = Function::default();
697
698 let function_type_id = self.get_function_type(LookupFunctionType {
699 parameter_type_ids: vec![left_type_id, right_type_id],
700 return_type_id,
701 });
702 function.signature = Some(Instruction::function(
703 return_type_id,
704 function_id,
705 spirv::FunctionControl::empty(),
706 function_type_id,
707 ));
708
709 let lhs_id = self.id_gen.next();
710 let rhs_id = self.id_gen.next();
711 if self.flags.contains(WriterFlags::DEBUG) {
712 self.debugs.push(Instruction::name(lhs_id, "lhs"));
713 self.debugs.push(Instruction::name(rhs_id, "rhs"));
714 }
715 let left_par = Instruction::function_parameter(left_type_id, lhs_id);
716 let right_par = Instruction::function_parameter(right_type_id, rhs_id);
717 for instruction in [left_par, right_par] {
718 function.parameters.push(FunctionArgument {
719 instruction,
720 handle_id: 0,
721 });
722 }
723
724 let label_id = self.id_gen.next();
725 let mut block = Block::new(label_id);
726
727 let bool_type = return_type.with_scalar(crate::Scalar::BOOL);
728 let bool_type_id = self.get_numeric_type_id(bool_type);
729
730 let maybe_splat_const = |writer: &mut Self, const_id| match return_type {
731 NumericType::Scalar(_) => const_id,
732 NumericType::Vector { size, .. } => {
733 let constituent_ids = [const_id; crate::VectorSize::MAX];
734 writer.get_constant_composite(
735 LookupType::Local(LocalType::Numeric(return_type)),
736 &constituent_ids[..size as usize],
737 )
738 }
739 NumericType::Matrix { .. } => unreachable!(),
740 };
741
742 let const_zero_id = self.get_constant_scalar_with(0, scalar)?;
743 let composite_zero_id = maybe_splat_const(self, const_zero_id);
744 let rhs_eq_zero_id = self.id_gen.next();
745 block.body.push(Instruction::binary(
746 spirv::Op::IEqual,
747 bool_type_id,
748 rhs_eq_zero_id,
749 rhs_id,
750 composite_zero_id,
751 ));
752 let divisor_selector_id = match scalar.kind {
753 crate::ScalarKind::Sint => {
754 let (const_min_id, const_neg_one_id) = match scalar.width {
755 2 => Ok((
756 self.get_constant_scalar(crate::Literal::I16(i16::MIN)),
757 self.get_constant_scalar(crate::Literal::I16(-1i16)),
758 )),
759 4 => Ok((
760 self.get_constant_scalar(crate::Literal::I32(i32::MIN)),
761 self.get_constant_scalar(crate::Literal::I32(-1i32)),
762 )),
763 8 => Ok((
764 self.get_constant_scalar(crate::Literal::I64(i64::MIN)),
765 self.get_constant_scalar(crate::Literal::I64(-1i64)),
766 )),
767 _ => Err(Error::Validation("Unexpected scalar width")),
768 }?;
769 let composite_min_id = maybe_splat_const(self, const_min_id);
770 let composite_neg_one_id = maybe_splat_const(self, const_neg_one_id);
771
772 let lhs_eq_int_min_id = self.id_gen.next();
773 block.body.push(Instruction::binary(
774 spirv::Op::IEqual,
775 bool_type_id,
776 lhs_eq_int_min_id,
777 lhs_id,
778 composite_min_id,
779 ));
780 let rhs_eq_neg_one_id = self.id_gen.next();
781 block.body.push(Instruction::binary(
782 spirv::Op::IEqual,
783 bool_type_id,
784 rhs_eq_neg_one_id,
785 rhs_id,
786 composite_neg_one_id,
787 ));
788 let lhs_eq_int_min_and_rhs_eq_neg_one_id = self.id_gen.next();
789 block.body.push(Instruction::binary(
790 spirv::Op::LogicalAnd,
791 bool_type_id,
792 lhs_eq_int_min_and_rhs_eq_neg_one_id,
793 lhs_eq_int_min_id,
794 rhs_eq_neg_one_id,
795 ));
796 let rhs_eq_zero_or_lhs_eq_int_min_and_rhs_eq_neg_one_id = self.id_gen.next();
797 block.body.push(Instruction::binary(
798 spirv::Op::LogicalOr,
799 bool_type_id,
800 rhs_eq_zero_or_lhs_eq_int_min_and_rhs_eq_neg_one_id,
801 rhs_eq_zero_id,
802 lhs_eq_int_min_and_rhs_eq_neg_one_id,
803 ));
804 rhs_eq_zero_or_lhs_eq_int_min_and_rhs_eq_neg_one_id
805 }
806 crate::ScalarKind::Uint => rhs_eq_zero_id,
807 _ => unreachable!(),
808 };
809
810 let const_one_id = self.get_constant_scalar_with(1, scalar)?;
811 let composite_one_id = maybe_splat_const(self, const_one_id);
812 let divisor_id = self.id_gen.next();
813 block.body.push(Instruction::select(
814 right_type_id,
815 divisor_id,
816 divisor_selector_id,
817 composite_one_id,
818 rhs_id,
819 ));
820 let return_id = if matches!(op, crate::BinaryOperator::Modulo)
821 && matches!(scalar.kind, crate::ScalarKind::Sint)
822 {
823 let quotient_id = self.id_gen.next();
829 block.body.push(Instruction::binary(
830 spirv::Op::SDiv,
831 return_type_id,
832 quotient_id,
833 lhs_id,
834 divisor_id,
835 ));
836 let product_id = self.id_gen.next();
837 block.body.push(Instruction::binary(
838 spirv::Op::IMul,
839 return_type_id,
840 product_id,
841 quotient_id,
842 divisor_id,
843 ));
844 let remainder_id = self.id_gen.next();
845 block.body.push(Instruction::binary(
846 spirv::Op::ISub,
847 return_type_id,
848 remainder_id,
849 lhs_id,
850 product_id,
851 ));
852 remainder_id
853 } else {
854 let spv_op = match (op, scalar.kind) {
855 (crate::BinaryOperator::Divide, crate::ScalarKind::Sint) => spirv::Op::SDiv,
856 (crate::BinaryOperator::Divide, crate::ScalarKind::Uint) => spirv::Op::UDiv,
857 (crate::BinaryOperator::Modulo, crate::ScalarKind::Sint) => spirv::Op::SRem,
858 (crate::BinaryOperator::Modulo, crate::ScalarKind::Uint) => spirv::Op::UMod,
859 _ => unreachable!(),
860 };
861 let return_id = self.id_gen.next();
862 block.body.push(Instruction::binary(
863 spv_op,
864 return_type_id,
865 return_id,
866 lhs_id,
867 divisor_id,
868 ));
869 return_id
870 };
871
872 function.consume(block, Instruction::return_value(return_id));
873 function.to_words(&mut self.logical_layout.function_definitions);
874 Ok(())
875 }
876
877 fn write_wrapped_convert_from_std140_compat_type(
882 &mut self,
883 ir_module: &crate::Module,
884 r#type: Handle<crate::Type>,
885 ) -> Result<(), Error> {
886 if !self.std140_compat_uniform_types.contains_key(&r#type) {
887 return Ok(());
888 }
889 let wrapped = WrappedFunction::ConvertFromStd140CompatType { r#type };
891 let function_id = match self.wrapped_functions.entry(wrapped) {
892 Entry::Occupied(_) => return Ok(()),
893 Entry::Vacant(e) => *e.insert(self.id_gen.next()),
894 };
895 if self.flags.contains(WriterFlags::DEBUG) {
896 self.debugs.push(Instruction::name(
897 function_id,
898 &format!("{:?}_from_std140", r#type.for_debug(&ir_module.types)),
899 ));
900 }
901 let param_type_id = self.std140_compat_uniform_types[&r#type].type_id;
902 let return_type_id = self.get_handle_type_id(r#type);
903
904 let mut function = Function::default();
905 let function_type_id = self.get_function_type(LookupFunctionType {
906 parameter_type_ids: vec![param_type_id],
907 return_type_id,
908 });
909 function.signature = Some(Instruction::function(
910 return_type_id,
911 function_id,
912 spirv::FunctionControl::empty(),
913 function_type_id,
914 ));
915 let param_id = self.id_gen.next();
916 function.parameters.push(FunctionArgument {
917 instruction: Instruction::function_parameter(param_type_id, param_id),
918 handle_id: 0,
919 });
920
921 let label_id = self.id_gen.next();
922 let mut block = Block::new(label_id);
923
924 let result_id = match ir_module.types[r#type].inner {
925 crate::TypeInner::Matrix {
929 columns,
930 rows: rows @ crate::VectorSize::Bi,
931 scalar,
932 } => {
933 let column_type_id =
934 self.get_numeric_type_id(NumericType::Vector { size: rows, scalar });
935
936 let mut column_ids: ArrayVec<Word, 4> = ArrayVec::new();
937 for column in 0..columns as u32 {
938 let column_id = self.id_gen.next();
939 block.body.push(Instruction::composite_extract(
940 column_type_id,
941 column_id,
942 param_id,
943 &[column],
944 ));
945 column_ids.push(column_id);
946 }
947 let result_id = self.id_gen.next();
948 block.body.push(Instruction::composite_construct(
949 return_type_id,
950 result_id,
951 &column_ids,
952 ));
953 result_id
954 }
955 crate::TypeInner::Array { base, size, .. } => {
959 self.write_wrapped_convert_from_std140_compat_type(ir_module, base)?;
962
963 let element_type_id = self.get_handle_type_id(base);
964 let std140_info = self.std140_compat_uniform_types.get(&base);
965 let mut element_ids = Vec::new();
966 let size = match size.resolve(ir_module.to_ctx())? {
967 crate::proc::IndexableLength::Known(size) => size,
968 crate::proc::IndexableLength::Dynamic => {
969 return Err(Error::Validation(
970 "Uniform buffers cannot contain dynamic arrays",
971 ))
972 }
973 };
974 for i in 0..size {
975 let std140_element_id = self.id_gen.next();
976 let std140_element_type_id =
977 std140_info.map_or(element_type_id, |info| info.type_id);
978 block.body.push(Instruction::composite_extract(
979 std140_element_type_id,
980 std140_element_id,
981 param_id,
982 &[i],
983 ));
984
985 let final_element_id = if std140_info.is_some() {
987 let conversion_fn_id = self.wrapped_functions
988 [&WrappedFunction::ConvertFromStd140CompatType { r#type: base }];
989 let id = self.id_gen.next();
990 block.body.push(Instruction::function_call(
991 element_type_id,
992 id,
993 conversion_fn_id,
994 &[std140_element_id],
995 ));
996 id
997 } else {
998 std140_element_type_id
999 };
1000 element_ids.push(final_element_id);
1001 }
1002 let result_id = self.id_gen.next();
1003 block.body.push(Instruction::composite_construct(
1004 return_type_id,
1005 result_id,
1006 &element_ids,
1007 ));
1008 result_id
1009 }
1010 crate::TypeInner::Struct { ref members, .. } => {
1017 let mut member_ids = Vec::new();
1018 let mut next_index = 0;
1019 for member in members {
1020 let member_id = self.id_gen.next();
1021 let member_type_id = self.get_handle_type_id(member.ty);
1022 match ir_module.types[member.ty].inner {
1023 crate::TypeInner::Matrix {
1024 columns,
1025 rows: rows @ crate::VectorSize::Bi,
1026 scalar,
1027 } => {
1028 let mut column_ids: ArrayVec<Word, 4> = ArrayVec::new();
1029 let column_type_id = self
1030 .get_numeric_type_id(NumericType::Vector { size: rows, scalar });
1031 for _ in 0..columns as u32 {
1032 let column_id = self.id_gen.next();
1033 block.body.push(Instruction::composite_extract(
1034 column_type_id,
1035 column_id,
1036 param_id,
1037 &[next_index],
1038 ));
1039 column_ids.push(column_id);
1040 next_index += 1;
1041 }
1042 block.body.push(Instruction::composite_construct(
1043 member_type_id,
1044 member_id,
1045 &column_ids,
1046 ));
1047 }
1048 _ => {
1049 self.write_wrapped_convert_from_std140_compat_type(
1052 ir_module, member.ty,
1053 )?;
1054 match self.std140_compat_uniform_types.get(&member.ty) {
1055 Some(std140_type_info) => {
1056 let std140_member_id = self.id_gen.next();
1057 block.body.push(Instruction::composite_extract(
1058 std140_type_info.type_id,
1059 std140_member_id,
1060 param_id,
1061 &[next_index],
1062 ));
1063 let function_id = self.wrapped_functions
1064 [&WrappedFunction::ConvertFromStd140CompatType {
1065 r#type: member.ty,
1066 }];
1067 block.body.push(Instruction::function_call(
1068 member_type_id,
1069 member_id,
1070 function_id,
1071 &[std140_member_id],
1072 ));
1073 next_index += 1;
1074 }
1075 None => {
1076 block.body.push(Instruction::composite_extract(
1077 member_type_id,
1078 member_id,
1079 param_id,
1080 &[next_index],
1081 ));
1082 next_index += 1;
1083 }
1084 }
1085 }
1086 }
1087 member_ids.push(member_id);
1088 }
1089 let result_id = self.id_gen.next();
1090 block.body.push(Instruction::composite_construct(
1091 return_type_id,
1092 result_id,
1093 &member_ids,
1094 ));
1095 result_id
1096 }
1097 _ => unreachable!(),
1098 };
1099
1100 function.consume(block, Instruction::return_value(result_id));
1101 function.to_words(&mut self.logical_layout.function_definitions);
1102 Ok(())
1103 }
1104
1105 fn write_wrapped_matcx2_get_column(
1118 &mut self,
1119 ir_module: &crate::Module,
1120 r#type: Handle<crate::Type>,
1121 ) -> Result<(), Error> {
1122 let wrapped = WrappedFunction::MatCx2GetColumn { r#type };
1123 let function_id = match self.wrapped_functions.entry(wrapped) {
1124 Entry::Occupied(_) => return Ok(()),
1125 Entry::Vacant(e) => *e.insert(self.id_gen.next()),
1126 };
1127 if self.flags.contains(WriterFlags::DEBUG) {
1128 self.debugs.push(Instruction::name(
1129 function_id,
1130 &format!("{:?}_get_column", r#type.for_debug(&ir_module.types)),
1131 ));
1132 }
1133
1134 let crate::TypeInner::Matrix {
1135 columns,
1136 rows: rows @ crate::VectorSize::Bi,
1137 scalar,
1138 } = ir_module.types[r#type].inner
1139 else {
1140 unreachable!();
1141 };
1142
1143 let mut function = Function::default();
1144 let matrix_type_id = self.get_handle_type_id(r#type);
1145 let column_index_type_id = self.get_u32_type_id();
1146 let column_type_id = self.get_numeric_type_id(NumericType::Vector { size: rows, scalar });
1147 let matrix_param_id = self.id_gen.next();
1148 let column_index_param_id = self.id_gen.next();
1149 function.parameters.push(FunctionArgument {
1150 instruction: Instruction::function_parameter(matrix_type_id, matrix_param_id),
1151 handle_id: 0,
1152 });
1153 function.parameters.push(FunctionArgument {
1154 instruction: Instruction::function_parameter(
1155 column_index_type_id,
1156 column_index_param_id,
1157 ),
1158 handle_id: 0,
1159 });
1160 let function_type_id = self.get_function_type(LookupFunctionType {
1161 parameter_type_ids: vec![matrix_type_id, column_index_type_id],
1162 return_type_id: column_type_id,
1163 });
1164 function.signature = Some(Instruction::function(
1165 column_type_id,
1166 function_id,
1167 spirv::FunctionControl::empty(),
1168 function_type_id,
1169 ));
1170
1171 let label_id = self.id_gen.next();
1172 let mut block = Block::new(label_id);
1173
1174 let merge_id = self.id_gen.next();
1178 block.body.push(Instruction::selection_merge(
1179 merge_id,
1180 spirv::SelectionControl::NONE,
1181 ));
1182 let cases = (0..columns as u32)
1183 .map(|i| super::instructions::Case {
1184 value: i,
1185 label_id: self.id_gen.next(),
1186 })
1187 .collect::<ArrayVec<_, 4>>();
1188
1189 let default_id = match self.bounds_check_policies.index {
1192 crate::proc::BoundsCheckPolicy::Restrict => cases.last().unwrap().label_id,
1194 crate::proc::BoundsCheckPolicy::ReadZeroSkipWrite => merge_id,
1197 crate::proc::BoundsCheckPolicy::Unchecked => self.id_gen.next(),
1200 };
1201 function.consume(
1202 block,
1203 Instruction::switch(column_index_param_id, default_id, &cases),
1204 );
1205
1206 let mut var_parent_pairs = cases
1210 .into_iter()
1211 .map(|case| {
1212 let mut block = Block::new(case.label_id);
1213 let column_id = self.id_gen.next();
1214 block.body.push(Instruction::composite_extract(
1215 column_type_id,
1216 column_id,
1217 matrix_param_id,
1218 &[case.value],
1219 ));
1220 function.consume(block, Instruction::branch(merge_id));
1221 (column_id, case.label_id)
1222 })
1223 .collect::<ArrayVec<_, 5>>();
1225
1226 match self.bounds_check_policies.index {
1229 crate::proc::BoundsCheckPolicy::Restrict => {}
1232 crate::proc::BoundsCheckPolicy::ReadZeroSkipWrite => {
1236 var_parent_pairs.push((self.get_constant_null(column_type_id), label_id));
1237 }
1238 crate::proc::BoundsCheckPolicy::Unchecked => {
1241 function.consume(
1242 Block::new(default_id),
1243 Instruction::new(spirv::Op::Unreachable),
1244 );
1245 }
1246 }
1247
1248 let mut block = Block::new(merge_id);
1249 let result_id = self.id_gen.next();
1250 block.body.push(Instruction::phi(
1251 column_type_id,
1252 result_id,
1253 &var_parent_pairs,
1254 ));
1255
1256 function.consume(block, Instruction::return_value(result_id));
1257 function.to_words(&mut self.logical_layout.function_definitions);
1258 Ok(())
1259 }
1260
1261 fn write_function(
1262 &mut self,
1263 ir_function: &crate::Function,
1264 info: &FunctionInfo,
1265 ir_module: &crate::Module,
1266 mut interface: Option<FunctionInterface>,
1267 debug_info: &Option<DebugInfoInner>,
1268 ) -> Result<Word, Error> {
1269 self.write_wrapped_functions(ir_function, info, ir_module)?;
1270
1271 log::trace!("Generating code for {:?}", ir_function.name);
1272 let mut function = Function::default();
1273
1274 let prelude_id = self.id_gen.next();
1275 let mut prelude = Block::new(prelude_id);
1276 let mut ep_context = EntryPointContext {
1277 argument_ids: Vec::new(),
1278 results: Vec::new(),
1279 task_payload_variable_id: if let Some(ref i) = interface {
1280 i.task_payload.map(|a| self.global_variables[a].var_id)
1281 } else {
1282 None
1283 },
1284 mesh_state: None,
1285 };
1286
1287 let mut parameter_type_ids = Vec::with_capacity(ir_function.arguments.len());
1288
1289 let mut local_invocation_index_var_id = None;
1290 let mut local_invocation_index_id = None;
1291
1292 for argument in ir_function.arguments.iter() {
1293 let class = spirv::StorageClass::Input;
1294 let handle_ty = ir_module.types[argument.ty].inner.is_handle();
1295 let argument_type_id = if handle_ty {
1296 self.get_handle_pointer_type_id(argument.ty, spirv::StorageClass::UniformConstant)
1297 } else {
1298 self.get_handle_type_id(argument.ty)
1299 };
1300
1301 if let Some(ref mut iface) = interface {
1302 let id = if let Some(ref binding) = argument.binding {
1303 let name = argument.name.as_deref();
1304
1305 let varying_id = self.write_varying(
1306 ir_module,
1307 iface.stage,
1308 class,
1309 name,
1310 argument.ty,
1311 binding,
1312 )?;
1313 iface.varying_ids.push(varying_id);
1314 let id = self.load_io_with_f16_polyfill(
1315 &mut prelude.body,
1316 varying_id,
1317 argument_type_id,
1318 );
1319 if binding == &crate::Binding::BuiltIn(crate::BuiltIn::LocalInvocationIndex) {
1320 local_invocation_index_id = Some(id);
1321 local_invocation_index_var_id = Some(varying_id);
1322 }
1323
1324 id
1325 } else if let crate::TypeInner::Struct { ref members, .. } =
1326 ir_module.types[argument.ty].inner
1327 {
1328 let struct_id = self.id_gen.next();
1329 let mut constituent_ids = Vec::with_capacity(members.len());
1330 for member in members {
1331 let type_id = self.get_handle_type_id(member.ty);
1332 let name = member.name.as_deref();
1333 let binding = member.binding.as_ref().unwrap();
1334 let varying_id = self.write_varying(
1335 ir_module,
1336 iface.stage,
1337 class,
1338 name,
1339 member.ty,
1340 binding,
1341 )?;
1342 iface.varying_ids.push(varying_id);
1343 let id =
1344 self.load_io_with_f16_polyfill(&mut prelude.body, varying_id, type_id);
1345 constituent_ids.push(id);
1346 if binding == &crate::Binding::BuiltIn(crate::BuiltIn::LocalInvocationIndex)
1347 {
1348 local_invocation_index_id = Some(id);
1349 local_invocation_index_var_id = Some(varying_id);
1350 }
1351 }
1352 prelude.body.push(Instruction::composite_construct(
1353 argument_type_id,
1354 struct_id,
1355 &constituent_ids,
1356 ));
1357 struct_id
1358 } else {
1359 unreachable!("Missing argument binding on an entry point");
1360 };
1361 ep_context.argument_ids.push(id);
1362 } else {
1363 let argument_id = self.id_gen.next();
1364 let instruction = Instruction::function_parameter(argument_type_id, argument_id);
1365 if self.flags.contains(WriterFlags::DEBUG) {
1366 if let Some(ref name) = argument.name {
1367 self.debugs.push(Instruction::name(argument_id, name));
1368 }
1369 }
1370 function.parameters.push(FunctionArgument {
1371 instruction,
1372 handle_id: if handle_ty {
1373 let id = self.id_gen.next();
1374 prelude.body.push(Instruction::load(
1375 self.get_handle_type_id(argument.ty),
1376 id,
1377 argument_id,
1378 None,
1379 ));
1380 id
1381 } else {
1382 0
1383 },
1384 });
1385 parameter_type_ids.push(argument_type_id);
1386 };
1387 }
1388
1389 let return_type_id = match ir_function.result {
1390 Some(ref result) => {
1391 if let Some(ref mut iface) = interface {
1392 let mut has_point_size = false;
1393 let class = spirv::StorageClass::Output;
1394 if let Some(ref binding) = result.binding {
1395 has_point_size |=
1396 *binding == crate::Binding::BuiltIn(crate::BuiltIn::PointSize);
1397 let type_id = self.get_handle_type_id(result.ty);
1398 let varying_id =
1399 if *binding == crate::Binding::BuiltIn(crate::BuiltIn::MeshTaskSize) {
1400 0
1401 } else {
1402 let varying_id = self.write_varying(
1403 ir_module,
1404 iface.stage,
1405 class,
1406 None,
1407 result.ty,
1408 binding,
1409 )?;
1410 iface.varying_ids.push(varying_id);
1411 varying_id
1412 };
1413 ep_context.results.push(ResultMember {
1414 id: varying_id,
1415 type_id,
1416 built_in: binding.to_built_in(),
1417 });
1418 } else if let crate::TypeInner::Struct { ref members, .. } =
1419 ir_module.types[result.ty].inner
1420 {
1421 for member in members {
1422 let type_id = self.get_handle_type_id(member.ty);
1423 let name = member.name.as_deref();
1424 let binding = member.binding.as_ref().unwrap();
1425 has_point_size |=
1426 *binding == crate::Binding::BuiltIn(crate::BuiltIn::PointSize);
1427 let varying_id = if *binding
1431 == crate::Binding::BuiltIn(crate::BuiltIn::MeshTaskSize)
1432 {
1433 0
1434 } else {
1435 let varying_id = self.write_varying(
1436 ir_module,
1437 iface.stage,
1438 class,
1439 name,
1440 member.ty,
1441 binding,
1442 )?;
1443 iface.varying_ids.push(varying_id);
1444 varying_id
1445 };
1446 ep_context.results.push(ResultMember {
1447 id: varying_id,
1448 type_id,
1449 built_in: binding.to_built_in(),
1450 });
1451 }
1452 } else {
1453 unreachable!("Missing result binding on an entry point");
1454 }
1455
1456 if self.flags.contains(WriterFlags::FORCE_POINT_SIZE)
1457 && iface.stage == crate::ShaderStage::Vertex
1458 && !has_point_size
1459 {
1460 let varying_id = self.id_gen.next();
1462 let pointer_type_id = self.get_f32_pointer_type_id(class);
1463 Instruction::variable(pointer_type_id, varying_id, class, None)
1464 .to_words(&mut self.logical_layout.declarations);
1465 self.decorate(
1466 varying_id,
1467 spirv::Decoration::BuiltIn,
1468 &[spirv::BuiltIn::PointSize as u32],
1469 );
1470 iface.varying_ids.push(varying_id);
1471
1472 let default_value_id = self.get_constant_scalar(crate::Literal::F32(1.0));
1473 prelude
1474 .body
1475 .push(Instruction::store(varying_id, default_value_id, None));
1476 }
1477 if iface.stage == crate::ShaderStage::Task {
1478 self.get_vec3u_type_id()
1479 } else {
1480 self.void_type
1481 }
1482 } else {
1483 self.get_handle_type_id(result.ty)
1484 }
1485 }
1486 None => self.void_type,
1487 };
1488
1489 if let Some(ref mut iface) = interface {
1490 if let Some(task_payload) = iface.task_payload {
1491 iface
1492 .varying_ids
1493 .push(self.global_variables[task_payload].var_id);
1494 }
1495 if iface.mesh_info.is_some() && local_invocation_index_var_id.is_none() {
1498 let u32_type_id = self.get_u32_type_id();
1499 let pointer_type_id =
1500 self.get_pointer_type_id(u32_type_id, spirv::StorageClass::Input);
1501 let var_id = self.id_gen.next();
1502 Instruction::variable(pointer_type_id, var_id, spirv::StorageClass::Input, None)
1503 .to_words(&mut self.logical_layout.declarations);
1504 self.decorate(
1505 var_id,
1506 spirv::Decoration::BuiltIn,
1507 &[spirv::BuiltIn::LocalInvocationIndex as u32],
1508 );
1509 iface.varying_ids.push(var_id);
1510
1511 let id = self.id_gen.next();
1512 prelude
1513 .body
1514 .push(Instruction::load(u32_type_id, id, var_id, None));
1515 local_invocation_index_var_id = Some(var_id);
1516 local_invocation_index_id = Some(id);
1517 }
1518 self.write_entry_point_mesh_shader_info(
1519 iface,
1520 local_invocation_index_var_id,
1521 ir_module,
1522 &mut ep_context,
1523 )?;
1524 }
1525
1526 let lookup_function_type = LookupFunctionType {
1527 parameter_type_ids,
1528 return_type_id,
1529 };
1530
1531 let function_id = self.id_gen.next();
1532 if self.flags.contains(WriterFlags::DEBUG) {
1533 if let Some(ref name) = ir_function.name {
1534 self.debugs.push(Instruction::name(function_id, name));
1535 }
1536 }
1537
1538 let function_type = self.get_function_type(lookup_function_type);
1539 function.signature = Some(Instruction::function(
1540 return_type_id,
1541 function_id,
1542 spirv::FunctionControl::empty(),
1543 function_type,
1544 ));
1545
1546 if interface.is_some() {
1547 function.entry_point_context = Some(ep_context);
1548 }
1549
1550 for gv in self.global_variables.iter_mut() {
1552 gv.reset_for_function();
1553 }
1554 for (handle, var) in ir_module.global_variables.iter() {
1555 if info[handle].is_empty() {
1556 continue;
1557 }
1558
1559 let mut gv = self.global_variables[handle].clone();
1560 if let Some(ref mut iface) = interface {
1561 if self.physical_layout.version >= 0x10400 && iface.task_payload != Some(handle) {
1563 iface.varying_ids.push(gv.var_id);
1564 }
1565 }
1566
1567 match ir_module.types[var.ty].inner {
1568 crate::TypeInner::BindingArray { .. } => {
1570 gv.access_id = gv.var_id;
1571 }
1572 _ => {
1573 if var.space == crate::AddressSpace::Handle {
1575 let var_type_id = self.get_handle_type_id(var.ty);
1576 let id = self.id_gen.next();
1577 prelude
1578 .body
1579 .push(Instruction::load(var_type_id, id, gv.var_id, None));
1580 gv.access_id = gv.var_id;
1581 gv.handle_id = id;
1582 } else if global_needs_wrapper(ir_module, var) {
1583 let class = map_storage_class(var.space);
1584 let pointer_type_id = match self.std140_compat_uniform_types.get(&var.ty) {
1585 Some(std140_type_info) if var.space == crate::AddressSpace::Uniform => {
1586 self.get_pointer_type_id(std140_type_info.type_id, class)
1587 }
1588 _ => self.get_handle_pointer_type_id(var.ty, class),
1589 };
1590 let index_id = self.get_index_constant(0);
1591 let id = self.id_gen.next();
1592 prelude.body.push(Instruction::access_chain(
1593 pointer_type_id,
1594 id,
1595 gv.var_id,
1596 &[index_id],
1597 ));
1598 gv.access_id = id;
1599 } else {
1600 gv.access_id = gv.var_id;
1602 };
1603 }
1604 }
1605
1606 self.global_variables[handle] = gv;
1608 }
1609
1610 let mut context = BlockContext {
1613 ir_module,
1614 ir_function,
1615 fun_info: info,
1616 function: &mut function,
1617 cached: core::mem::take(&mut self.saved_cached),
1619
1620 temp_list: core::mem::take(&mut self.temp_list),
1622 force_loop_bounding: self.force_loop_bounding,
1623 writer: self,
1624 expression_constness: super::ExpressionConstnessTracker::from_arena(
1625 &ir_function.expressions,
1626 ),
1627 ray_query_tracker_expr: crate::FastHashMap::default(),
1628 };
1629
1630 context.cached.reset(ir_function.expressions.len());
1632 for (handle, expr) in ir_function.expressions.iter() {
1633 if (expr.needs_pre_emit() && !matches!(*expr, crate::Expression::LocalVariable(_)))
1634 || context.expression_constness.is_const(handle)
1635 {
1636 context.cache_expression_value(handle, &mut prelude)?;
1637 }
1638 }
1639
1640 for (handle, variable) in ir_function.local_variables.iter() {
1641 let id = context.gen_id();
1642
1643 if context.writer.flags.contains(WriterFlags::DEBUG) {
1644 if let Some(ref name) = variable.name {
1645 context.writer.debugs.push(Instruction::name(id, name));
1646 }
1647 }
1648
1649 let init_word = variable.init.map(|constant| context.cached[constant]);
1650 let pointer_type_id = context
1651 .writer
1652 .get_handle_pointer_type_id(variable.ty, spirv::StorageClass::Function);
1653 let instruction = Instruction::variable(
1654 pointer_type_id,
1655 id,
1656 spirv::StorageClass::Function,
1657 init_word.or_else(|| match ir_module.types[variable.ty].inner {
1658 crate::TypeInner::RayQuery { .. } => None,
1659 _ => {
1660 let type_id = context.get_handle_type_id(variable.ty);
1661 Some(context.writer.write_constant_null(type_id))
1662 }
1663 }),
1664 );
1665
1666 context
1667 .function
1668 .variables
1669 .insert(handle, LocalVariable { id, instruction });
1670
1671 if let crate::TypeInner::RayQuery { .. } = ir_module.types[variable.ty].inner {
1672 let u32_type_id = context.writer.get_u32_type_id();
1676 let ptr_u32_type_id = context
1677 .writer
1678 .get_pointer_type_id(u32_type_id, spirv::StorageClass::Function);
1679 let tracker_id = context.gen_id();
1680 let tracker_init_id = context.writer.get_constant_scalar(crate::Literal::U32(
1681 crate::back::RayQueryPoint::empty().bits(),
1682 ));
1683 let tracker_instruction = Instruction::variable(
1684 ptr_u32_type_id,
1685 tracker_id,
1686 spirv::StorageClass::Function,
1687 Some(tracker_init_id),
1688 );
1689
1690 context
1691 .function
1692 .ray_query_initialization_tracker_variables
1693 .insert(
1694 handle,
1695 LocalVariable {
1696 id: tracker_id,
1697 instruction: tracker_instruction,
1698 },
1699 );
1700 let f32_type_id = context.writer.get_f32_type_id();
1701 let ptr_f32_type_id = context
1702 .writer
1703 .get_pointer_type_id(f32_type_id, spirv::StorageClass::Function);
1704 let t_max_tracker_id = context.gen_id();
1705 let t_max_tracker_init_id =
1706 context.writer.get_constant_scalar(crate::Literal::F32(0.0));
1707 let t_max_tracker_instruction = Instruction::variable(
1708 ptr_f32_type_id,
1709 t_max_tracker_id,
1710 spirv::StorageClass::Function,
1711 Some(t_max_tracker_init_id),
1712 );
1713
1714 context.function.ray_query_t_max_tracker_variables.insert(
1715 handle,
1716 LocalVariable {
1717 id: t_max_tracker_id,
1718 instruction: t_max_tracker_instruction,
1719 },
1720 );
1721 }
1722 }
1723
1724 for (handle, expr) in ir_function.expressions.iter() {
1725 match *expr {
1726 crate::Expression::LocalVariable(_) => {
1727 context.cache_expression_value(handle, &mut prelude)?;
1730 }
1731 crate::Expression::Access { base, .. }
1732 | crate::Expression::AccessIndex { base, .. } => {
1733 *context.function.access_uses.entry(base).or_insert(0) += 1;
1736 }
1737 _ => {}
1738 }
1739 }
1740
1741 let next_id = context.gen_id();
1742
1743 context
1744 .function
1745 .consume(prelude, Instruction::branch(next_id));
1746
1747 let workgroup_vars_init_exit_block_id =
1748 match (context.writer.zero_initialize_workgroup_memory, interface) {
1749 (
1750 super::ZeroInitializeWorkgroupMemoryMode::Polyfill,
1751 Some(
1752 ref mut interface @ FunctionInterface {
1753 stage:
1754 crate::ShaderStage::Compute
1755 | crate::ShaderStage::Mesh
1756 | crate::ShaderStage::Task,
1757 ..
1758 },
1759 ),
1760 ) => context.writer.generate_workgroup_vars_init_block(
1761 next_id,
1762 ir_module,
1763 info,
1764 local_invocation_index_id,
1765 interface,
1766 context.function,
1767 ),
1768 _ => None,
1769 };
1770
1771 let main_id = if let Some(exit_id) = workgroup_vars_init_exit_block_id {
1772 exit_id
1773 } else {
1774 next_id
1775 };
1776
1777 context.write_function_body(main_id, debug_info.as_ref())?;
1778
1779 let BlockContext {
1782 cached, temp_list, ..
1783 } = context;
1784 self.saved_cached = cached;
1785 self.temp_list = temp_list;
1786
1787 function.to_words(&mut self.logical_layout.function_definitions);
1788
1789 if let Some(EntryPointContext {
1790 mesh_state: Some(ref mesh_state),
1791 ..
1792 }) = function.entry_point_context
1793 {
1794 self.write_mesh_shader_wrapper(mesh_state, function_id)
1795 } else if let Some(EntryPointContext {
1796 task_payload_variable_id: Some(tp),
1797 ..
1798 }) = function.entry_point_context
1799 {
1800 self.write_task_shader_wrapper(tp, function_id)
1801 } else {
1802 Ok(function_id)
1803 }
1804 }
1805
1806 fn write_execution_mode(
1807 &mut self,
1808 function_id: Word,
1809 mode: spirv::ExecutionMode,
1810 ) -> Result<(), Error> {
1811 Instruction::execution_mode(function_id, mode, &[])
1813 .to_words(&mut self.logical_layout.execution_modes);
1814 Ok(())
1815 }
1816
1817 fn write_entry_point(
1819 &mut self,
1820 entry_point: &crate::EntryPoint,
1821 info: &FunctionInfo,
1822 ir_module: &crate::Module,
1823 debug_info: &Option<DebugInfoInner>,
1824 ) -> Result<Instruction, Error> {
1825 let mut interface_ids = Vec::new();
1826
1827 let function_id = self.write_function(
1828 &entry_point.function,
1829 info,
1830 ir_module,
1831 Some(FunctionInterface {
1832 varying_ids: &mut interface_ids,
1833 stage: entry_point.stage,
1834 task_payload: entry_point.task_payload,
1835 mesh_info: entry_point.mesh_info.clone(),
1836 workgroup_size: entry_point.workgroup_size,
1837 }),
1838 debug_info,
1839 )?;
1840
1841 let exec_model = match entry_point.stage {
1842 crate::ShaderStage::Vertex => spirv::ExecutionModel::Vertex,
1843 crate::ShaderStage::Fragment => {
1844 self.write_execution_mode(function_id, spirv::ExecutionMode::OriginUpperLeft)?;
1845 match entry_point.early_depth_test {
1846 Some(crate::EarlyDepthTest::Force) => {
1847 self.write_execution_mode(
1848 function_id,
1849 spirv::ExecutionMode::EarlyFragmentTests,
1850 )?;
1851 }
1852 Some(crate::EarlyDepthTest::Allow { conservative }) => {
1853 match conservative {
1858 crate::ConservativeDepth::GreaterEqual => self.write_execution_mode(
1859 function_id,
1860 spirv::ExecutionMode::DepthGreater,
1861 )?,
1862 crate::ConservativeDepth::LessEqual => self.write_execution_mode(
1863 function_id,
1864 spirv::ExecutionMode::DepthLess,
1865 )?,
1866 crate::ConservativeDepth::Unchanged => self.write_execution_mode(
1867 function_id,
1868 spirv::ExecutionMode::DepthUnchanged,
1869 )?,
1870 }
1871 }
1872 None => {}
1873 }
1874 if let Some(ref result) = entry_point.function.result {
1875 if contains_builtin(
1876 result.binding.as_ref(),
1877 result.ty,
1878 &ir_module.types,
1879 crate::BuiltIn::FragDepth,
1880 ) {
1881 self.write_execution_mode(
1882 function_id,
1883 spirv::ExecutionMode::DepthReplacing,
1884 )?;
1885 }
1886 }
1887 spirv::ExecutionModel::Fragment
1888 }
1889 crate::ShaderStage::Compute => {
1890 let execution_mode = spirv::ExecutionMode::LocalSize;
1891 Instruction::execution_mode(
1892 function_id,
1893 execution_mode,
1894 &entry_point.workgroup_size,
1895 )
1896 .to_words(&mut self.logical_layout.execution_modes);
1897 spirv::ExecutionModel::GLCompute
1898 }
1899 crate::ShaderStage::Task => {
1900 let execution_mode = spirv::ExecutionMode::LocalSize;
1901 Instruction::execution_mode(
1902 function_id,
1903 execution_mode,
1904 &entry_point.workgroup_size,
1905 )
1906 .to_words(&mut self.logical_layout.execution_modes);
1907 spirv::ExecutionModel::TaskEXT
1908 }
1909 crate::ShaderStage::Mesh => {
1910 let execution_mode = spirv::ExecutionMode::LocalSize;
1911 Instruction::execution_mode(
1912 function_id,
1913 execution_mode,
1914 &entry_point.workgroup_size,
1915 )
1916 .to_words(&mut self.logical_layout.execution_modes);
1917 let mesh_info = entry_point.mesh_info.as_ref().unwrap();
1918 Instruction::execution_mode(
1919 function_id,
1920 match mesh_info.topology {
1921 crate::MeshOutputTopology::Points => spirv::ExecutionMode::OutputPoints,
1922 crate::MeshOutputTopology::Lines => spirv::ExecutionMode::OutputLinesEXT,
1923 crate::MeshOutputTopology::Triangles => {
1924 spirv::ExecutionMode::OutputTrianglesEXT
1925 }
1926 },
1927 &[],
1928 )
1929 .to_words(&mut self.logical_layout.execution_modes);
1930 Instruction::execution_mode(
1931 function_id,
1932 spirv::ExecutionMode::OutputVertices,
1933 core::slice::from_ref(&mesh_info.max_vertices),
1934 )
1935 .to_words(&mut self.logical_layout.execution_modes);
1936 Instruction::execution_mode(
1937 function_id,
1938 spirv::ExecutionMode::OutputPrimitivesEXT,
1939 core::slice::from_ref(&mesh_info.max_primitives),
1940 )
1941 .to_words(&mut self.logical_layout.execution_modes);
1942 spirv::ExecutionModel::MeshEXT
1943 }
1944 crate::ShaderStage::RayGeneration => {
1945 self.require_any("ray tracing pipelines", &[spirv::Capability::RayTracingKHR])?;
1946 spirv::ExecutionModel::RayGenerationKHR
1947 }
1948 crate::ShaderStage::AnyHit => {
1949 self.require_any("ray tracing pipelines", &[spirv::Capability::RayTracingKHR])?;
1950 spirv::ExecutionModel::AnyHitKHR
1951 }
1952 crate::ShaderStage::ClosestHit => {
1953 self.require_any("ray tracing pipelines", &[spirv::Capability::RayTracingKHR])?;
1954 spirv::ExecutionModel::ClosestHitKHR
1955 }
1956 crate::ShaderStage::Miss => {
1957 self.require_any("ray tracing pipelines", &[spirv::Capability::RayTracingKHR])?;
1958 spirv::ExecutionModel::MissKHR
1959 }
1960 };
1961 Ok(Instruction::entry_point(
1964 exec_model,
1965 function_id,
1966 &entry_point.name,
1967 interface_ids.as_slice(),
1968 ))
1969 }
1970
1971 fn make_scalar(&mut self, id: Word, scalar: crate::Scalar) -> Instruction {
1972 use crate::ScalarKind as Sk;
1973
1974 let bits = (scalar.width * BITS_PER_BYTE) as u32;
1975 match scalar.kind {
1976 Sk::Sint | Sk::Uint => {
1977 let signedness = if scalar.kind == Sk::Sint {
1978 super::instructions::Signedness::Signed
1979 } else {
1980 super::instructions::Signedness::Unsigned
1981 };
1982 let cap = match bits {
1983 8 => Some(spirv::Capability::Int8),
1984 16 => Some(spirv::Capability::Int16),
1985 64 => Some(spirv::Capability::Int64),
1986 _ => None,
1987 };
1988 if let Some(cap) = cap {
1989 self.capabilities_used.insert(cap);
1990 }
1991 if bits == 16 {
1992 self.capabilities_used
1993 .insert(spirv::Capability::StorageBuffer16BitAccess);
1994 self.capabilities_used
1995 .insert(spirv::Capability::UniformAndStorageBuffer16BitAccess);
1996 if self.use_storage_input_output_16 {
1997 self.capabilities_used
1998 .insert(spirv::Capability::StorageInputOutput16);
1999 }
2000 }
2001 Instruction::type_int(id, bits, signedness)
2002 }
2003 Sk::Float => {
2004 if bits == 64 {
2005 self.capabilities_used.insert(spirv::Capability::Float64);
2006 }
2007 if bits == 16 {
2008 self.capabilities_used.insert(spirv::Capability::Float16);
2009 self.capabilities_used
2010 .insert(spirv::Capability::StorageBuffer16BitAccess);
2011 self.capabilities_used
2012 .insert(spirv::Capability::UniformAndStorageBuffer16BitAccess);
2013 if self.use_storage_input_output_16 {
2014 self.capabilities_used
2015 .insert(spirv::Capability::StorageInputOutput16);
2016 }
2017 }
2018 Instruction::type_float(id, bits)
2019 }
2020 Sk::Bool => Instruction::type_bool(id),
2021 Sk::AbstractInt | Sk::AbstractFloat => {
2022 unreachable!("abstract types should never reach the backend");
2023 }
2024 }
2025 }
2026
2027 fn request_type_capabilities(&mut self, inner: &crate::TypeInner) -> Result<(), Error> {
2028 match *inner {
2029 crate::TypeInner::Image {
2030 dim,
2031 arrayed,
2032 class,
2033 } => {
2034 let sampled = match class {
2035 crate::ImageClass::Sampled { .. } => true,
2036 crate::ImageClass::Depth { .. } => true,
2037 crate::ImageClass::Storage { format, .. } => {
2038 self.request_image_format_capabilities(format.into())?;
2039 false
2040 }
2041 crate::ImageClass::External => unimplemented!(),
2042 };
2043
2044 match dim {
2045 crate::ImageDimension::D1 => {
2046 if sampled {
2047 self.require_any("sampled 1D images", &[spirv::Capability::Sampled1D])?;
2048 } else {
2049 self.require_any("1D storage images", &[spirv::Capability::Image1D])?;
2050 }
2051 }
2052 crate::ImageDimension::Cube if arrayed => {
2053 if sampled {
2054 self.require_any(
2055 "sampled cube array images",
2056 &[spirv::Capability::SampledCubeArray],
2057 )?;
2058 } else {
2059 self.require_any(
2060 "cube array storage images",
2061 &[spirv::Capability::ImageCubeArray],
2062 )?;
2063 }
2064 }
2065 _ => {}
2066 }
2067 }
2068 crate::TypeInner::AccelerationStructure { .. } => {
2069 self.require_any(
2070 "Acceleration Structure",
2071 &[if self.has_ray_tracing_pipeline {
2074 spirv::Capability::RayTracingKHR
2075 } else {
2076 spirv::Capability::RayQueryKHR
2077 }],
2078 )?;
2079 }
2080 crate::TypeInner::RayQuery { .. } => {
2081 self.require_any("Ray Query", &[spirv::Capability::RayQueryKHR])?;
2082 }
2083 crate::TypeInner::Atomic(crate::Scalar { width: 8, kind: _ }) => {
2084 self.require_any("64 bit integer atomics", &[spirv::Capability::Int64Atomics])?;
2085 }
2086 crate::TypeInner::Atomic(crate::Scalar {
2087 width: 4,
2088 kind: crate::ScalarKind::Float,
2089 }) => {
2090 self.require_any(
2091 "32 bit floating-point atomics",
2092 &[spirv::Capability::AtomicFloat32AddEXT],
2093 )?;
2094 self.use_extension("SPV_EXT_shader_atomic_float_add");
2095 }
2096 crate::TypeInner::Matrix {
2098 scalar: crate::Scalar::F16,
2099 ..
2100 }
2101 | crate::TypeInner::Vector {
2102 scalar: crate::Scalar::F16,
2103 ..
2104 }
2105 | crate::TypeInner::Scalar(crate::Scalar::F16) => {
2106 self.require_any("16 bit floating-point", &[spirv::Capability::Float16])?;
2107 self.use_extension("SPV_KHR_16bit_storage");
2108 }
2109 crate::TypeInner::Vector {
2111 scalar:
2112 crate::Scalar {
2113 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
2114 width: 2,
2115 },
2116 ..
2117 }
2118 | crate::TypeInner::Scalar(crate::Scalar {
2119 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
2120 width: 2,
2121 }) => {
2122 self.require_any("16 bit integer", &[spirv::Capability::Int16])?;
2123 self.use_extension("SPV_KHR_16bit_storage");
2124 }
2125 crate::TypeInner::CooperativeMatrix { .. } => {
2127 self.require_any(
2128 "cooperative matrix",
2129 &[spirv::Capability::CooperativeMatrixKHR],
2130 )?;
2131 self.require_any("memory model", &[spirv::Capability::VulkanMemoryModel])?;
2132 self.use_extension("SPV_KHR_cooperative_matrix");
2133 self.use_extension("SPV_KHR_vulkan_memory_model");
2134 }
2135 _ => {}
2136 }
2137 Ok(())
2138 }
2139
2140 fn write_numeric_type_declaration_local(&mut self, id: Word, numeric: NumericType) {
2141 let instruction = match numeric {
2142 NumericType::Scalar(scalar) => self.make_scalar(id, scalar),
2143 NumericType::Vector { size, scalar } => {
2144 let scalar_id = self.get_numeric_type_id(NumericType::Scalar(scalar));
2145 Instruction::type_vector(id, scalar_id, size)
2146 }
2147 NumericType::Matrix {
2148 columns,
2149 rows,
2150 scalar,
2151 } => {
2152 let column_id =
2153 self.get_numeric_type_id(NumericType::Vector { size: rows, scalar });
2154 Instruction::type_matrix(id, column_id, columns)
2155 }
2156 };
2157
2158 instruction.to_words(&mut self.logical_layout.declarations);
2159 }
2160
2161 fn write_cooperative_type_declaration_local(&mut self, id: Word, coop: CooperativeType) {
2162 let instruction = match coop {
2163 CooperativeType::Matrix {
2164 columns,
2165 rows,
2166 scalar,
2167 role,
2168 } => {
2169 let scalar_id =
2170 self.get_localtype_id(LocalType::Numeric(NumericType::Scalar(scalar)));
2171 let scope_id = self.get_index_constant(spirv::Scope::Subgroup as u32);
2172 let columns_id = self.get_index_constant(columns as u32);
2173 let rows_id = self.get_index_constant(rows as u32);
2174 let role_id =
2175 self.get_index_constant(spirv::CooperativeMatrixUse::from(role) as u32);
2176 Instruction::type_coop_matrix(id, scalar_id, scope_id, rows_id, columns_id, role_id)
2177 }
2178 };
2179
2180 instruction.to_words(&mut self.logical_layout.declarations);
2181 }
2182
2183 fn write_type_declaration_local(&mut self, id: Word, local_ty: LocalType) {
2184 let instruction = match local_ty {
2185 LocalType::Numeric(numeric) => {
2186 self.write_numeric_type_declaration_local(id, numeric);
2187 return;
2188 }
2189 LocalType::Cooperative(coop) => {
2190 self.write_cooperative_type_declaration_local(id, coop);
2191 return;
2192 }
2193 LocalType::Pointer { base, class } => Instruction::type_pointer(id, class, base),
2194 LocalType::Image(image) => {
2195 let local_type = LocalType::Numeric(NumericType::Scalar(image.sampled_type));
2196 let type_id = self.get_localtype_id(local_type);
2197 Instruction::type_image(id, type_id, image.dim, image.flags, image.image_format)
2198 }
2199 LocalType::Sampler => Instruction::type_sampler(id),
2200 LocalType::SampledImage { image_type_id } => {
2201 Instruction::type_sampled_image(id, image_type_id)
2202 }
2203 LocalType::BindingArray { base, size } => {
2204 let inner_ty = self.get_handle_type_id(base);
2205 let scalar_id = self.get_constant_scalar(crate::Literal::U32(size));
2206 Instruction::type_array(id, inner_ty, scalar_id)
2207 }
2208 LocalType::AccelerationStructure => Instruction::type_acceleration_structure(id),
2209 LocalType::RayQuery => Instruction::type_ray_query(id),
2210 };
2211
2212 instruction.to_words(&mut self.logical_layout.declarations);
2213 }
2214
2215 fn write_type_declaration_arena(
2216 &mut self,
2217 module: &crate::Module,
2218 handle: Handle<crate::Type>,
2219 ) -> Result<Word, Error> {
2220 let ty = &module.types[handle];
2221 self.request_type_capabilities(&ty.inner)?;
2226 let id = if let Some(local) = self.localtype_from_inner(&ty.inner) {
2227 match self.lookup_type.entry(LookupType::Local(local)) {
2231 Entry::Occupied(e) => *e.get(),
2233
2234 Entry::Vacant(e) => {
2236 let id = self.id_gen.next();
2237 e.insert(id);
2238
2239 self.write_type_declaration_local(id, local);
2240
2241 id
2242 }
2243 }
2244 } else {
2245 use spirv::Decoration;
2246
2247 let id = self.id_gen.next();
2248 let instruction = match ty.inner {
2249 crate::TypeInner::Array { base, size, stride } => {
2250 self.decorate(id, Decoration::ArrayStride, &[stride]);
2251
2252 let type_id = self.get_handle_type_id(base);
2253 match size.resolve(module.to_ctx())? {
2254 crate::proc::IndexableLength::Known(length) => {
2255 let length_id = self.get_index_constant(length);
2256 Instruction::type_array(id, type_id, length_id)
2257 }
2258 crate::proc::IndexableLength::Dynamic => {
2259 Instruction::type_runtime_array(id, type_id)
2260 }
2261 }
2262 }
2263 crate::TypeInner::BindingArray { base, size } => {
2264 let type_id = self.get_handle_type_id(base);
2265 match size.resolve(module.to_ctx())? {
2266 crate::proc::IndexableLength::Known(length) => {
2267 let length_id = self.get_index_constant(length);
2268 Instruction::type_array(id, type_id, length_id)
2269 }
2270 crate::proc::IndexableLength::Dynamic => {
2271 Instruction::type_runtime_array(id, type_id)
2272 }
2273 }
2274 }
2275 crate::TypeInner::Struct {
2276 ref members,
2277 span: _,
2278 } => {
2279 let mut has_runtime_array = false;
2280 let mut member_ids = Vec::with_capacity(members.len());
2281 for (index, member) in members.iter().enumerate() {
2282 let member_ty = &module.types[member.ty];
2283 match member_ty.inner {
2284 crate::TypeInner::Array {
2285 base: _,
2286 size: crate::ArraySize::Dynamic,
2287 stride: _,
2288 } => {
2289 has_runtime_array = true;
2290 }
2291 _ => (),
2292 }
2293 self.decorate_struct_member(id, index, member, &module.types)?;
2294 let member_id = self.get_handle_type_id(member.ty);
2295 member_ids.push(member_id);
2296 }
2297 if has_runtime_array {
2298 self.decorate(id, Decoration::Block, &[]);
2299 }
2300 Instruction::type_struct(id, member_ids.as_slice())
2301 }
2302
2303 crate::TypeInner::Scalar(_)
2306 | crate::TypeInner::Atomic(_)
2307 | crate::TypeInner::Vector { .. }
2308 | crate::TypeInner::Matrix { .. }
2309 | crate::TypeInner::CooperativeMatrix { .. }
2310 | crate::TypeInner::Pointer { .. }
2311 | crate::TypeInner::ValuePointer { .. }
2312 | crate::TypeInner::Image { .. }
2313 | crate::TypeInner::Sampler { .. }
2314 | crate::TypeInner::AccelerationStructure { .. }
2315 | crate::TypeInner::RayQuery { .. } => unreachable!(),
2316 };
2317
2318 instruction.to_words(&mut self.logical_layout.declarations);
2319 id
2320 };
2321
2322 self.lookup_type.insert(LookupType::Handle(handle), id);
2324
2325 if self.flags.contains(WriterFlags::DEBUG) {
2326 if let Some(ref name) = ty.name {
2327 self.debugs.push(Instruction::name(id, name));
2328 }
2329 }
2330
2331 Ok(id)
2332 }
2333
2334 fn write_std140_compat_type_declaration(
2370 &mut self,
2371 module: &crate::Module,
2372 handle: Handle<crate::Type>,
2373 ) -> Result<Option<Word>, Error> {
2374 if let Some(std140_type_info) = self.std140_compat_uniform_types.get(&handle) {
2375 return Ok(Some(std140_type_info.type_id));
2376 }
2377
2378 let type_inner = &module.types[handle].inner;
2379 let std140_type_id = match *type_inner {
2380 crate::TypeInner::Matrix {
2381 columns,
2382 rows: rows @ crate::VectorSize::Bi,
2383 scalar,
2384 } => {
2385 let std140_type_id = self.id_gen.next();
2386 let mut member_type_ids: ArrayVec<Word, 4> = ArrayVec::new();
2387 let column_type_id =
2388 self.get_numeric_type_id(NumericType::Vector { size: rows, scalar });
2389 for column in 0..columns as u32 {
2390 member_type_ids.push(column_type_id);
2391 self.annotations.push(Instruction::member_decorate(
2392 std140_type_id,
2393 column,
2394 spirv::Decoration::Offset,
2395 &[column * rows as u32 * scalar.width as u32],
2396 ));
2397 if self.flags.contains(WriterFlags::DEBUG) {
2398 self.debugs.push(Instruction::member_name(
2399 std140_type_id,
2400 column,
2401 &format!("col{column}"),
2402 ));
2403 }
2404 }
2405 Instruction::type_struct(std140_type_id, &member_type_ids)
2406 .to_words(&mut self.logical_layout.declarations);
2407 self.std140_compat_uniform_types.insert(
2408 handle,
2409 Std140CompatTypeInfo {
2410 type_id: std140_type_id,
2411 member_indices: Vec::new(),
2412 },
2413 );
2414 Some(std140_type_id)
2415 }
2416 crate::TypeInner::Array { base, size, stride } => {
2417 match self.write_std140_compat_type_declaration(module, base)? {
2418 Some(std140_base_type_id) => {
2419 let std140_type_id = self.id_gen.next();
2420 self.decorate(std140_type_id, spirv::Decoration::ArrayStride, &[stride]);
2421 let instruction = match size.resolve(module.to_ctx())? {
2422 crate::proc::IndexableLength::Known(length) => {
2423 let length_id = self.get_index_constant(length);
2424 Instruction::type_array(
2425 std140_type_id,
2426 std140_base_type_id,
2427 length_id,
2428 )
2429 }
2430 crate::proc::IndexableLength::Dynamic => {
2431 unreachable!()
2432 }
2433 };
2434 instruction.to_words(&mut self.logical_layout.declarations);
2435 self.std140_compat_uniform_types.insert(
2436 handle,
2437 Std140CompatTypeInfo {
2438 type_id: std140_type_id,
2439 member_indices: Vec::new(),
2440 },
2441 );
2442 Some(std140_type_id)
2443 }
2444 None => None,
2445 }
2446 }
2447 crate::TypeInner::Struct { ref members, .. } => {
2448 let mut needs_std140_type = false;
2449 for member in members {
2450 match module.types[member.ty].inner {
2451 crate::TypeInner::Matrix {
2455 rows: crate::VectorSize::Bi,
2456 ..
2457 } => needs_std140_type = true,
2458 crate::TypeInner::Array { .. }
2462 if self
2463 .write_std140_compat_type_declaration(module, member.ty)?
2464 .is_some() =>
2465 {
2466 needs_std140_type = true;
2467 }
2468 _ => {}
2469 }
2470 }
2471
2472 if needs_std140_type {
2473 let std140_type_id = self.id_gen.next();
2474 let mut member_ids = Vec::new();
2475 let mut member_indices = Vec::new();
2476 let mut next_index = 0;
2477
2478 for member in members {
2479 member_indices.push(next_index);
2480 match module.types[member.ty].inner {
2481 crate::TypeInner::Matrix {
2482 columns,
2483 rows: rows @ crate::VectorSize::Bi,
2484 scalar,
2485 } => {
2486 let vector_type_id =
2487 self.get_numeric_type_id(NumericType::Vector {
2488 size: rows,
2489 scalar,
2490 });
2491 for column in 0..columns as u32 {
2492 self.annotations.push(Instruction::member_decorate(
2493 std140_type_id,
2494 next_index,
2495 spirv::Decoration::Offset,
2496 &[member.offset
2497 + column * rows as u32 * scalar.width as u32],
2498 ));
2499 if self.flags.contains(WriterFlags::DEBUG) {
2500 if let Some(ref name) = member.name {
2501 self.debugs.push(Instruction::member_name(
2502 std140_type_id,
2503 next_index,
2504 &format!("{name}_col{column}"),
2505 ));
2506 }
2507 }
2508 member_ids.push(vector_type_id);
2509 next_index += 1;
2510 }
2511 }
2512 _ => {
2513 let member_id =
2514 match self.std140_compat_uniform_types.get(&member.ty) {
2515 Some(std140_member_type_info) => {
2516 self.annotations.push(Instruction::member_decorate(
2517 std140_type_id,
2518 next_index,
2519 spirv::Decoration::Offset,
2520 &[member.offset],
2521 ));
2522 if self.flags.contains(WriterFlags::DEBUG) {
2523 if let Some(ref name) = member.name {
2524 self.debugs.push(Instruction::member_name(
2525 std140_type_id,
2526 next_index,
2527 name,
2528 ));
2529 }
2530 }
2531 std140_member_type_info.type_id
2532 }
2533 None => {
2534 self.decorate_struct_member(
2535 std140_type_id,
2536 next_index as usize,
2537 member,
2538 &module.types,
2539 )?;
2540 self.get_handle_type_id(member.ty)
2541 }
2542 };
2543 member_ids.push(member_id);
2544 next_index += 1;
2545 }
2546 }
2547 }
2548
2549 Instruction::type_struct(std140_type_id, &member_ids)
2550 .to_words(&mut self.logical_layout.declarations);
2551 self.std140_compat_uniform_types.insert(
2552 handle,
2553 Std140CompatTypeInfo {
2554 type_id: std140_type_id,
2555 member_indices,
2556 },
2557 );
2558 Some(std140_type_id)
2559 } else {
2560 None
2561 }
2562 }
2563 _ => None,
2564 };
2565
2566 if let Some(std140_type_id) = std140_type_id {
2567 if self.flags.contains(WriterFlags::DEBUG) {
2568 let name = format!("std140_{:?}", handle.for_debug(&module.types));
2569 self.debugs.push(Instruction::name(std140_type_id, &name));
2570 }
2571 }
2572 Ok(std140_type_id)
2573 }
2574
2575 fn request_image_format_capabilities(
2576 &mut self,
2577 format: spirv::ImageFormat,
2578 ) -> Result<(), Error> {
2579 use spirv::ImageFormat as If;
2580 match format {
2581 If::Rg32f
2582 | If::Rg16f
2583 | If::R11fG11fB10f
2584 | If::R16f
2585 | If::Rgba16
2586 | If::Rgb10A2
2587 | If::Rg16
2588 | If::Rg8
2589 | If::R16
2590 | If::R8
2591 | If::Rgba16Snorm
2592 | If::Rg16Snorm
2593 | If::Rg8Snorm
2594 | If::R16Snorm
2595 | If::R8Snorm
2596 | If::Rg32i
2597 | If::Rg16i
2598 | If::Rg8i
2599 | If::R16i
2600 | If::R8i
2601 | If::Rgb10a2ui
2602 | If::Rg32ui
2603 | If::Rg16ui
2604 | If::Rg8ui
2605 | If::R16ui
2606 | If::R8ui => self.require_any(
2607 "storage image format",
2608 &[spirv::Capability::StorageImageExtendedFormats],
2609 ),
2610 If::R64ui | If::R64i => {
2611 self.use_extension("SPV_EXT_shader_image_int64");
2612 self.require_any(
2613 "64-bit integer storage image format",
2614 &[spirv::Capability::Int64ImageEXT],
2615 )
2616 }
2617 If::Unknown
2618 | If::Rgba32f
2619 | If::Rgba16f
2620 | If::R32f
2621 | If::Rgba8
2622 | If::Rgba8Snorm
2623 | If::Rgba32i
2624 | If::Rgba16i
2625 | If::Rgba8i
2626 | If::R32i
2627 | If::Rgba32ui
2628 | If::Rgba16ui
2629 | If::Rgba8ui
2630 | If::R32ui => Ok(()),
2631 }
2632 }
2633
2634 pub(super) fn get_index_constant(&mut self, index: Word) -> Word {
2635 self.get_constant_scalar(crate::Literal::U32(index))
2636 }
2637
2638 pub(super) fn get_constant_scalar_with(
2639 &mut self,
2640 value: u8,
2641 scalar: crate::Scalar,
2642 ) -> Result<Word, Error> {
2643 Ok(
2644 self.get_constant_scalar(crate::Literal::new(value, scalar).ok_or(
2645 Error::Validation("Unexpected kind and/or width for Literal"),
2646 )?),
2647 )
2648 }
2649
2650 pub(super) fn get_constant_scalar(&mut self, value: crate::Literal) -> Word {
2651 let scalar = CachedConstant::Literal(value.into());
2652 if let Some(&id) = self.cached_constants.get(&scalar) {
2653 return id;
2654 }
2655 let id = self.id_gen.next();
2656 self.write_constant_scalar(id, &value, None);
2657 self.cached_constants.insert(scalar, id);
2658 id
2659 }
2660
2661 fn write_constant_scalar(
2662 &mut self,
2663 id: Word,
2664 value: &crate::Literal,
2665 debug_name: Option<&String>,
2666 ) {
2667 if self.flags.contains(WriterFlags::DEBUG) {
2668 if let Some(name) = debug_name {
2669 self.debugs.push(Instruction::name(id, name));
2670 }
2671 }
2672 let type_id = self.get_numeric_type_id(NumericType::Scalar(value.scalar()));
2673 let instruction = match *value {
2674 crate::Literal::F64(value) => {
2675 let bits = value.to_bits();
2676 Instruction::constant_64bit(type_id, id, bits as u32, (bits >> 32) as u32)
2677 }
2678 crate::Literal::F32(value) => Instruction::constant_32bit(type_id, id, value.to_bits()),
2679 crate::Literal::F16(value) => {
2680 let low = value.to_bits();
2681 Instruction::constant_16bit(type_id, id, low as u32)
2682 }
2683 crate::Literal::U16(value) => Instruction::constant_16bit(type_id, id, value as u32),
2684 crate::Literal::I16(value) => {
2685 Instruction::constant_16bit(type_id, id, value as i32 as u32)
2689 }
2690 crate::Literal::U32(value) => Instruction::constant_32bit(type_id, id, value),
2691 crate::Literal::I32(value) => Instruction::constant_32bit(type_id, id, value as u32),
2692 crate::Literal::U64(value) => {
2693 Instruction::constant_64bit(type_id, id, value as u32, (value >> 32) as u32)
2694 }
2695 crate::Literal::I64(value) => {
2696 Instruction::constant_64bit(type_id, id, value as u32, (value >> 32) as u32)
2697 }
2698 crate::Literal::Bool(true) => Instruction::constant_true(type_id, id),
2699 crate::Literal::Bool(false) => Instruction::constant_false(type_id, id),
2700 crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
2701 unreachable!("Abstract types should not appear in IR presented to backends");
2702 }
2703 };
2704
2705 instruction.to_words(&mut self.logical_layout.declarations);
2706 }
2707
2708 pub(super) fn get_constant_composite(
2709 &mut self,
2710 ty: LookupType,
2711 constituent_ids: &[Word],
2712 ) -> Word {
2713 let composite = CachedConstant::Composite {
2714 ty,
2715 constituent_ids: constituent_ids.to_vec(),
2716 };
2717 if let Some(&id) = self.cached_constants.get(&composite) {
2718 return id;
2719 }
2720 let id = self.id_gen.next();
2721 self.write_constant_composite(id, ty, constituent_ids, None);
2722 self.cached_constants.insert(composite, id);
2723 id
2724 }
2725
2726 fn write_constant_composite(
2727 &mut self,
2728 id: Word,
2729 ty: LookupType,
2730 constituent_ids: &[Word],
2731 debug_name: Option<&String>,
2732 ) {
2733 if self.flags.contains(WriterFlags::DEBUG) {
2734 if let Some(name) = debug_name {
2735 self.debugs.push(Instruction::name(id, name));
2736 }
2737 }
2738 let type_id = self.get_type_id(ty);
2739 Instruction::constant_composite(type_id, id, constituent_ids)
2740 .to_words(&mut self.logical_layout.declarations);
2741 }
2742
2743 pub(super) fn get_constant_null(&mut self, type_id: Word) -> Word {
2744 let null = CachedConstant::ZeroValue(type_id);
2745 if let Some(&id) = self.cached_constants.get(&null) {
2746 return id;
2747 }
2748 let id = self.write_constant_null(type_id);
2749 self.cached_constants.insert(null, id);
2750 id
2751 }
2752
2753 pub(super) fn write_constant_null(&mut self, type_id: Word) -> Word {
2754 let null_id = self.id_gen.next();
2755 Instruction::constant_null(type_id, null_id)
2756 .to_words(&mut self.logical_layout.declarations);
2757 null_id
2758 }
2759
2760 fn write_constant_expr(
2761 &mut self,
2762 handle: Handle<crate::Expression>,
2763 ir_module: &crate::Module,
2764 mod_info: &ModuleInfo,
2765 ) -> Result<Word, Error> {
2766 let id = match ir_module.global_expressions[handle] {
2767 crate::Expression::Literal(literal) => self.get_constant_scalar(literal),
2768 crate::Expression::Constant(constant) => {
2769 let constant = &ir_module.constants[constant];
2770 self.constant_ids[constant.init]
2771 }
2772 crate::Expression::ZeroValue(ty) => {
2773 let type_id = self.get_handle_type_id(ty);
2774 self.get_constant_null(type_id)
2775 }
2776 crate::Expression::Compose { ty, ref components } => {
2777 let component_ids: Vec<_> = crate::proc::flatten_compose(
2778 ty,
2779 components,
2780 &ir_module.global_expressions,
2781 &ir_module.types,
2782 )
2783 .map(|component| self.constant_ids[component])
2784 .collect();
2785 self.get_constant_composite(LookupType::Handle(ty), component_ids.as_slice())
2786 }
2787 crate::Expression::Splat { size, value } => {
2788 let value_id = self.constant_ids[value];
2789 let component_ids = &[value_id; 4][..size as usize];
2790
2791 let ty = self.get_expression_lookup_type(&mod_info[handle]);
2792
2793 self.get_constant_composite(ty, component_ids)
2794 }
2795 _ => {
2796 return Err(Error::Override);
2797 }
2798 };
2799
2800 self.constant_ids[handle] = id;
2801
2802 Ok(id)
2803 }
2804
2805 pub(super) fn write_control_barrier(
2806 &mut self,
2807 flags: crate::Barrier,
2808 body: &mut Vec<Instruction>,
2809 ) {
2810 let memory_scope = if flags.contains(crate::Barrier::STORAGE) {
2811 spirv::Scope::Device
2812 } else if flags.contains(crate::Barrier::SUB_GROUP) {
2813 spirv::Scope::Subgroup
2814 } else {
2815 spirv::Scope::Workgroup
2816 };
2817 let mut semantics = spirv::MemorySemantics::ACQUIRE_RELEASE;
2818 semantics.set(
2819 spirv::MemorySemantics::UNIFORM_MEMORY,
2820 flags.contains(crate::Barrier::STORAGE),
2821 );
2822 semantics.set(
2823 spirv::MemorySemantics::WORKGROUP_MEMORY,
2824 flags.contains(crate::Barrier::WORK_GROUP),
2825 );
2826 semantics.set(
2827 spirv::MemorySemantics::SUBGROUP_MEMORY,
2828 flags.contains(crate::Barrier::SUB_GROUP),
2829 );
2830 semantics.set(
2831 spirv::MemorySemantics::IMAGE_MEMORY,
2832 flags.contains(crate::Barrier::TEXTURE),
2833 );
2834 let exec_scope_id = if flags.contains(crate::Barrier::SUB_GROUP) {
2835 self.get_index_constant(spirv::Scope::Subgroup as u32)
2836 } else {
2837 self.get_index_constant(spirv::Scope::Workgroup as u32)
2838 };
2839 let mem_scope_id = self.get_index_constant(memory_scope as u32);
2840 let semantics_id = self.get_index_constant(semantics.bits());
2841 body.push(Instruction::control_barrier(
2842 exec_scope_id,
2843 mem_scope_id,
2844 semantics_id,
2845 ));
2846 }
2847
2848 pub(super) fn write_memory_barrier(&mut self, flags: crate::Barrier, block: &mut Block) {
2849 let mut semantics = spirv::MemorySemantics::ACQUIRE_RELEASE;
2850 semantics.set(
2851 spirv::MemorySemantics::UNIFORM_MEMORY,
2852 flags.contains(crate::Barrier::STORAGE),
2853 );
2854 semantics.set(
2855 spirv::MemorySemantics::WORKGROUP_MEMORY,
2856 flags.contains(crate::Barrier::WORK_GROUP),
2857 );
2858 semantics.set(
2859 spirv::MemorySemantics::SUBGROUP_MEMORY,
2860 flags.contains(crate::Barrier::SUB_GROUP),
2861 );
2862 semantics.set(
2863 spirv::MemorySemantics::IMAGE_MEMORY,
2864 flags.contains(crate::Barrier::TEXTURE),
2865 );
2866 let mem_scope_id = if flags.contains(crate::Barrier::STORAGE) {
2867 self.get_index_constant(spirv::Scope::Device as u32)
2868 } else if flags.contains(crate::Barrier::SUB_GROUP) {
2869 self.get_index_constant(spirv::Scope::Subgroup as u32)
2870 } else {
2871 self.get_index_constant(spirv::Scope::Workgroup as u32)
2872 };
2873 let semantics_id = self.get_index_constant(semantics.bits());
2874 block
2875 .body
2876 .push(Instruction::memory_barrier(mem_scope_id, semantics_id));
2877 }
2878
2879 fn generate_workgroup_vars_init_block(
2880 &mut self,
2881 entry_id: Word,
2882 ir_module: &crate::Module,
2883 info: &FunctionInfo,
2884 local_invocation_index: Option<Word>,
2885 interface: &mut FunctionInterface,
2886 function: &mut Function,
2887 ) -> Option<Word> {
2888 let body = ir_module
2889 .global_variables
2890 .iter()
2891 .filter(|&(handle, var)| {
2892 let task_exception = (var.space == crate::AddressSpace::TaskPayload)
2893 && interface.stage == crate::ShaderStage::Task;
2894 !info[handle].is_empty()
2895 && (var.space == crate::AddressSpace::WorkGroup || task_exception)
2896 })
2897 .map(|(handle, var)| {
2898 let var_id = self.global_variables[handle].var_id;
2902 let var_type_id = self.get_handle_type_id(var.ty);
2903 let init_word = self.get_constant_null(var_type_id);
2904 Instruction::store(var_id, init_word, None)
2905 })
2906 .collect::<Vec<_>>();
2907
2908 if body.is_empty() {
2909 return None;
2910 }
2911
2912 let mut pre_if_block = Block::new(entry_id);
2913
2914 let local_invocation_index = if let Some(local_invocation_index) = local_invocation_index {
2915 local_invocation_index
2916 } else {
2917 let varying_id = self.id_gen.next();
2918 let class = spirv::StorageClass::Input;
2919 let u32_ty_id = self.get_u32_type_id();
2920 let pointer_type_id = self.get_pointer_type_id(u32_ty_id, class);
2921
2922 Instruction::variable(pointer_type_id, varying_id, class, None)
2923 .to_words(&mut self.logical_layout.declarations);
2924
2925 self.decorate(
2926 varying_id,
2927 spirv::Decoration::BuiltIn,
2928 &[spirv::BuiltIn::LocalInvocationIndex as u32],
2929 );
2930
2931 interface.varying_ids.push(varying_id);
2932 let id = self.id_gen.next();
2933 pre_if_block
2934 .body
2935 .push(Instruction::load(u32_ty_id, id, varying_id, None));
2936
2937 id
2938 };
2939
2940 let zero_id = self.get_constant_scalar(crate::Literal::U32(0));
2941
2942 let eq_id = self.id_gen.next();
2943 pre_if_block.body.push(Instruction::binary(
2944 spirv::Op::IEqual,
2945 self.get_bool_type_id(),
2946 eq_id,
2947 local_invocation_index,
2948 zero_id,
2949 ));
2950
2951 let merge_id = self.id_gen.next();
2952 pre_if_block.body.push(Instruction::selection_merge(
2953 merge_id,
2954 spirv::SelectionControl::NONE,
2955 ));
2956
2957 let accept_id = self.id_gen.next();
2958 function.consume(
2959 pre_if_block,
2960 Instruction::branch_conditional(eq_id, accept_id, merge_id),
2961 );
2962
2963 let accept_block = Block {
2964 label_id: accept_id,
2965 body,
2966 };
2967 function.consume(accept_block, Instruction::branch(merge_id));
2968
2969 let mut post_if_block = Block::new(merge_id);
2970
2971 self.write_control_barrier(crate::Barrier::WORK_GROUP, &mut post_if_block.body);
2972
2973 let next_id = self.id_gen.next();
2974 function.consume(post_if_block, Instruction::branch(next_id));
2975 Some(next_id)
2976 }
2977
2978 fn write_varying(
2999 &mut self,
3000 ir_module: &crate::Module,
3001 stage: crate::ShaderStage,
3002 class: spirv::StorageClass,
3003 debug_name: Option<&str>,
3004 ty: Handle<crate::Type>,
3005 binding: &crate::Binding,
3006 ) -> Result<Word, Error> {
3007 let class = match *binding {
3020 crate::Binding::BuiltIn(crate::BuiltIn::HitBarycentrics) => {
3021 spirv::StorageClass::HitAttributeKHR
3022 }
3023 _ => class,
3024 };
3025
3026 let id = self.id_gen.next();
3027 let ty_inner = &ir_module.types[ty].inner;
3028 let needs_polyfill = self.needs_f16_polyfill(ty_inner);
3029
3030 let pointer_type_id = if needs_polyfill {
3031 let f32_value_local =
3032 super::f16_polyfill::F16IoPolyfill::create_polyfill_type(ty_inner)
3033 .expect("needs_polyfill returned true but create_polyfill_type returned None");
3034
3035 let f32_type_id = self.get_localtype_id(f32_value_local);
3036 let ptr_id = self.get_pointer_type_id(f32_type_id, class);
3037 self.io_f16_polyfills.register_io_var(id, f32_type_id);
3038
3039 ptr_id
3040 } else {
3041 self.get_handle_pointer_type_id(ty, class)
3042 };
3043
3044 Instruction::variable(pointer_type_id, id, class, None)
3045 .to_words(&mut self.logical_layout.declarations);
3046
3047 if self
3048 .flags
3049 .contains(WriterFlags::DEBUG | WriterFlags::LABEL_VARYINGS)
3050 {
3051 if let Some(name) = debug_name {
3052 self.debugs.push(Instruction::name(id, name));
3053 }
3054 }
3055
3056 let binding = self.map_binding(ir_module, stage, class, ty, binding)?;
3057 self.write_binding(id, binding);
3058
3059 Ok(id)
3060 }
3061
3062 pub fn write_binding(&mut self, id: Word, binding: BindingDecorations) {
3063 match binding {
3064 BindingDecorations::None => (),
3065 BindingDecorations::BuiltIn(bi, others) => {
3066 self.decorate(id, spirv::Decoration::BuiltIn, &[bi as u32]);
3067 for other in others {
3068 self.decorate(id, other, &[]);
3069 }
3070 }
3071 BindingDecorations::Location {
3072 location,
3073 others,
3074 blend_src,
3075 } => {
3076 self.decorate(id, spirv::Decoration::Location, &[location]);
3077 for other in others {
3078 self.decorate(id, other, &[]);
3079 }
3080 if let Some(blend_src) = blend_src {
3081 self.decorate(id, spirv::Decoration::Index, &[blend_src]);
3082 }
3083 }
3084 }
3085 }
3086
3087 pub fn write_binding_struct_member(
3088 &mut self,
3089 struct_id: Word,
3090 member_idx: Word,
3091 binding_info: BindingDecorations,
3092 ) {
3093 match binding_info {
3094 BindingDecorations::None => (),
3095 BindingDecorations::BuiltIn(bi, others) => {
3096 self.annotations.push(Instruction::member_decorate(
3097 struct_id,
3098 member_idx,
3099 spirv::Decoration::BuiltIn,
3100 &[bi as Word],
3101 ));
3102 for other in others {
3103 self.annotations.push(Instruction::member_decorate(
3104 struct_id,
3105 member_idx,
3106 other,
3107 &[],
3108 ));
3109 }
3110 }
3111 BindingDecorations::Location {
3112 location,
3113 others,
3114 blend_src,
3115 } => {
3116 self.annotations.push(Instruction::member_decorate(
3117 struct_id,
3118 member_idx,
3119 spirv::Decoration::Location,
3120 &[location],
3121 ));
3122 for other in others {
3123 self.annotations.push(Instruction::member_decorate(
3124 struct_id,
3125 member_idx,
3126 other,
3127 &[],
3128 ));
3129 }
3130 if let Some(blend_src) = blend_src {
3131 self.annotations.push(Instruction::member_decorate(
3132 struct_id,
3133 member_idx,
3134 spirv::Decoration::Index,
3135 &[blend_src],
3136 ));
3137 }
3138 }
3139 }
3140 }
3141
3142 pub fn map_binding(
3143 &mut self,
3144 ir_module: &crate::Module,
3145 stage: crate::ShaderStage,
3146 class: spirv::StorageClass,
3147 ty: Handle<crate::Type>,
3148 binding: &crate::Binding,
3149 ) -> Result<BindingDecorations, Error> {
3150 use spirv::BuiltIn;
3151 use spirv::Decoration;
3152 match *binding {
3153 crate::Binding::Location {
3154 location,
3155 interpolation,
3156 sampling,
3157 blend_src,
3158 per_primitive,
3159 } => {
3160 let mut others = ArrayVec::new();
3161
3162 let no_decorations =
3163 (class == spirv::StorageClass::Input && stage == crate::ShaderStage::Vertex) ||
3167 (class == spirv::StorageClass::Output && stage == crate::ShaderStage::Fragment);
3171
3172 if !no_decorations {
3173 match interpolation {
3174 None | Some(crate::Interpolation::Perspective) => (),
3176 Some(crate::Interpolation::Flat) => {
3177 others.push(Decoration::Flat);
3178 }
3179 Some(crate::Interpolation::Linear) => {
3180 others.push(Decoration::NoPerspective);
3181 }
3182 Some(crate::Interpolation::PerVertex) => {
3183 others.push(Decoration::PerVertexKHR);
3184 self.require_any(
3185 "`per_vertex` interpolation",
3186 &[spirv::Capability::FragmentBarycentricKHR],
3187 )?;
3188 self.use_extension("SPV_KHR_fragment_shader_barycentric");
3189 }
3190 }
3191 match sampling {
3192 None
3194 | Some(
3195 crate::Sampling::Center
3196 | crate::Sampling::First
3197 | crate::Sampling::Either,
3198 ) => (),
3199 Some(crate::Sampling::Centroid) => {
3200 others.push(Decoration::Centroid);
3201 }
3202 Some(crate::Sampling::Sample) => {
3203 self.require_any(
3204 "per-sample interpolation",
3205 &[spirv::Capability::SampleRateShading],
3206 )?;
3207 others.push(Decoration::Sample);
3208 }
3209 }
3210 }
3211 if per_primitive && stage == crate::ShaderStage::Fragment {
3212 others.push(Decoration::PerPrimitiveEXT);
3213 }
3214 Ok(BindingDecorations::Location {
3215 location,
3216 others,
3217 blend_src,
3218 })
3219 }
3220 crate::Binding::BuiltIn(built_in) => {
3221 use crate::BuiltIn as Bi;
3222 let mut others = ArrayVec::new();
3223
3224 let built_in = match built_in {
3225 Bi::Position { invariant } => {
3226 if invariant {
3227 others.push(Decoration::Invariant);
3228 }
3229
3230 if class == spirv::StorageClass::Output {
3231 BuiltIn::Position
3232 } else {
3233 BuiltIn::FragCoord
3234 }
3235 }
3236 Bi::ViewIndex => {
3237 self.require_any("`view_index` built-in", &[spirv::Capability::MultiView])?;
3238 BuiltIn::ViewIndex
3239 }
3240 Bi::BaseInstance => BuiltIn::BaseInstance,
3242 Bi::BaseVertex => BuiltIn::BaseVertex,
3243 Bi::ClipDistances => {
3244 self.require_any(
3245 "`clip_distances` built-in",
3246 &[spirv::Capability::ClipDistance],
3247 )?;
3248 BuiltIn::ClipDistance
3249 }
3250 Bi::CullDistance => {
3251 self.require_any(
3252 "`cull_distance` built-in",
3253 &[spirv::Capability::CullDistance],
3254 )?;
3255 BuiltIn::CullDistance
3256 }
3257 Bi::InstanceIndex => match stage {
3258 crate::ShaderStage::AnyHit | crate::ShaderStage::ClosestHit => {
3259 BuiltIn::InstanceId
3260 }
3261 _ => BuiltIn::InstanceIndex,
3262 },
3263 Bi::PointSize => BuiltIn::PointSize,
3264 Bi::VertexIndex => BuiltIn::VertexIndex,
3265 Bi::DrawIndex => {
3266 self.use_extension("SPV_KHR_shader_draw_parameters");
3267 self.require_any(
3268 "`draw_index built-in",
3269 &[spirv::Capability::DrawParameters],
3270 )?;
3271 BuiltIn::DrawIndex
3272 }
3273 Bi::FragDepth => BuiltIn::FragDepth,
3275 Bi::PointCoord => BuiltIn::PointCoord,
3276 Bi::FrontFacing => BuiltIn::FrontFacing,
3277 Bi::PrimitiveIndex => {
3278 let enabled_by: &[spirv::Capability] = match stage {
3284 crate::ShaderStage::AnyHit | crate::ShaderStage::ClosestHit => &[],
3286 crate::ShaderStage::Mesh => &[],
3288 crate::ShaderStage::Fragment => &[
3291 spirv::Capability::Geometry,
3292 spirv::Capability::Tessellation,
3293 spirv::Capability::MeshShadingEXT,
3294 ],
3295 _ => return Err(Error::Validation(
3297 "`primitive_index` built-in is not allowed in this shader stage",
3298 )),
3299 };
3300 self.require_any("`primitive_index` built-in", enabled_by)?;
3301
3302 if stage == crate::ShaderStage::Mesh {
3303 others.push(Decoration::PerPrimitiveEXT);
3304 }
3305
3306 BuiltIn::PrimitiveId
3307 }
3308 Bi::Barycentric { perspective } => {
3309 self.require_any(
3310 "`barycentric` built-in",
3311 &[spirv::Capability::FragmentBarycentricKHR],
3312 )?;
3313 self.use_extension("SPV_KHR_fragment_shader_barycentric");
3314 if perspective {
3315 BuiltIn::BaryCoordKHR
3316 } else {
3317 BuiltIn::BaryCoordNoPerspKHR
3318 }
3319 }
3320 Bi::SampleIndex => {
3321 self.require_any(
3322 "`sample_index` built-in",
3323 &[spirv::Capability::SampleRateShading],
3324 )?;
3325
3326 BuiltIn::SampleId
3327 }
3328 Bi::SampleMask => BuiltIn::SampleMask,
3329 Bi::GlobalInvocationId => BuiltIn::GlobalInvocationId,
3331 Bi::LocalInvocationId => BuiltIn::LocalInvocationId,
3332 Bi::LocalInvocationIndex => BuiltIn::LocalInvocationIndex,
3333 Bi::WorkGroupId => BuiltIn::WorkgroupId,
3334 Bi::WorkGroupSize => BuiltIn::WorkgroupSize,
3335 Bi::NumWorkGroups => BuiltIn::NumWorkgroups,
3336 Bi::NumSubgroups => {
3338 self.require_any(
3339 "`num_subgroups` built-in",
3340 &[spirv::Capability::GroupNonUniform],
3341 )?;
3342 BuiltIn::NumSubgroups
3343 }
3344 Bi::SubgroupId => {
3345 self.require_any(
3346 "`subgroup_id` built-in",
3347 &[spirv::Capability::GroupNonUniform],
3348 )?;
3349 BuiltIn::SubgroupId
3350 }
3351 Bi::SubgroupSize => {
3352 self.require_any(
3353 "`subgroup_size` built-in",
3354 &[
3355 spirv::Capability::GroupNonUniform,
3356 spirv::Capability::SubgroupBallotKHR,
3357 ],
3358 )?;
3359 BuiltIn::SubgroupSize
3360 }
3361 Bi::SubgroupInvocationId => {
3362 self.require_any(
3363 "`subgroup_invocation_id` built-in",
3364 &[
3365 spirv::Capability::GroupNonUniform,
3366 spirv::Capability::SubgroupBallotKHR,
3367 ],
3368 )?;
3369 BuiltIn::SubgroupLocalInvocationId
3370 }
3371 Bi::CullPrimitive => {
3372 others.push(Decoration::PerPrimitiveEXT);
3373 BuiltIn::CullPrimitiveEXT
3374 }
3375 Bi::PointIndex => BuiltIn::PrimitivePointIndicesEXT,
3376 Bi::LineIndices => BuiltIn::PrimitiveLineIndicesEXT,
3377 Bi::TriangleIndices => BuiltIn::PrimitiveTriangleIndicesEXT,
3378 Bi::MeshTaskSize => return Ok(BindingDecorations::None),
3380 Bi::VertexCount | Bi::Vertices | Bi::PrimitiveCount | Bi::Primitives => {
3382 unreachable!()
3383 }
3384 Bi::RayInvocationId => BuiltIn::LaunchIdKHR,
3386 Bi::NumRayInvocations => BuiltIn::LaunchSizeKHR,
3387 Bi::InstanceCustomData => BuiltIn::InstanceCustomIndexKHR,
3388 Bi::GeometryIndex => BuiltIn::RayGeometryIndexKHR,
3389 Bi::WorldRayOrigin => BuiltIn::WorldRayOriginKHR,
3390 Bi::WorldRayDirection => BuiltIn::WorldRayDirectionKHR,
3391 Bi::ObjectRayOrigin => BuiltIn::ObjectRayOriginKHR,
3392 Bi::ObjectRayDirection => BuiltIn::ObjectRayDirectionKHR,
3393 Bi::RayTmin => BuiltIn::RayTminKHR,
3394 Bi::RayTCurrentMax => BuiltIn::RayTmaxKHR,
3395 Bi::ObjectToWorld => BuiltIn::ObjectToWorldKHR,
3396 Bi::WorldToObject => BuiltIn::WorldToObjectKHR,
3397 Bi::HitKind => BuiltIn::HitKindKHR,
3398 Bi::HitBarycentrics => return Ok(BindingDecorations::None),
3400 };
3401
3402 use crate::ScalarKind as Sk;
3403
3404 if class == spirv::StorageClass::Input && stage == crate::ShaderStage::Fragment {
3410 let is_flat = match ir_module.types[ty].inner {
3411 crate::TypeInner::Scalar(scalar)
3412 | crate::TypeInner::Vector { scalar, .. } => match scalar.kind {
3413 Sk::Uint | Sk::Sint | Sk::Bool => true,
3414 Sk::Float => false,
3415 Sk::AbstractInt | Sk::AbstractFloat => {
3416 return Err(Error::Validation(
3417 "Abstract types should not appear in IR presented to backends",
3418 ))
3419 }
3420 },
3421 _ => false,
3422 };
3423
3424 if is_flat {
3425 others.push(Decoration::Flat);
3426 }
3427 }
3428 Ok(BindingDecorations::BuiltIn(built_in, others))
3429 }
3430 }
3431 }
3432
3433 pub(super) fn load_io_with_f16_polyfill(
3436 &mut self,
3437 body: &mut Vec<Instruction>,
3438 varying_id: Word,
3439 target_type_id: Word,
3440 ) -> Word {
3441 let tmp = self.id_gen.next();
3442 if let Some(f32_ty) = self.io_f16_polyfills.get_f32_io_type(varying_id) {
3443 body.push(Instruction::load(f32_ty, tmp, varying_id, None));
3444 let converted = self.id_gen.next();
3445 super::f16_polyfill::F16IoPolyfill::emit_f32_to_f16_conversion(
3446 tmp,
3447 target_type_id,
3448 converted,
3449 body,
3450 );
3451 converted
3452 } else {
3453 body.push(Instruction::load(target_type_id, tmp, varying_id, None));
3454 tmp
3455 }
3456 }
3457
3458 pub(super) fn store_io_with_f16_polyfill(
3460 &mut self,
3461 body: &mut Vec<Instruction>,
3462 varying_id: Word,
3463 value_id: Word,
3464 ) {
3465 if let Some(f32_ty) = self.io_f16_polyfills.get_f32_io_type(varying_id) {
3466 let converted = self.id_gen.next();
3467 super::f16_polyfill::F16IoPolyfill::emit_f16_to_f32_conversion(
3468 value_id, f32_ty, converted, body,
3469 );
3470 body.push(Instruction::store(varying_id, converted, None));
3471 } else {
3472 body.push(Instruction::store(varying_id, value_id, None));
3473 }
3474 }
3475
3476 fn write_global_variable(
3477 &mut self,
3478 ir_module: &crate::Module,
3479 global_variable: &crate::GlobalVariable,
3480 ) -> Result<Word, Error> {
3481 use spirv::Decoration;
3482
3483 let id = self.id_gen.next();
3484 let class = map_storage_class(global_variable.space);
3485
3486 if let crate::AddressSpace::RayPayload | crate::AddressSpace::IncomingRayPayload =
3487 global_variable.space
3488 {
3489 self.require_any("ray tracing pipelines", &[spirv::Capability::RayTracingKHR])?;
3490 }
3491
3492 if global_variable
3495 .memory_decorations
3496 .contains(crate::MemoryDecorations::COHERENT)
3497 {
3498 self.decorate(id, Decoration::Coherent, &[]);
3499 }
3500 if global_variable
3501 .memory_decorations
3502 .contains(crate::MemoryDecorations::VOLATILE)
3503 {
3504 self.decorate(id, Decoration::Volatile, &[]);
3505 }
3506
3507 if self.flags.contains(WriterFlags::DEBUG) {
3508 if let Some(ref name) = global_variable.name {
3509 self.debugs.push(Instruction::name(id, name));
3510 }
3511 }
3512
3513 let storage_access = match global_variable.space {
3514 crate::AddressSpace::Storage { access } => Some(access),
3515 _ => match ir_module.types[global_variable.ty].inner {
3516 crate::TypeInner::Image {
3517 class: crate::ImageClass::Storage { access, .. },
3518 ..
3519 } => Some(access),
3520 _ => None,
3521 },
3522 };
3523 if let Some(storage_access) = storage_access {
3524 if !storage_access.contains(crate::StorageAccess::LOAD) {
3525 self.decorate(id, Decoration::NonReadable, &[]);
3526 }
3527 if !storage_access.contains(crate::StorageAccess::STORE) {
3528 self.decorate(id, Decoration::NonWritable, &[]);
3529 }
3530 }
3531
3532 let mut substitute_inner_type_lookup = None;
3536 if let Some(ref res_binding) = global_variable.binding {
3537 let bind_target = self.resolve_resource_binding(res_binding)?;
3538 self.decorate(id, Decoration::DescriptorSet, &[bind_target.descriptor_set]);
3539 self.decorate(id, Decoration::Binding, &[bind_target.binding]);
3540
3541 if let Some(remapped_binding_array_size) = bind_target.binding_array_size {
3542 if let crate::TypeInner::BindingArray { base, .. } =
3543 ir_module.types[global_variable.ty].inner
3544 {
3545 let binding_array_type_id =
3546 self.get_type_id(LookupType::Local(LocalType::BindingArray {
3547 base,
3548 size: remapped_binding_array_size,
3549 }));
3550 substitute_inner_type_lookup = Some(LookupType::Local(LocalType::Pointer {
3551 base: binding_array_type_id,
3552 class,
3553 }));
3554 }
3555 }
3556 };
3557
3558 let init_word = global_variable
3559 .init
3560 .map(|constant| self.constant_ids[constant]);
3561 let inner_type_id = self.get_type_id(
3562 substitute_inner_type_lookup.unwrap_or(LookupType::Handle(global_variable.ty)),
3563 );
3564
3565 let pointer_type_id = if global_needs_wrapper(ir_module, global_variable) {
3567 let wrapper_type_id = self.id_gen.next();
3568
3569 self.decorate(wrapper_type_id, Decoration::Block, &[]);
3570
3571 match self.std140_compat_uniform_types.get(&global_variable.ty) {
3572 Some(std140_type_info) if global_variable.space == crate::AddressSpace::Uniform => {
3573 self.annotations.push(Instruction::member_decorate(
3574 wrapper_type_id,
3575 0,
3576 Decoration::Offset,
3577 &[0],
3578 ));
3579 Instruction::type_struct(wrapper_type_id, &[std140_type_info.type_id])
3580 .to_words(&mut self.logical_layout.declarations);
3581 }
3582 _ => {
3583 let member = crate::StructMember {
3584 name: None,
3585 ty: global_variable.ty,
3586 binding: None,
3587 offset: 0,
3588 };
3589 self.decorate_struct_member(wrapper_type_id, 0, &member, &ir_module.types)?;
3590
3591 Instruction::type_struct(wrapper_type_id, &[inner_type_id])
3592 .to_words(&mut self.logical_layout.declarations);
3593 }
3594 }
3595
3596 let pointer_type_id = self.id_gen.next();
3597 Instruction::type_pointer(pointer_type_id, class, wrapper_type_id)
3598 .to_words(&mut self.logical_layout.declarations);
3599
3600 pointer_type_id
3601 } else {
3602 if let crate::AddressSpace::Storage { .. } = global_variable.space {
3608 match ir_module.types[global_variable.ty].inner {
3609 crate::TypeInner::BindingArray { base, .. } => {
3610 let ty = &ir_module.types[base];
3611 let mut should_decorate = true;
3612 if let crate::TypeInner::Struct { ref members, .. } = ty.inner {
3616 if let Some(last_member) = members.last() {
3618 if let &crate::TypeInner::Array {
3619 size: crate::ArraySize::Dynamic,
3620 ..
3621 } = &ir_module.types[last_member.ty].inner
3622 {
3623 should_decorate = false;
3624 }
3625 }
3626 }
3627 if should_decorate {
3628 let decorated_id = self.get_handle_type_id(base);
3629 self.decorate(decorated_id, Decoration::Block, &[]);
3630 }
3631 }
3632 _ => (),
3633 };
3634 }
3635 if substitute_inner_type_lookup.is_some() {
3636 inner_type_id
3637 } else {
3638 self.get_handle_pointer_type_id(global_variable.ty, class)
3639 }
3640 };
3641
3642 let init_word = match (global_variable.space, self.zero_initialize_workgroup_memory) {
3643 (crate::AddressSpace::Private, _)
3644 | (crate::AddressSpace::WorkGroup, super::ZeroInitializeWorkgroupMemoryMode::Native) => {
3645 init_word.or_else(|| Some(self.get_constant_null(inner_type_id)))
3646 }
3647 _ => init_word,
3648 };
3649
3650 Instruction::variable(pointer_type_id, id, class, init_word)
3651 .to_words(&mut self.logical_layout.declarations);
3652 Ok(id)
3653 }
3654
3655 fn decorate_struct_member(
3660 &mut self,
3661 struct_id: Word,
3662 index: usize,
3663 member: &crate::StructMember,
3664 arena: &UniqueArena<crate::Type>,
3665 ) -> Result<(), Error> {
3666 use spirv::Decoration;
3667
3668 self.annotations.push(Instruction::member_decorate(
3669 struct_id,
3670 index as u32,
3671 Decoration::Offset,
3672 &[member.offset],
3673 ));
3674
3675 if self.flags.contains(WriterFlags::DEBUG) {
3676 if let Some(ref name) = member.name {
3677 self.debugs
3678 .push(Instruction::member_name(struct_id, index as u32, name));
3679 }
3680 }
3681
3682 let mut member_array_subty_inner = &arena[member.ty].inner;
3685 while let crate::TypeInner::Array { base, .. } = *member_array_subty_inner {
3686 member_array_subty_inner = &arena[base].inner;
3687 }
3688
3689 if let crate::TypeInner::Matrix {
3690 columns: _,
3691 rows,
3692 scalar,
3693 } = *member_array_subty_inner
3694 {
3695 let byte_stride = Alignment::from(rows) * scalar.width as u32;
3696 self.annotations.push(Instruction::member_decorate(
3697 struct_id,
3698 index as u32,
3699 Decoration::ColMajor,
3700 &[],
3701 ));
3702 self.annotations.push(Instruction::member_decorate(
3703 struct_id,
3704 index as u32,
3705 Decoration::MatrixStride,
3706 &[byte_stride],
3707 ));
3708 }
3709
3710 Ok(())
3711 }
3712
3713 pub(super) fn get_function_type(&mut self, lookup_function_type: LookupFunctionType) -> Word {
3714 match self
3715 .lookup_function_type
3716 .entry(lookup_function_type.clone())
3717 {
3718 Entry::Occupied(e) => *e.get(),
3719 Entry::Vacant(_) => {
3720 let id = self.id_gen.next();
3721 let instruction = Instruction::type_function(
3722 id,
3723 lookup_function_type.return_type_id,
3724 &lookup_function_type.parameter_type_ids,
3725 );
3726 instruction.to_words(&mut self.logical_layout.declarations);
3727 self.lookup_function_type.insert(lookup_function_type, id);
3728 id
3729 }
3730 }
3731 }
3732
3733 const fn write_physical_layout(&mut self) {
3734 self.physical_layout.bound = self.id_gen.0 + 1;
3735 }
3736
3737 fn write_logical_layout(
3738 &mut self,
3739 ir_module: &crate::Module,
3740 mod_info: &ModuleInfo,
3741 ep_index: Option<usize>,
3742 debug_info: &Option<DebugInfo>,
3743 ) -> Result<(), Error> {
3744 fn has_view_index_check(
3745 ir_module: &crate::Module,
3746 binding: Option<&crate::Binding>,
3747 ty: Handle<crate::Type>,
3748 ) -> bool {
3749 match ir_module.types[ty].inner {
3750 crate::TypeInner::Struct { ref members, .. } => members.iter().any(|member| {
3751 has_view_index_check(ir_module, member.binding.as_ref(), member.ty)
3752 }),
3753 _ => binding == Some(&crate::Binding::BuiltIn(crate::BuiltIn::ViewIndex)),
3754 }
3755 }
3756
3757 let has_storage_buffers =
3758 ir_module
3759 .global_variables
3760 .iter()
3761 .any(|(_, var)| match var.space {
3762 crate::AddressSpace::Storage { .. } => true,
3763 _ => false,
3764 });
3765 let has_view_index = ir_module
3766 .entry_points
3767 .iter()
3768 .flat_map(|entry| entry.function.arguments.iter())
3769 .any(|arg| has_view_index_check(ir_module, arg.binding.as_ref(), arg.ty));
3770 let has_vertex_return = ir_module.special_types.ray_vertex_return.is_some();
3771
3772 let rt_uses = ir_module.uses_ray_tracing(ep_index);
3773 let has_ray_query = rt_uses.queries;
3774 let has_ray_tracing_pipeline = rt_uses.pipelines;
3775
3776 self.has_ray_tracing_pipeline = has_ray_tracing_pipeline;
3777
3778 if self.physical_layout.version < 0x10300 && has_storage_buffers {
3779 Instruction::extension("SPV_KHR_storage_buffer_storage_class")
3781 .to_words(&mut self.logical_layout.extensions);
3782 }
3783 if has_view_index {
3784 Instruction::extension("SPV_KHR_multiview")
3785 .to_words(&mut self.logical_layout.extensions)
3786 }
3787 if has_ray_query {
3788 Instruction::extension("SPV_KHR_ray_query")
3789 .to_words(&mut self.logical_layout.extensions)
3790 }
3791 if has_vertex_return {
3792 Instruction::extension("SPV_KHR_ray_tracing_position_fetch")
3793 .to_words(&mut self.logical_layout.extensions);
3794 }
3795 if ir_module.uses_mesh_shaders() {
3796 self.use_extension("SPV_EXT_mesh_shader");
3797 self.require_any("Mesh Shaders", &[spirv::Capability::MeshShadingEXT])?;
3798 let lang_version = self.lang_version();
3799 if lang_version.0 <= 1 && lang_version.1 < 4 {
3800 return Err(Error::SpirvVersionTooLow(1, 4));
3801 }
3802 }
3803 if has_ray_tracing_pipeline {
3804 let lang_version = self.lang_version();
3810 if lang_version.0 <= 1 && lang_version.1 < 4 {
3811 return Err(Error::SpirvVersionTooLow(1, 4));
3812 }
3813 Instruction::extension("SPV_KHR_ray_tracing")
3814 .to_words(&mut self.logical_layout.extensions)
3815 }
3816 Instruction::type_void(self.void_type).to_words(&mut self.logical_layout.declarations);
3817 Instruction::ext_inst_import(self.gl450_ext_inst_id, "GLSL.std.450")
3818 .to_words(&mut self.logical_layout.ext_inst_imports);
3819
3820 let mut debug_info_inner = None;
3821 if self.flags.contains(WriterFlags::DEBUG) {
3822 if let Some(debug_info) = debug_info.as_ref() {
3823 let source_file_id = self.id_gen.next();
3824 self.debugs
3825 .push(Instruction::string(debug_info.file_name, source_file_id));
3826
3827 debug_info_inner = Some(DebugInfoInner {
3828 source_code: debug_info.source_code,
3829 source_file_id,
3830 });
3831 self.debugs.append(&mut Instruction::source_auto_continued(
3832 debug_info.language,
3833 0,
3834 &debug_info_inner,
3835 ));
3836 }
3837 }
3838
3839 for (handle, _) in ir_module.types.iter() {
3841 self.write_type_declaration_arena(ir_module, handle)?;
3842 }
3843
3844 for (_, var) in ir_module.global_variables.iter() {
3846 if var.space == crate::AddressSpace::Uniform {
3847 self.write_std140_compat_type_declaration(ir_module, var.ty)?;
3848 }
3849 }
3850
3851 self.constant_ids
3853 .resize(ir_module.global_expressions.len(), 0);
3854 for (handle, _) in ir_module.global_expressions.iter() {
3855 self.write_constant_expr(handle, ir_module, mod_info)?;
3856 }
3857 debug_assert!(self.constant_ids.iter().all(|&id| id != 0));
3858
3859 if self.flags.contains(WriterFlags::DEBUG) {
3861 for (_, constant) in ir_module.constants.iter() {
3862 if let Some(ref name) = constant.name {
3863 let id = self.constant_ids[constant.init];
3864 self.debugs.push(Instruction::name(id, name));
3865 }
3866 }
3867 }
3868
3869 for (handle, var) in ir_module.global_variables.iter() {
3871 let gvar = match ep_index {
3875 Some(index) if mod_info.get_entry_point(index)[handle].is_empty() => {
3876 GlobalVariable::dummy()
3877 }
3878 _ => {
3879 let id = self.write_global_variable(ir_module, var)?;
3880 GlobalVariable::new(id)
3881 }
3882 };
3883 self.global_variables.insert(handle, gvar);
3884 }
3885
3886 for (handle, ir_function) in ir_module.functions.iter() {
3888 let info = &mod_info[handle];
3889 if let Some(index) = ep_index {
3890 let ep_info = mod_info.get_entry_point(index);
3891 if !ep_info.dominates_global_use(info) {
3895 log::debug!("Skip function {:?}", ir_function.name);
3896 continue;
3897 }
3898
3899 if !info.available_stages.contains(ep_info.available_stages) {
3909 continue;
3910 }
3911 }
3912 let id = self.write_function(ir_function, info, ir_module, None, &debug_info_inner)?;
3913 self.lookup_function.insert(handle, id);
3914 }
3915
3916 for (index, ir_ep) in ir_module.entry_points.iter().enumerate() {
3918 if ep_index.is_some() && ep_index != Some(index) {
3919 continue;
3920 }
3921 let info = mod_info.get_entry_point(index);
3922 let ep_instruction =
3923 self.write_entry_point(ir_ep, info, ir_module, &debug_info_inner)?;
3924 ep_instruction.to_words(&mut self.logical_layout.entry_points);
3925 }
3926
3927 for capability in self.capabilities_used.iter() {
3928 Instruction::capability(*capability).to_words(&mut self.logical_layout.capabilities);
3929 }
3930 for extension in self.extensions_used.iter() {
3931 Instruction::extension(extension).to_words(&mut self.logical_layout.extensions);
3932 }
3933 if ir_module.entry_points.is_empty() {
3934 Instruction::capability(spirv::Capability::Linkage)
3936 .to_words(&mut self.logical_layout.capabilities);
3937 }
3938
3939 let addressing_model = spirv::AddressingModel::Logical;
3940 let memory_model = if self
3941 .capabilities_used
3942 .contains(&spirv::Capability::VulkanMemoryModel)
3943 {
3944 spirv::MemoryModel::Vulkan
3945 } else {
3946 spirv::MemoryModel::GLSL450
3947 };
3948 Instruction::memory_model(addressing_model, memory_model)
3952 .to_words(&mut self.logical_layout.memory_model);
3953
3954 for debug_string in self.debug_strings.iter() {
3955 debug_string.to_words(&mut self.logical_layout.debugs);
3956 }
3957
3958 if self.flags.contains(WriterFlags::DEBUG) {
3959 for debug in self.debugs.iter() {
3960 debug.to_words(&mut self.logical_layout.debugs);
3961 }
3962 }
3963
3964 for annotation in self.annotations.iter() {
3965 annotation.to_words(&mut self.logical_layout.annotations);
3966 }
3967
3968 Ok(())
3969 }
3970
3971 pub fn write(
3972 &mut self,
3973 ir_module: &crate::Module,
3974 info: &ModuleInfo,
3975 pipeline_options: Option<&PipelineOptions>,
3976 debug_info: &Option<DebugInfo>,
3977 words: &mut Vec<Word>,
3978 ) -> Result<(), Error> {
3979 self.reset();
3980
3981 let ep_index = match pipeline_options {
3983 Some(po) => {
3984 let index = ir_module
3985 .entry_points
3986 .iter()
3987 .position(|ep| po.shader_stage == ep.stage && po.entry_point == ep.name)
3988 .ok_or(Error::EntryPointNotFound)?;
3989 Some(index)
3990 }
3991 None => None,
3992 };
3993
3994 self.write_logical_layout(ir_module, info, ep_index, debug_info)?;
3995 self.write_physical_layout();
3996
3997 self.physical_layout.in_words(words);
3998 self.logical_layout.in_words(words);
3999 Ok(())
4000 }
4001
4002 pub const fn get_capabilities_used(&self) -> &crate::FastIndexSet<spirv::Capability> {
4004 &self.capabilities_used
4005 }
4006
4007 pub fn decorate_non_uniform_binding_array_access(&mut self, id: Word) -> Result<(), Error> {
4008 self.require_any("NonUniformEXT", &[spirv::Capability::ShaderNonUniform])?;
4009 self.use_extension("SPV_EXT_descriptor_indexing");
4010 self.decorate(id, spirv::Decoration::NonUniform, &[]);
4011 Ok(())
4012 }
4013
4014 pub(super) fn needs_f16_polyfill(&self, ty_inner: &crate::TypeInner) -> bool {
4015 self.io_f16_polyfills.needs_polyfill(ty_inner)
4016 }
4017
4018 pub(super) fn write_debug_printf(
4019 &mut self,
4020 block: &mut Block,
4021 string: &str,
4022 format_params: &[Word],
4023 ) {
4024 if self.debug_printf.is_none() {
4025 self.use_extension("SPV_KHR_non_semantic_info");
4026 let import_id = self.id_gen.next();
4027 Instruction::ext_inst_import(import_id, "NonSemantic.DebugPrintf")
4028 .to_words(&mut self.logical_layout.ext_inst_imports);
4029 self.debug_printf = Some(import_id)
4030 }
4031
4032 let import_id = self.debug_printf.unwrap();
4033
4034 let string_id = self.id_gen.next();
4035 self.debug_strings
4036 .push(Instruction::string(string, string_id));
4037
4038 let mut operands = Vec::with_capacity(1 + format_params.len());
4039 operands.push(string_id);
4040 operands.extend(format_params.iter());
4041
4042 let print_id = self.id_gen.next();
4043 block.body.push(Instruction::ext_inst(
4044 import_id,
4045 1,
4046 self.void_type,
4047 print_id,
4048 &operands,
4049 ));
4050 }
4051}
4052
4053#[test]
4054fn test_write_physical_layout() {
4055 let mut writer = Writer::new(&Options::default()).unwrap();
4056 assert_eq!(writer.physical_layout.bound, 0);
4057 writer.write_physical_layout();
4058 assert_eq!(writer.physical_layout.bound, 3);
4059}