1use spirv::Word;
6
7use super::{
8 selection::{MergeTuple, Selection},
9 Block, BlockContext, Error, IdGenerator, Instruction, LocalType, LookupType, NumericType,
10};
11use crate::arena::Handle;
12
13struct ImageCoordinates {
24 value_id: Word,
28
29 type_id: Word,
31
32 size: Option<crate::VectorSize>,
35}
36
37trait Access {
52 type Output: MergeTuple + Copy + Clone;
61
62 fn generate(
75 &self,
76 id_gen: &mut IdGenerator,
77 coordinates_id: Word,
78 level_id: Option<Word>,
79 sample_id: Option<Word>,
80 block: &mut Block,
81 ) -> Self::Output;
82
83 fn result_type(&self) -> Self::Output;
87
88 fn out_of_bounds_value(&self, ctx: &mut BlockContext<'_>) -> Self::Output;
92}
93
94struct Load {
98 opcode: spirv::Op,
101
102 type_id: Word,
104
105 image_id: Word,
107}
108
109impl Load {
110 fn from_image_expr(
111 ctx: &mut BlockContext<'_>,
112 image_id: Word,
113 image_class: crate::ImageClass,
114 result_type_id: Word,
115 ) -> Result<Load, Error> {
116 let opcode = match image_class {
117 crate::ImageClass::Storage { .. } => spirv::Op::ImageRead,
118 crate::ImageClass::Depth { .. } | crate::ImageClass::Sampled { .. } => {
119 spirv::Op::ImageFetch
120 }
121 };
122
123 let type_id = match image_class {
129 crate::ImageClass::Depth { .. } => ctx.get_numeric_type_id(NumericType::Vector {
130 size: crate::VectorSize::Quad,
131 scalar: crate::Scalar::F32,
132 }),
133 _ => result_type_id,
134 };
135
136 Ok(Load {
137 opcode,
138 type_id,
139 image_id,
140 })
141 }
142}
143
144impl Access for Load {
145 type Output = Word;
146
147 fn generate(
149 &self,
150 id_gen: &mut IdGenerator,
151 coordinates_id: Word,
152 level_id: Option<Word>,
153 sample_id: Option<Word>,
154 block: &mut Block,
155 ) -> Word {
156 let texel_id = id_gen.next();
157 let mut instruction = Instruction::image_fetch_or_read(
158 self.opcode,
159 self.type_id,
160 texel_id,
161 self.image_id,
162 coordinates_id,
163 );
164
165 match (level_id, sample_id) {
166 (None, None) => {}
167 (Some(level_id), None) => {
168 instruction.add_operand(spirv::ImageOperands::LOD.bits());
169 instruction.add_operand(level_id);
170 }
171 (None, Some(sample_id)) => {
172 instruction.add_operand(spirv::ImageOperands::SAMPLE.bits());
173 instruction.add_operand(sample_id);
174 }
175 (Some(_), Some(_)) => unreachable!(),
177 }
178
179 block.body.push(instruction);
180
181 texel_id
182 }
183
184 fn result_type(&self) -> Word {
185 self.type_id
186 }
187
188 fn out_of_bounds_value(&self, ctx: &mut BlockContext<'_>) -> Word {
189 ctx.writer.get_constant_null(self.type_id)
190 }
191}
192
193struct Store {
197 image_id: Word,
199
200 value_id: Word,
202}
203
204impl Access for Store {
205 type Output = ();
207
208 fn generate(
209 &self,
210 _id_gen: &mut IdGenerator,
211 coordinates_id: Word,
212 _level_id: Option<Word>,
213 _sample_id: Option<Word>,
214 block: &mut Block,
215 ) {
216 block.body.push(Instruction::image_write(
217 self.image_id,
218 coordinates_id,
219 self.value_id,
220 ));
221 }
222
223 fn result_type(&self) {}
225
226 fn out_of_bounds_value(&self, _ctx: &mut BlockContext<'_>) {}
228}
229
230impl BlockContext<'_> {
231 fn write_image_coordinates(
258 &mut self,
259 coordinates: Handle<crate::Expression>,
260 array_index: Option<Handle<crate::Expression>>,
261 block: &mut Block,
262 ) -> Result<ImageCoordinates, Error> {
263 use crate::TypeInner as Ti;
264 use crate::VectorSize as Vs;
265
266 let coordinates_id = self.cached[coordinates];
267 let ty = &self.fun_info[coordinates].ty;
268 let inner_ty = ty.inner_with(&self.ir_module.types);
269
270 let array_index = match array_index {
273 None => {
274 let value_id = coordinates_id;
275 let type_id = self.get_expression_type_id(ty);
276 let size = match *inner_ty {
277 Ti::Scalar { .. } => None,
278 Ti::Vector { size, .. } => Some(size),
279 _ => return Err(Error::Validation("coordinate type")),
280 };
281 return Ok(ImageCoordinates {
282 value_id,
283 type_id,
284 size,
285 });
286 }
287 Some(ix) => ix,
288 };
289
290 let (component_scalar, size) = match *inner_ty {
293 Ti::Scalar(scalar @ crate::Scalar { width: 4, .. }) => (scalar, Vs::Bi),
294 Ti::Vector {
295 scalar: scalar @ crate::Scalar { width: 4, .. },
296 size: Vs::Bi,
297 } => (scalar, Vs::Tri),
298 Ti::Vector {
299 scalar: scalar @ crate::Scalar { width: 4, .. },
300 size: Vs::Tri,
301 } => (scalar, Vs::Quad),
302 Ti::Vector { size: Vs::Quad, .. } => {
303 return Err(Error::Validation("extending vec4 coordinate"));
304 }
305 ref other => {
306 log::error!("wrong coordinate type {:?}", other);
307 return Err(Error::Validation("coordinate type"));
308 }
309 };
310
311 let array_index_id = self.cached[array_index];
313 let ty = &self.fun_info[array_index].ty;
314 let inner_ty = ty.inner_with(&self.ir_module.types);
315 let array_index_scalar = match *inner_ty {
316 Ti::Scalar(
317 scalar @ crate::Scalar {
318 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
319 width: 4,
320 },
321 ) => scalar,
322 _ => unreachable!("we only allow i32 and u32"),
323 };
324 let cast = match (component_scalar.kind, array_index_scalar.kind) {
325 (crate::ScalarKind::Sint, crate::ScalarKind::Sint)
326 | (crate::ScalarKind::Uint, crate::ScalarKind::Uint) => None,
327 (crate::ScalarKind::Sint, crate::ScalarKind::Uint)
328 | (crate::ScalarKind::Uint, crate::ScalarKind::Sint) => Some(spirv::Op::Bitcast),
329 (crate::ScalarKind::Float, crate::ScalarKind::Sint) => Some(spirv::Op::ConvertSToF),
330 (crate::ScalarKind::Float, crate::ScalarKind::Uint) => Some(spirv::Op::ConvertUToF),
331 (crate::ScalarKind::Bool, _) => unreachable!("we don't allow bool for component"),
332 (_, crate::ScalarKind::Bool | crate::ScalarKind::Float) => {
333 unreachable!("we don't allow bool or float for array index")
334 }
335 (crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat, _)
336 | (_, crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat) => {
337 unreachable!("abstract types should never reach backends")
338 }
339 };
340 let reconciled_array_index_id = if let Some(cast) = cast {
341 let component_ty_id = self.get_numeric_type_id(NumericType::Scalar(component_scalar));
342 let reconciled_id = self.gen_id();
343 block.body.push(Instruction::unary(
344 cast,
345 component_ty_id,
346 reconciled_id,
347 array_index_id,
348 ));
349 reconciled_id
350 } else {
351 array_index_id
352 };
353
354 let type_id = self.get_numeric_type_id(NumericType::Vector {
356 size,
357 scalar: component_scalar,
358 });
359
360 let value_id = self.gen_id();
362 block.body.push(Instruction::composite_construct(
363 type_id,
364 value_id,
365 &[coordinates_id, reconciled_array_index_id],
366 ));
367 Ok(ImageCoordinates {
368 value_id,
369 type_id,
370 size: Some(size),
371 })
372 }
373
374 pub(super) fn get_handle_id(&mut self, expr_handle: Handle<crate::Expression>) -> Word {
375 let id = match self.ir_function.expressions[expr_handle] {
376 crate::Expression::GlobalVariable(handle) => {
377 self.writer.global_variables[handle].handle_id
378 }
379 crate::Expression::FunctionArgument(i) => {
380 self.function.parameters[i as usize].handle_id
381 }
382 crate::Expression::Access { .. } | crate::Expression::AccessIndex { .. } => {
383 self.cached[expr_handle]
384 }
385 ref other => unreachable!("Unexpected image expression {:?}", other),
386 };
387
388 if id == 0 {
389 unreachable!(
390 "Image expression {:?} doesn't have a handle ID",
391 expr_handle
392 );
393 }
394
395 id
396 }
397
398 fn write_coordinate_one(&mut self, coordinates: &ImageCoordinates) -> Result<Word, Error> {
403 let one = self.get_scope_constant(1);
404 match coordinates.size {
405 None => Ok(one),
406 Some(vector_size) => {
407 let ones = [one; 4];
408 let id = self.gen_id();
409 Instruction::constant_composite(
410 coordinates.type_id,
411 id,
412 &ones[..vector_size as usize],
413 )
414 .to_words(&mut self.writer.logical_layout.declarations);
415 Ok(id)
416 }
417 }
418 }
419
420 fn restrict_scalar(
426 &mut self,
427 type_id: Word,
428 input_id: Word,
429 size_id: Word,
430 block: &mut Block,
431 ) -> Result<Word, Error> {
432 let i32_one_id = self.get_scope_constant(1);
433
434 let limit_id = self.gen_id();
436 block.body.push(Instruction::binary(
437 spirv::Op::ISub,
438 type_id,
439 limit_id,
440 size_id,
441 i32_one_id,
442 ));
443
444 let restricted_id = self.gen_id();
448 block.body.push(Instruction::ext_inst(
449 self.writer.gl450_ext_inst_id,
450 spirv::GLOp::UMin,
451 type_id,
452 restricted_id,
453 &[input_id, limit_id],
454 ));
455
456 Ok(restricted_id)
457 }
458
459 fn write_coordinate_bounds(
464 &mut self,
465 type_id: Word,
466 image_id: Word,
467 level_id: Option<Word>,
468 block: &mut Block,
469 ) -> Word {
470 let coordinate_bounds_id = self.gen_id();
471 match level_id {
472 Some(level_id) => {
473 let mut inst = Instruction::image_query(
476 spirv::Op::ImageQuerySizeLod,
477 type_id,
478 coordinate_bounds_id,
479 image_id,
480 );
481 inst.add_operand(level_id);
482 block.body.push(inst);
483 }
484 _ => {
485 block.body.push(Instruction::image_query(
487 spirv::Op::ImageQuerySize,
488 type_id,
489 coordinate_bounds_id,
490 image_id,
491 ));
492 }
493 }
494
495 coordinate_bounds_id
496 }
497
498 fn write_restricted_coordinates(
513 &mut self,
514 image_id: Word,
515 coordinates: ImageCoordinates,
516 level_id: Option<Word>,
517 sample_id: Option<Word>,
518 block: &mut Block,
519 ) -> Result<(Word, Option<Word>, Option<Word>), Error> {
520 self.writer.require_any(
521 "the `Restrict` image bounds check policy",
522 &[spirv::Capability::ImageQuery],
523 )?;
524
525 let i32_type_id = self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::I32));
526
527 let level_id = level_id
531 .map(|level_id| {
532 let num_levels_id = self.gen_id();
534 block.body.push(Instruction::image_query(
535 spirv::Op::ImageQueryLevels,
536 i32_type_id,
537 num_levels_id,
538 image_id,
539 ));
540
541 self.restrict_scalar(i32_type_id, level_id, num_levels_id, block)
542 })
543 .transpose()?;
544
545 let sample_id = sample_id
547 .map(|sample_id| {
548 let num_samples_id = self.gen_id();
550 block.body.push(Instruction::image_query(
551 spirv::Op::ImageQuerySamples,
552 i32_type_id,
553 num_samples_id,
554 image_id,
555 ));
556
557 self.restrict_scalar(i32_type_id, sample_id, num_samples_id, block)
558 })
559 .transpose()?;
560
561 let coordinate_bounds_id =
563 self.write_coordinate_bounds(coordinates.type_id, image_id, level_id, block);
564
565 let ones = self.write_coordinate_one(&coordinates)?;
567 let coordinate_limit_id = self.gen_id();
568 block.body.push(Instruction::binary(
569 spirv::Op::ISub,
570 coordinates.type_id,
571 coordinate_limit_id,
572 coordinate_bounds_id,
573 ones,
574 ));
575
576 let restricted_coordinates_id = self.gen_id();
582 block.body.push(Instruction::ext_inst(
583 self.writer.gl450_ext_inst_id,
584 spirv::GLOp::UMin,
585 coordinates.type_id,
586 restricted_coordinates_id,
587 &[coordinates.value_id, coordinate_limit_id],
588 ));
589
590 Ok((restricted_coordinates_id, level_id, sample_id))
591 }
592
593 fn write_conditional_image_access<A: Access>(
594 &mut self,
595 image_id: Word,
596 coordinates: ImageCoordinates,
597 level_id: Option<Word>,
598 sample_id: Option<Word>,
599 block: &mut Block,
600 access: &A,
601 ) -> Result<A::Output, Error> {
602 self.writer.require_any(
603 "the `ReadZeroSkipWrite` image bounds check policy",
604 &[spirv::Capability::ImageQuery],
605 )?;
606
607 let bool_type_id = self.writer.get_bool_type_id();
608 let i32_type_id = self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::I32));
609
610 let null_id = access.out_of_bounds_value(self);
611
612 let mut selection = Selection::start(block, access.result_type());
613
614 if let Some(level_id) = level_id {
618 let num_levels_id = self.gen_id();
620 selection.block().body.push(Instruction::image_query(
621 spirv::Op::ImageQueryLevels,
622 i32_type_id,
623 num_levels_id,
624 image_id,
625 ));
626
627 let lod_cond_id = self.gen_id();
628 selection.block().body.push(Instruction::binary(
629 spirv::Op::ULessThan,
630 bool_type_id,
631 lod_cond_id,
632 level_id,
633 num_levels_id,
634 ));
635
636 selection.if_true(self, lod_cond_id, null_id);
637 }
638
639 if let Some(sample_id) = sample_id {
641 let num_samples_id = self.gen_id();
643 selection.block().body.push(Instruction::image_query(
644 spirv::Op::ImageQuerySamples,
645 i32_type_id,
646 num_samples_id,
647 image_id,
648 ));
649
650 let samples_cond_id = self.gen_id();
651 selection.block().body.push(Instruction::binary(
652 spirv::Op::ULessThan,
653 bool_type_id,
654 samples_cond_id,
655 sample_id,
656 num_samples_id,
657 ));
658
659 selection.if_true(self, samples_cond_id, null_id);
660 }
661
662 let coordinate_bounds_id = self.write_coordinate_bounds(
664 coordinates.type_id,
665 image_id,
666 level_id,
667 selection.block(),
668 );
669
670 let coords_numeric_type = match coordinates.size {
672 Some(size) => NumericType::Vector {
673 size,
674 scalar: crate::Scalar::BOOL,
675 },
676 None => NumericType::Scalar(crate::Scalar::BOOL),
677 };
678 let coords_bool_type_id = self.get_numeric_type_id(coords_numeric_type);
679 let coords_conds_id = self.gen_id();
680 selection.block().body.push(Instruction::binary(
681 spirv::Op::ULessThan,
682 coords_bool_type_id,
683 coords_conds_id,
684 coordinates.value_id,
685 coordinate_bounds_id,
686 ));
687
688 let coords_cond_id = if coords_bool_type_id != bool_type_id {
691 let id = self.gen_id();
692 selection.block().body.push(Instruction::relational(
693 spirv::Op::All,
694 bool_type_id,
695 id,
696 coords_conds_id,
697 ));
698 id
699 } else {
700 coords_conds_id
701 };
702
703 selection.if_true(self, coords_cond_id, null_id);
704
705 let texel_id = access.generate(
707 &mut self.writer.id_gen,
708 coordinates.value_id,
709 level_id,
710 sample_id,
711 selection.block(),
712 );
713
714 Ok(selection.finish(self, texel_id))
716 }
717
718 #[allow(clippy::too_many_arguments)]
722 pub(super) fn write_image_load(
723 &mut self,
724 result_type_id: Word,
725 image: Handle<crate::Expression>,
726 coordinate: Handle<crate::Expression>,
727 array_index: Option<Handle<crate::Expression>>,
728 level: Option<Handle<crate::Expression>>,
729 sample: Option<Handle<crate::Expression>>,
730 block: &mut Block,
731 ) -> Result<Word, Error> {
732 let image_id = self.get_handle_id(image);
733 let image_type = self.fun_info[image].ty.inner_with(&self.ir_module.types);
734 let image_class = match *image_type {
735 crate::TypeInner::Image { class, .. } => class,
736 _ => return Err(Error::Validation("image type")),
737 };
738
739 let access = Load::from_image_expr(self, image_id, image_class, result_type_id)?;
740 let coordinates = self.write_image_coordinates(coordinate, array_index, block)?;
741
742 let level_id = level.map(|expr| self.cached[expr]);
743 let sample_id = sample.map(|expr| self.cached[expr]);
744
745 let access_id = match self.writer.bounds_check_policies.image_load {
747 crate::proc::BoundsCheckPolicy::Restrict => {
748 let (coords, level_id, sample_id) = self.write_restricted_coordinates(
749 image_id,
750 coordinates,
751 level_id,
752 sample_id,
753 block,
754 )?;
755 access.generate(&mut self.writer.id_gen, coords, level_id, sample_id, block)
756 }
757 crate::proc::BoundsCheckPolicy::ReadZeroSkipWrite => self
758 .write_conditional_image_access(
759 image_id,
760 coordinates,
761 level_id,
762 sample_id,
763 block,
764 &access,
765 )?,
766 crate::proc::BoundsCheckPolicy::Unchecked => access.generate(
767 &mut self.writer.id_gen,
768 coordinates.value_id,
769 level_id,
770 sample_id,
771 block,
772 ),
773 };
774
775 let result_id = if result_type_id == access.result_type() {
779 access_id
782 } else {
783 let component_id = self.gen_id();
786 block.body.push(Instruction::composite_extract(
787 result_type_id,
788 component_id,
789 access_id,
790 &[0],
791 ));
792 component_id
793 };
794
795 Ok(result_id)
796 }
797
798 #[allow(clippy::too_many_arguments)]
802 pub(super) fn write_image_sample(
803 &mut self,
804 result_type_id: Word,
805 image: Handle<crate::Expression>,
806 sampler: Handle<crate::Expression>,
807 gather: Option<crate::SwizzleComponent>,
808 coordinate: Handle<crate::Expression>,
809 array_index: Option<Handle<crate::Expression>>,
810 offset: Option<Handle<crate::Expression>>,
811 level: crate::SampleLevel,
812 depth_ref: Option<Handle<crate::Expression>>,
813 clamp_to_edge: bool,
814 block: &mut Block,
815 ) -> Result<Word, Error> {
816 use super::instructions::SampleLod;
817 let image_id = self.get_handle_id(image);
819 let image_type = self.fun_info[image].ty.handle().unwrap();
820 let needs_sub_access = match self.ir_module.types[image_type].inner {
823 crate::TypeInner::Image {
824 class: crate::ImageClass::Depth { .. },
825 ..
826 } => depth_ref.is_none() && gather.is_none(),
827 _ => false,
828 };
829 let sample_result_type_id = if needs_sub_access {
830 self.get_numeric_type_id(NumericType::Vector {
831 size: crate::VectorSize::Quad,
832 scalar: crate::Scalar::F32,
833 })
834 } else {
835 result_type_id
836 };
837
838 let image_type_id = self.get_handle_type_id(image_type);
840 let sampled_image_type_id =
841 self.get_type_id(LookupType::Local(LocalType::SampledImage { image_type_id }));
842
843 let sampler_id = self.get_handle_id(sampler);
844
845 let coordinates = self.write_image_coordinates(coordinate, array_index, block)?;
846 let coordinates_id = if clamp_to_edge {
847 self.writer.require_any(
848 "clamp sample coordinates to edge",
849 &[spirv::Capability::ImageQuery],
850 )?;
851
852 if level != crate::SampleLevel::Zero {
858 return Err(Error::Validation(
859 "ImageSample::clamp_to_edge requires SampleLevel::Zero",
860 ));
861 }
862
863 let image_size_id = self.gen_id();
865 let vec2u_type_id = self.writer.get_vec2u_type_id();
866 let const_zero_uint_id = self.writer.get_constant_scalar(crate::Literal::U32(0));
867 let mut query_inst = Instruction::image_query(
868 spirv::Op::ImageQuerySizeLod,
869 vec2u_type_id,
870 image_size_id,
871 image_id,
872 );
873 query_inst.add_operand(const_zero_uint_id);
874 block.body.push(query_inst);
875
876 let image_size_f_id = self.gen_id();
877 let vec2f_type_id = self.writer.get_vec2f_type_id();
878 block.body.push(Instruction::unary(
879 spirv::Op::ConvertUToF,
880 vec2f_type_id,
881 image_size_f_id,
882 image_size_id,
883 ));
884
885 let const_0_5_f32_id = self.writer.get_constant_scalar(crate::Literal::F32(0.5));
888 let const_0_5_vec2f_id = self.writer.get_constant_composite(
889 LookupType::Local(LocalType::Numeric(NumericType::Vector {
890 size: crate::VectorSize::Bi,
891 scalar: crate::Scalar::F32,
892 })),
893 &[const_0_5_f32_id, const_0_5_f32_id],
894 );
895
896 let margin_left_id = self.gen_id();
897 block.body.push(Instruction::binary(
898 spirv::Op::FDiv,
899 vec2f_type_id,
900 margin_left_id,
901 const_0_5_vec2f_id,
902 image_size_f_id,
903 ));
904
905 let const_1_f32_id = self.writer.get_constant_scalar(crate::Literal::F32(1.0));
906 let const_1_vec2f_id = self.writer.get_constant_composite(
907 LookupType::Local(LocalType::Numeric(NumericType::Vector {
908 size: crate::VectorSize::Bi,
909 scalar: crate::Scalar::F32,
910 })),
911 &[const_1_f32_id, const_1_f32_id],
912 );
913
914 let margin_right_id = self.gen_id();
915 block.body.push(Instruction::binary(
916 spirv::Op::FSub,
917 vec2f_type_id,
918 margin_right_id,
919 const_1_vec2f_id,
920 margin_left_id,
921 ));
922
923 let clamped_coords_id = self.gen_id();
925 block.body.push(Instruction::ext_inst(
926 self.writer.gl450_ext_inst_id,
927 spirv::GLOp::NClamp,
928 vec2f_type_id,
929 clamped_coords_id,
930 &[coordinates.value_id, margin_left_id, margin_right_id],
931 ));
932
933 clamped_coords_id
934 } else {
935 coordinates.value_id
936 };
937
938 let sampled_image_id = self.gen_id();
939 block.body.push(Instruction::sampled_image(
940 sampled_image_type_id,
941 sampled_image_id,
942 image_id,
943 sampler_id,
944 ));
945 let id = self.gen_id();
946
947 let depth_id = depth_ref.map(|handle| self.cached[handle]);
948 let mut mask = spirv::ImageOperands::empty();
949 mask.set(spirv::ImageOperands::CONST_OFFSET, offset.is_some());
950
951 let mut main_instruction = match (level, gather) {
952 (_, Some(component)) => {
953 let component_id = self.get_index_constant(component as u32);
954 let mut inst = Instruction::image_gather(
955 sample_result_type_id,
956 id,
957 sampled_image_id,
958 coordinates_id,
959 component_id,
960 depth_id,
961 );
962 if !mask.is_empty() {
963 inst.add_operand(mask.bits());
964 }
965 inst
966 }
967 (crate::SampleLevel::Zero, None) => {
968 let mut inst = Instruction::image_sample(
969 sample_result_type_id,
970 id,
971 SampleLod::Explicit,
972 sampled_image_id,
973 coordinates_id,
974 depth_id,
975 );
976
977 let zero_id = self.writer.get_constant_scalar(crate::Literal::F32(0.0));
978
979 mask |= spirv::ImageOperands::LOD;
980 inst.add_operand(mask.bits());
981 inst.add_operand(zero_id);
982
983 inst
984 }
985 (crate::SampleLevel::Auto, None) => {
986 let mut inst = Instruction::image_sample(
987 sample_result_type_id,
988 id,
989 SampleLod::Implicit,
990 sampled_image_id,
991 coordinates_id,
992 depth_id,
993 );
994 if !mask.is_empty() {
995 inst.add_operand(mask.bits());
996 }
997 inst
998 }
999 (crate::SampleLevel::Exact(lod_handle), None) => {
1000 let mut inst = Instruction::image_sample(
1001 sample_result_type_id,
1002 id,
1003 SampleLod::Explicit,
1004 sampled_image_id,
1005 coordinates_id,
1006 depth_id,
1007 );
1008
1009 let mut lod_id = self.cached[lod_handle];
1010 if matches!(
1014 self.ir_module.types[image_type].inner,
1015 crate::TypeInner::Image {
1016 class: crate::ImageClass::Depth { .. },
1017 ..
1018 }
1019 ) {
1020 let lod_f32_id = self.gen_id();
1021 let f32_type_id =
1022 self.get_numeric_type_id(NumericType::Scalar(crate::Scalar::F32));
1023 let convert_op = match *self.fun_info[lod_handle]
1024 .ty
1025 .inner_with(&self.ir_module.types)
1026 {
1027 crate::TypeInner::Scalar(crate::Scalar {
1028 kind: crate::ScalarKind::Uint,
1029 width: 4,
1030 }) => spirv::Op::ConvertUToF,
1031 crate::TypeInner::Scalar(crate::Scalar {
1032 kind: crate::ScalarKind::Sint,
1033 width: 4,
1034 }) => spirv::Op::ConvertSToF,
1035 _ => unreachable!(),
1036 };
1037 block.body.push(Instruction::unary(
1038 convert_op,
1039 f32_type_id,
1040 lod_f32_id,
1041 lod_id,
1042 ));
1043 lod_id = lod_f32_id;
1044 }
1045 mask |= spirv::ImageOperands::LOD;
1046 inst.add_operand(mask.bits());
1047 inst.add_operand(lod_id);
1048
1049 inst
1050 }
1051 (crate::SampleLevel::Bias(bias_handle), None) => {
1052 let mut inst = Instruction::image_sample(
1053 sample_result_type_id,
1054 id,
1055 SampleLod::Implicit,
1056 sampled_image_id,
1057 coordinates_id,
1058 depth_id,
1059 );
1060
1061 let bias_id = self.cached[bias_handle];
1062 mask |= spirv::ImageOperands::BIAS;
1063 inst.add_operand(mask.bits());
1064 inst.add_operand(bias_id);
1065
1066 inst
1067 }
1068 (crate::SampleLevel::Gradient { x, y }, None) => {
1069 let mut inst = Instruction::image_sample(
1070 sample_result_type_id,
1071 id,
1072 SampleLod::Explicit,
1073 sampled_image_id,
1074 coordinates_id,
1075 depth_id,
1076 );
1077
1078 let x_id = self.cached[x];
1079 let y_id = self.cached[y];
1080 mask |= spirv::ImageOperands::GRAD;
1081 inst.add_operand(mask.bits());
1082 inst.add_operand(x_id);
1083 inst.add_operand(y_id);
1084
1085 inst
1086 }
1087 };
1088
1089 if let Some(offset_const) = offset {
1090 let offset_id = self.cached[offset_const];
1091 main_instruction.add_operand(offset_id);
1092 }
1093
1094 block.body.push(main_instruction);
1095
1096 let id = if needs_sub_access {
1097 let sub_id = self.gen_id();
1098 block.body.push(Instruction::composite_extract(
1099 result_type_id,
1100 sub_id,
1101 id,
1102 &[0],
1103 ));
1104 sub_id
1105 } else {
1106 id
1107 };
1108
1109 Ok(id)
1110 }
1111
1112 pub(super) fn write_image_query(
1116 &mut self,
1117 result_type_id: Word,
1118 image: Handle<crate::Expression>,
1119 query: crate::ImageQuery,
1120 block: &mut Block,
1121 ) -> Result<Word, Error> {
1122 use crate::{ImageClass as Ic, ImageDimension as Id, ImageQuery as Iq};
1123
1124 let image_id = self.get_handle_id(image);
1125 let image_type = self.fun_info[image].ty.handle().unwrap();
1126 let (dim, arrayed, class) = match self.ir_module.types[image_type].inner {
1127 crate::TypeInner::Image {
1128 dim,
1129 arrayed,
1130 class,
1131 } => (dim, arrayed, class),
1132 _ => {
1133 return Err(Error::Validation("image type"));
1134 }
1135 };
1136
1137 self.writer
1138 .require_any("image queries", &[spirv::Capability::ImageQuery])?;
1139
1140 let id = match query {
1141 Iq::Size { level } => {
1142 let dim_coords = match dim {
1143 Id::D1 => 1,
1144 Id::D2 | Id::Cube => 2,
1145 Id::D3 => 3,
1146 };
1147 let array_coords = usize::from(arrayed);
1148 let vector_size = match dim_coords + array_coords {
1149 2 => Some(crate::VectorSize::Bi),
1150 3 => Some(crate::VectorSize::Tri),
1151 4 => Some(crate::VectorSize::Quad),
1152 _ => None,
1153 };
1154 let vector_numeric_type = match vector_size {
1155 Some(size) => NumericType::Vector {
1156 size,
1157 scalar: crate::Scalar::U32,
1158 },
1159 None => NumericType::Scalar(crate::Scalar::U32),
1160 };
1161
1162 let extended_size_type_id = self.get_numeric_type_id(vector_numeric_type);
1163
1164 let (query_op, level_id) = match class {
1165 Ic::Sampled { multi: true, .. }
1166 | Ic::Depth { multi: true }
1167 | Ic::Storage { .. } => (spirv::Op::ImageQuerySize, None),
1168 _ => {
1169 let level_id = match level {
1170 Some(expr) => self.cached[expr],
1171 None => self.get_index_constant(0),
1172 };
1173 (spirv::Op::ImageQuerySizeLod, Some(level_id))
1174 }
1175 };
1176
1177 let id_extended = self.gen_id();
1180 let mut inst = Instruction::image_query(
1181 query_op,
1182 extended_size_type_id,
1183 id_extended,
1184 image_id,
1185 );
1186 if let Some(expr_id) = level_id {
1187 inst.add_operand(expr_id);
1188 }
1189 block.body.push(inst);
1190
1191 if result_type_id != extended_size_type_id {
1192 let id = self.gen_id();
1193 let components = match dim {
1194 Id::Cube => &[0u32, 0][..],
1196 _ => &[0u32, 1, 2, 3][..dim_coords],
1197 };
1198 block.body.push(Instruction::vector_shuffle(
1199 result_type_id,
1200 id,
1201 id_extended,
1202 id_extended,
1203 components,
1204 ));
1205
1206 id
1207 } else {
1208 id_extended
1209 }
1210 }
1211 Iq::NumLevels => {
1212 let query_id = self.gen_id();
1213 block.body.push(Instruction::image_query(
1214 spirv::Op::ImageQueryLevels,
1215 result_type_id,
1216 query_id,
1217 image_id,
1218 ));
1219
1220 query_id
1221 }
1222 Iq::NumLayers => {
1223 let vec_size = match dim {
1224 Id::D1 => crate::VectorSize::Bi,
1225 Id::D2 | Id::Cube => crate::VectorSize::Tri,
1226 Id::D3 => crate::VectorSize::Quad,
1227 };
1228 let extended_size_type_id = self.get_numeric_type_id(NumericType::Vector {
1229 size: vec_size,
1230 scalar: crate::Scalar::U32,
1231 });
1232 let id_extended = self.gen_id();
1233 let mut inst = Instruction::image_query(
1234 spirv::Op::ImageQuerySizeLod,
1235 extended_size_type_id,
1236 id_extended,
1237 image_id,
1238 );
1239 inst.add_operand(self.get_index_constant(0));
1240 block.body.push(inst);
1241
1242 let extract_id = self.gen_id();
1243 block.body.push(Instruction::composite_extract(
1244 result_type_id,
1245 extract_id,
1246 id_extended,
1247 &[vec_size as u32 - 1],
1248 ));
1249
1250 extract_id
1251 }
1252 Iq::NumSamples => {
1253 let query_id = self.gen_id();
1254 block.body.push(Instruction::image_query(
1255 spirv::Op::ImageQuerySamples,
1256 result_type_id,
1257 query_id,
1258 image_id,
1259 ));
1260
1261 query_id
1262 }
1263 };
1264
1265 Ok(id)
1266 }
1267
1268 pub(super) fn write_image_store(
1269 &mut self,
1270 image: Handle<crate::Expression>,
1271 coordinate: Handle<crate::Expression>,
1272 array_index: Option<Handle<crate::Expression>>,
1273 value: Handle<crate::Expression>,
1274 block: &mut Block,
1275 ) -> Result<(), Error> {
1276 let image_id = self.get_handle_id(image);
1277 let coordinates = self.write_image_coordinates(coordinate, array_index, block)?;
1278 let value_id = self.cached[value];
1279
1280 let write = Store { image_id, value_id };
1281
1282 match *self.fun_info[image].ty.inner_with(&self.ir_module.types) {
1283 crate::TypeInner::Image {
1284 class:
1285 crate::ImageClass::Storage {
1286 format: crate::StorageFormat::Bgra8Unorm,
1287 ..
1288 },
1289 ..
1290 } => self.writer.require_any(
1291 "Bgra8Unorm storage write",
1292 &[spirv::Capability::StorageImageWriteWithoutFormat],
1293 )?,
1294 _ => {}
1295 }
1296
1297 write.generate(
1298 &mut self.writer.id_gen,
1299 coordinates.value_id,
1300 None,
1301 None,
1302 block,
1303 );
1304
1305 Ok(())
1306 }
1307
1308 pub(super) fn write_image_atomic(
1309 &mut self,
1310 image: Handle<crate::Expression>,
1311 coordinate: Handle<crate::Expression>,
1312 array_index: Option<Handle<crate::Expression>>,
1313 fun: crate::AtomicFunction,
1314 value: Handle<crate::Expression>,
1315 block: &mut Block,
1316 ) -> Result<(), Error> {
1317 let image_id = match self.ir_function.originating_global(image) {
1318 Some(handle) => self.writer.global_variables[handle].var_id,
1319 _ => return Err(Error::Validation("Unexpected image type")),
1320 };
1321 let crate::TypeInner::Image { class, .. } =
1322 *self.fun_info[image].ty.inner_with(&self.ir_module.types)
1323 else {
1324 return Err(Error::Validation("Invalid image type"));
1325 };
1326 let crate::ImageClass::Storage { format, .. } = class else {
1327 return Err(Error::Validation("Invalid image class"));
1328 };
1329 let scalar = format.into();
1330 let scalar_type_id = self.get_numeric_type_id(NumericType::Scalar(scalar));
1331 let pointer_type_id = self.get_pointer_type_id(scalar_type_id, spirv::StorageClass::Image);
1332 let signed = scalar.kind == crate::ScalarKind::Sint;
1333 if scalar.width == 8 {
1334 self.writer
1335 .require_any("64 bit image atomics", &[spirv::Capability::Int64Atomics])?;
1336 }
1337 let pointer_id = self.gen_id();
1338 let coordinates = self.write_image_coordinates(coordinate, array_index, block)?;
1339 let sample_id = self.writer.get_constant_scalar(crate::Literal::U32(0));
1340 block.body.push(Instruction::image_texel_pointer(
1341 pointer_type_id,
1342 pointer_id,
1343 image_id,
1344 coordinates.value_id,
1345 sample_id,
1346 ));
1347
1348 let op = match fun {
1349 crate::AtomicFunction::Add => spirv::Op::AtomicIAdd,
1350 crate::AtomicFunction::Subtract => spirv::Op::AtomicISub,
1351 crate::AtomicFunction::And => spirv::Op::AtomicAnd,
1352 crate::AtomicFunction::ExclusiveOr => spirv::Op::AtomicXor,
1353 crate::AtomicFunction::InclusiveOr => spirv::Op::AtomicOr,
1354 crate::AtomicFunction::Min if signed => spirv::Op::AtomicSMin,
1355 crate::AtomicFunction::Min => spirv::Op::AtomicUMin,
1356 crate::AtomicFunction::Max if signed => spirv::Op::AtomicSMax,
1357 crate::AtomicFunction::Max => spirv::Op::AtomicUMax,
1358 crate::AtomicFunction::Exchange { .. } => {
1359 return Err(Error::Validation("Exchange atomics are not supported yet"))
1360 }
1361 };
1362 let result_type_id = self.get_expression_type_id(&self.fun_info[value].ty);
1363 let id = self.gen_id();
1364 let space = crate::AddressSpace::Handle;
1365 let (semantics, scope) = space.to_spirv_semantics_and_scope();
1366 let scope_constant_id = self.get_scope_constant(scope as u32);
1367 let semantics_id = self.get_index_constant(semantics.bits());
1368 let value_id = self.cached[value];
1369
1370 block.body.push(Instruction::image_atomic(
1371 op,
1372 result_type_id,
1373 id,
1374 pointer_id,
1375 scope_constant_id,
1376 semantics_id,
1377 value_id,
1378 ));
1379
1380 Ok(())
1381 }
1382}