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