1use alloc::vec::Vec;
2
3use crate::{
4 arena::{Handle, UniqueArena},
5 Scalar,
6};
7
8use super::{Error, LookupExpression, LookupHelper as _};
9
10#[derive(Clone, Debug)]
11pub(super) struct LookupSampledImage {
12 image: Handle<crate::Expression>,
13 sampler: Handle<crate::Expression>,
14}
15
16bitflags::bitflags! {
17 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
19 pub struct SamplingFlags: u32 {
20 const REGULAR = 0x1;
22 const COMPARISON = 0x2;
24 }
25}
26
27impl super::BlockContext<'_> {
28 fn get_image_expr_ty(
29 &self,
30 handle: Handle<crate::Expression>,
31 ) -> Result<Handle<crate::Type>, Error> {
32 match self.expressions[handle] {
33 crate::Expression::GlobalVariable(handle) => {
34 Ok(self.module.global_variables[handle].ty)
35 }
36 crate::Expression::FunctionArgument(i) => Ok(self.arguments[i as usize].ty),
37 crate::Expression::Access { base, .. } => Ok(self.get_image_expr_ty(base)?),
38 ref other => Err(Error::InvalidImageExpression(other.clone())),
39 }
40 }
41}
42
43#[derive(Debug)]
45pub struct SamplingOptions {
46 pub project: bool,
49 pub compare: bool,
51 pub gather: bool,
53}
54
55enum ExtraCoordinate {
56 ArrayLayer,
57 Projection,
58 Garbage,
59}
60
61fn extract_image_coordinates(
67 image_dim: crate::ImageDimension,
68 extra_coordinate: ExtraCoordinate,
69 base: Handle<crate::Expression>,
70 coordinate_ty: Handle<crate::Type>,
71 ctx: &mut super::BlockContext,
72) -> (Handle<crate::Expression>, Option<Handle<crate::Expression>>) {
73 let (given_size, kind) = match ctx.module.types[coordinate_ty].inner {
74 crate::TypeInner::Scalar(Scalar { kind, .. }) => (None, kind),
75 crate::TypeInner::Vector {
76 size,
77 scalar: Scalar { kind, .. },
78 } => (Some(size), kind),
79 ref other => unreachable!("Unexpected texture coordinate {:?}", other),
80 };
81
82 let required_size = image_dim.required_coordinate_size();
83 let required_ty = required_size.map(|size| {
84 ctx.module
85 .types
86 .get(&crate::Type {
87 name: None,
88 inner: crate::TypeInner::Vector {
89 size,
90 scalar: Scalar { kind, width: 4 },
91 },
92 })
93 .expect("Required coordinate type should have been set up by `parse_type_image`!")
94 });
95 let extra_expr = crate::Expression::AccessIndex {
96 base,
97 index: required_size.map_or(1, |size| size as u32),
98 };
99
100 let base_span = ctx.expressions.get_span(base);
101
102 match extra_coordinate {
103 ExtraCoordinate::ArrayLayer => {
104 let extracted = match required_size {
105 None => ctx
106 .expressions
107 .append(crate::Expression::AccessIndex { base, index: 0 }, base_span),
108 Some(size) => {
109 let mut components = Vec::with_capacity(size as usize);
110 for index in 0..size as u32 {
111 let comp = ctx
112 .expressions
113 .append(crate::Expression::AccessIndex { base, index }, base_span);
114 components.push(comp);
115 }
116 ctx.expressions.append(
117 crate::Expression::Compose {
118 ty: required_ty.unwrap(),
119 components,
120 },
121 base_span,
122 )
123 }
124 };
125 let array_index_f32 = ctx.expressions.append(extra_expr, base_span);
126 let array_index = ctx.expressions.append(
127 crate::Expression::As {
128 target: Scalar::I32,
129 expr: array_index_f32,
130 },
131 base_span,
132 );
133 (extracted, Some(array_index))
134 }
135 ExtraCoordinate::Projection => {
136 let projection = ctx.expressions.append(extra_expr, base_span);
137 let divided = match required_size {
138 None => {
139 let temp = ctx
140 .expressions
141 .append(crate::Expression::AccessIndex { base, index: 0 }, base_span);
142 ctx.expressions.append(
143 crate::Expression::Binary {
144 op: crate::BinaryOperator::Divide,
145 left: temp,
146 right: projection,
147 },
148 base_span,
149 )
150 }
151 Some(size) => {
152 let mut components = Vec::with_capacity(size as usize);
153 for index in 0..size as u32 {
154 let temp = ctx
155 .expressions
156 .append(crate::Expression::AccessIndex { base, index }, base_span);
157 let comp = ctx.expressions.append(
158 crate::Expression::Binary {
159 op: crate::BinaryOperator::Divide,
160 left: temp,
161 right: projection,
162 },
163 base_span,
164 );
165 components.push(comp);
166 }
167 ctx.expressions.append(
168 crate::Expression::Compose {
169 ty: required_ty.unwrap(),
170 components,
171 },
172 base_span,
173 )
174 }
175 };
176 (divided, None)
177 }
178 ExtraCoordinate::Garbage if given_size == required_size => (base, None),
179 ExtraCoordinate::Garbage => {
180 use crate::SwizzleComponent as Sc;
181 let cut_expr = match required_size {
182 None => crate::Expression::AccessIndex { base, index: 0 },
183 Some(size) => crate::Expression::Swizzle {
184 size,
185 vector: base,
186 pattern: [Sc::X, Sc::Y, Sc::Z, Sc::W],
187 },
188 };
189 (ctx.expressions.append(cut_expr, base_span), None)
190 }
191 }
192}
193
194pub(super) fn patch_comparison_type(
195 flags: SamplingFlags,
196 var: &mut crate::GlobalVariable,
197 arena: &mut UniqueArena<crate::Type>,
198) -> bool {
199 if !flags.contains(SamplingFlags::COMPARISON) {
200 return true;
201 }
202 if flags == SamplingFlags::all() {
203 return false;
204 }
205
206 log::debug!("Flipping comparison for {var:?}");
207 let original_ty = &arena[var.ty];
208 let original_ty_span = arena.get_span(var.ty);
209 let ty_inner = match original_ty.inner {
210 crate::TypeInner::Image {
211 class: crate::ImageClass::Sampled { multi, .. },
212 dim,
213 arrayed,
214 } => crate::TypeInner::Image {
215 class: crate::ImageClass::Depth { multi },
216 dim,
217 arrayed,
218 },
219 crate::TypeInner::Sampler { .. } => crate::TypeInner::Sampler { comparison: true },
220 ref other => unreachable!("Unexpected type for comparison mutation: {:?}", other),
221 };
222
223 let name = original_ty.name.clone();
224 var.ty = arena.insert(
225 crate::Type {
226 name,
227 inner: ty_inner,
228 },
229 original_ty_span,
230 );
231 true
232}
233
234impl<I: Iterator<Item = u32>> super::Frontend<I> {
235 pub(super) fn parse_image_couple(&mut self) -> Result<(), Error> {
236 let _result_type_id = self.next()?;
237 let result_id = self.next()?;
238 let image_id = self.next()?;
239 let sampler_id = self.next()?;
240 let image_lexp = self.lookup_expression.lookup(image_id)?;
241 let sampler_lexp = self.lookup_expression.lookup(sampler_id)?;
242 self.lookup_sampled_image.insert(
243 result_id,
244 LookupSampledImage {
245 image: image_lexp.handle,
246 sampler: sampler_lexp.handle,
247 },
248 );
249 Ok(())
250 }
251
252 pub(super) fn parse_image_uncouple(&mut self, block_id: spirv::Word) -> Result<(), Error> {
253 let result_type_id = self.next()?;
254 let result_id = self.next()?;
255 let sampled_image_id = self.next()?;
256 self.lookup_expression.insert(
257 result_id,
258 LookupExpression {
259 handle: self.lookup_sampled_image.lookup(sampled_image_id)?.image,
260 type_id: result_type_id,
261 block_id,
262 },
263 );
264 Ok(())
265 }
266
267 pub(super) fn parse_image_write(
268 &mut self,
269 words_left: u16,
270 ctx: &mut super::BlockContext,
271 emitter: &mut crate::proc::Emitter,
272 block: &mut crate::Block,
273 body_idx: usize,
274 ) -> Result<crate::Statement, Error> {
275 let image_id = self.next()?;
276 let coordinate_id = self.next()?;
277 let value_id = self.next()?;
278
279 let image_ops = if words_left != 0 { self.next()? } else { 0 };
280
281 if image_ops != 0 {
282 let other = spirv::ImageOperands::from_bits_truncate(image_ops);
283 log::warn!("Unknown image write ops {other:?}");
284 for _ in 1..words_left {
285 self.next()?;
286 }
287 }
288
289 let image_lexp = self.lookup_expression.lookup(image_id)?;
290 let image_ty = ctx.get_image_expr_ty(image_lexp.handle)?;
291
292 let coord_lexp = self.lookup_expression.lookup(coordinate_id)?;
293 let coord_handle =
294 self.get_expr_handle(coordinate_id, coord_lexp, ctx, emitter, block, body_idx);
295 let coord_type_handle = self.lookup_type.lookup(coord_lexp.type_id)?.handle;
296 let (coordinate, array_index) = match ctx.module.types[image_ty].inner {
297 crate::TypeInner::Image {
298 dim,
299 arrayed,
300 class: _,
301 } => extract_image_coordinates(
302 dim,
303 if arrayed {
304 ExtraCoordinate::ArrayLayer
305 } else {
306 ExtraCoordinate::Garbage
307 },
308 coord_handle,
309 coord_type_handle,
310 ctx,
311 ),
312 _ => return Err(Error::InvalidImage(image_ty)),
313 };
314
315 let value_lexp = self.lookup_expression.lookup(value_id)?;
316 let value = self.get_expr_handle(value_id, value_lexp, ctx, emitter, block, body_idx);
317 let value_type = self.lookup_type.lookup(value_lexp.type_id)?.handle;
318
319 let expanded_value = match ctx.module.types[value_type].inner {
321 crate::TypeInner::Scalar(_) => Some(crate::Expression::Splat {
322 value,
323 size: crate::VectorSize::Quad,
324 }),
325 crate::TypeInner::Vector { size, .. } => match size {
326 crate::VectorSize::Bi => Some(crate::Expression::Swizzle {
327 size: crate::VectorSize::Quad,
328 vector: value,
329 pattern: [
330 crate::SwizzleComponent::X,
331 crate::SwizzleComponent::Y,
332 crate::SwizzleComponent::Y,
333 crate::SwizzleComponent::Y,
334 ],
335 }),
336 crate::VectorSize::Tri => Some(crate::Expression::Swizzle {
337 size: crate::VectorSize::Quad,
338 vector: value,
339 pattern: [
340 crate::SwizzleComponent::X,
341 crate::SwizzleComponent::Y,
342 crate::SwizzleComponent::Z,
343 crate::SwizzleComponent::Z,
344 ],
345 }),
346 crate::VectorSize::Quad => None,
347 },
348 _ => return Err(Error::InvalidVectorType(value_type)),
349 };
350
351 let value_patched = if let Some(s) = expanded_value {
352 ctx.expressions.append(s, crate::Span::default())
353 } else {
354 value
355 };
356
357 Ok(crate::Statement::ImageStore {
358 image: image_lexp.handle,
359 coordinate,
360 array_index,
361 value: value_patched,
362 })
363 }
364
365 pub(super) fn parse_image_load(
366 &mut self,
367 mut words_left: u16,
368 ctx: &mut super::BlockContext,
369 emitter: &mut crate::proc::Emitter,
370 block: &mut crate::Block,
371 block_id: spirv::Word,
372 body_idx: usize,
373 ) -> Result<(), Error> {
374 let start = self.data_offset;
375 let result_type_id = self.next()?;
376 let result_id = self.next()?;
377 let image_id = self.next()?;
378 let coordinate_id = self.next()?;
379
380 let mut image_ops = if words_left != 0 {
381 words_left -= 1;
382 self.next()?
383 } else {
384 0
385 };
386
387 let mut sample = None;
388 let mut level = None;
389 while image_ops != 0 {
390 let bit = 1 << image_ops.trailing_zeros();
391 match spirv::ImageOperands::from_bits_truncate(bit) {
392 spirv::ImageOperands::LOD => {
393 let lod_expr = self.next()?;
394 let lod_lexp = self.lookup_expression.lookup(lod_expr)?;
395 let lod_handle =
396 self.get_expr_handle(lod_expr, lod_lexp, ctx, emitter, block, body_idx);
397 level = Some(lod_handle);
398 words_left -= 1;
399 }
400 spirv::ImageOperands::SAMPLE => {
401 let sample_expr = self.next()?;
402 let sample_handle = self.lookup_expression.lookup(sample_expr)?.handle;
403 sample = Some(sample_handle);
404 words_left -= 1;
405 }
406 other => {
407 log::warn!("Unknown image load op {other:?}");
408 for _ in 0..words_left {
409 self.next()?;
410 }
411 break;
412 }
413 }
414 image_ops ^= bit;
415 }
416
417 let image_lexp = self.lookup_expression.lookup(image_id)?;
420 let image_ty = ctx.get_image_expr_ty(image_lexp.handle)?;
421
422 let coord_lexp = self.lookup_expression.lookup(coordinate_id)?;
423 let coord_handle =
424 self.get_expr_handle(coordinate_id, coord_lexp, ctx, emitter, block, body_idx);
425 let coord_type_handle = self.lookup_type.lookup(coord_lexp.type_id)?.handle;
426 let (coordinate, array_index, is_depth) = match ctx.module.types[image_ty].inner {
427 crate::TypeInner::Image {
428 dim,
429 arrayed,
430 class,
431 } => {
432 let (coord, array_index) = extract_image_coordinates(
433 dim,
434 if arrayed {
435 ExtraCoordinate::ArrayLayer
436 } else {
437 ExtraCoordinate::Garbage
438 },
439 coord_handle,
440 coord_type_handle,
441 ctx,
442 );
443 (coord, array_index, class.is_depth())
444 }
445 _ => return Err(Error::InvalidImage(image_ty)),
446 };
447
448 let image_load_expr = crate::Expression::ImageLoad {
449 image: image_lexp.handle,
450 coordinate,
451 array_index,
452 sample,
453 level,
454 };
455 let image_load_handle = ctx
456 .expressions
457 .append(image_load_expr, self.span_from_with_op(start));
458
459 let handle = if is_depth {
460 let result_ty = self.lookup_type.lookup(result_type_id)?;
461 match ctx.module.types[result_ty.handle].inner {
463 crate::TypeInner::Vector { size, .. } => {
464 let splat_expr = crate::Expression::Splat {
465 size,
466 value: image_load_handle,
467 };
468 ctx.expressions
469 .append(splat_expr, self.span_from_with_op(start))
470 }
471 _ => image_load_handle,
472 }
473 } else {
474 image_load_handle
475 };
476
477 self.lookup_expression.insert(
478 result_id,
479 LookupExpression {
480 handle,
481 type_id: result_type_id,
482 block_id,
483 },
484 );
485 Ok(())
486 }
487
488 #[allow(clippy::too_many_arguments)]
489 pub(super) fn parse_image_sample(
490 &mut self,
491 mut words_left: u16,
492 options: SamplingOptions,
493 ctx: &mut super::BlockContext,
494 emitter: &mut crate::proc::Emitter,
495 block: &mut crate::Block,
496 block_id: spirv::Word,
497 body_idx: usize,
498 ) -> Result<(), Error> {
499 let start = self.data_offset;
500 let result_type_id = self.next()?;
501 let result_id = self.next()?;
502 let sampled_image_id = self.next()?;
503 let coordinate_id = self.next()?;
504 let (component_id, dref_id) = match (options.gather, options.compare) {
505 (true, false) => (Some(self.next()?), None),
506 (_, true) => (None, Some(self.next()?)),
507 (_, _) => (None, None),
508 };
509 let span = self.span_from_with_op(start);
510
511 let mut image_ops = if words_left != 0 {
512 words_left -= 1;
513 self.next()?
514 } else {
515 0
516 };
517
518 let mut level = crate::SampleLevel::Auto;
519 let mut offset = None;
520 while image_ops != 0 {
521 let bit = 1 << image_ops.trailing_zeros();
522 match spirv::ImageOperands::from_bits_truncate(bit) {
523 spirv::ImageOperands::BIAS => {
524 let bias_expr = self.next()?;
525 let bias_lexp = self.lookup_expression.lookup(bias_expr)?;
526 let bias_handle =
527 self.get_expr_handle(bias_expr, bias_lexp, ctx, emitter, block, body_idx);
528 level = crate::SampleLevel::Bias(bias_handle);
529 words_left -= 1;
530 }
531 spirv::ImageOperands::LOD => {
532 let lod_expr = self.next()?;
533 let lod_lexp = self.lookup_expression.lookup(lod_expr)?;
534 let lod_handle =
535 self.get_expr_handle(lod_expr, lod_lexp, ctx, emitter, block, body_idx);
536
537 let is_depth_image = {
538 let image_lexp = self.lookup_sampled_image.lookup(sampled_image_id)?;
539 let image_ty = ctx.get_image_expr_ty(image_lexp.image)?;
540 matches!(
541 ctx.module.types[image_ty].inner,
542 crate::TypeInner::Image {
543 class: crate::ImageClass::Depth { .. },
544 ..
545 }
546 )
547 };
548
549 level = if options.compare {
550 log::debug!("Assuming {lod_handle:?} is zero");
551 crate::SampleLevel::Zero
552 } else if is_depth_image {
553 log::debug!(
554 "Assuming level {lod_handle:?} converts losslessly to an integer"
555 );
556 let expr = crate::Expression::As {
557 expr: lod_handle,
558 target: Scalar::I32,
559 };
560 let s32_lod_handle = ctx.expressions.append(expr, span);
561 crate::SampleLevel::Exact(s32_lod_handle)
562 } else {
563 crate::SampleLevel::Exact(lod_handle)
564 };
565 words_left -= 1;
566 }
567 spirv::ImageOperands::GRAD => {
568 let grad_x_expr = self.next()?;
569 let grad_x_lexp = self.lookup_expression.lookup(grad_x_expr)?;
570 let grad_x_handle = self.get_expr_handle(
571 grad_x_expr,
572 grad_x_lexp,
573 ctx,
574 emitter,
575 block,
576 body_idx,
577 );
578 let grad_y_expr = self.next()?;
579 let grad_y_lexp = self.lookup_expression.lookup(grad_y_expr)?;
580 let grad_y_handle = self.get_expr_handle(
581 grad_y_expr,
582 grad_y_lexp,
583 ctx,
584 emitter,
585 block,
586 body_idx,
587 );
588 level = if options.compare {
589 log::debug!(
590 "Assuming gradients {grad_x_handle:?} and {grad_y_handle:?} are not greater than 1"
591 );
592 crate::SampleLevel::Zero
593 } else {
594 crate::SampleLevel::Gradient {
595 x: grad_x_handle,
596 y: grad_y_handle,
597 }
598 };
599 words_left -= 2;
600 }
601 spirv::ImageOperands::CONST_OFFSET => {
602 let offset_expr = self.next()?;
603 let offset_lexp = self.lookup_expression.lookup(offset_expr)?;
604 let offset_handle = self.get_expr_handle(
605 offset_expr,
606 offset_lexp,
607 ctx,
608 emitter,
609 block,
610 body_idx,
611 );
612 offset = Some(offset_handle);
613 words_left -= 1;
614 }
615 other => {
616 log::warn!("Unknown image sample operand {other:?}");
617 for _ in 0..words_left {
618 self.next()?;
619 }
620 break;
621 }
622 }
623 image_ops ^= bit;
624 }
625
626 let si_lexp = self.lookup_sampled_image.lookup(sampled_image_id)?;
627 let coord_lexp = self.lookup_expression.lookup(coordinate_id)?;
628 let coord_handle =
629 self.get_expr_handle(coordinate_id, coord_lexp, ctx, emitter, block, body_idx);
630 let coord_type_handle = self.lookup_type.lookup(coord_lexp.type_id)?.handle;
631
632 let gather = match (options.gather, component_id) {
633 (true, Some(component_id)) => {
634 let component_lexp = self.lookup_expression.lookup(component_id)?;
635
636 let component_value = match ctx.expressions[component_lexp.handle] {
637 crate::Expression::Constant(const_handle) => {
641 let constant = &ctx.module.constants[const_handle];
642 match ctx.module.global_expressions[constant.init] {
643 crate::Expression::Literal(crate::Literal::U32(value)) => value,
645 crate::Expression::Literal(crate::Literal::I32(value)) => value as u32,
646 _ => {
647 log::error!(
648 "Image gather component constant must be a 32-bit integer literal"
649 );
650 return Err(Error::InvalidOperand);
651 }
652 }
653 }
654 _ => {
655 log::error!("Image gather component must be a constant");
656 return Err(Error::InvalidOperand);
657 }
658 };
659
660 debug_assert_eq!(level, crate::SampleLevel::Auto);
661 level = crate::SampleLevel::Zero;
662
663 match component_value {
665 0 => Some(crate::SwizzleComponent::X),
666 1 => Some(crate::SwizzleComponent::Y),
667 2 => Some(crate::SwizzleComponent::Z),
668 3 => Some(crate::SwizzleComponent::W),
669 other => {
670 log::error!("Invalid gather component operand: {other}");
671 return Err(Error::InvalidOperand);
672 }
673 }
674 }
675 (true, None) => {
676 debug_assert_eq!(level, crate::SampleLevel::Auto);
677 level = crate::SampleLevel::Zero;
678
679 Some(crate::SwizzleComponent::X)
680 }
681 (_, _) => None,
682 };
683
684 let sampling_bit = if options.compare {
685 SamplingFlags::COMPARISON
686 } else {
687 SamplingFlags::REGULAR
688 };
689
690 let image_ty = match ctx.expressions[si_lexp.image] {
691 crate::Expression::GlobalVariable(handle) => {
692 if let Some(flags) = self.handle_sampling.get_mut(&handle) {
693 *flags |= sampling_bit;
694 }
695
696 ctx.module.global_variables[handle].ty
697 }
698
699 crate::Expression::FunctionArgument(i) => {
700 ctx.parameter_sampling[i as usize] |= sampling_bit;
701 ctx.arguments[i as usize].ty
702 }
703
704 crate::Expression::Access { base, .. } => match ctx.expressions[base] {
705 crate::Expression::GlobalVariable(handle) => {
706 if let Some(flags) = self.handle_sampling.get_mut(&handle) {
707 *flags |= sampling_bit;
708 }
709
710 match ctx.module.types[ctx.module.global_variables[handle].ty].inner {
711 crate::TypeInner::BindingArray { base, .. } => base,
712 _ => return Err(Error::InvalidGlobalVar(ctx.expressions[base].clone())),
713 }
714 }
715
716 ref other => return Err(Error::InvalidGlobalVar(other.clone())),
717 },
718
719 ref other => return Err(Error::InvalidGlobalVar(other.clone())),
720 };
721
722 match ctx.expressions[si_lexp.sampler] {
723 crate::Expression::GlobalVariable(handle) => {
724 *self.handle_sampling.get_mut(&handle).unwrap() |= sampling_bit;
725 }
726
727 crate::Expression::FunctionArgument(i) => {
728 ctx.parameter_sampling[i as usize] |= sampling_bit;
729 }
730
731 crate::Expression::Access { base, .. } => match ctx.expressions[base] {
732 crate::Expression::GlobalVariable(handle) => {
733 *self.handle_sampling.get_mut(&handle).unwrap() |= sampling_bit;
734 }
735
736 ref other => return Err(Error::InvalidGlobalVar(other.clone())),
737 },
738
739 ref other => return Err(Error::InvalidGlobalVar(other.clone())),
740 }
741
742 let ((coordinate, array_index), depth_ref, is_depth) =
743 match ctx.module.types[image_ty].inner {
744 crate::TypeInner::Image {
745 dim,
746 arrayed,
747 class,
748 } => (
749 extract_image_coordinates(
750 dim,
751 if options.project {
752 ExtraCoordinate::Projection
753 } else if arrayed {
754 ExtraCoordinate::ArrayLayer
755 } else {
756 ExtraCoordinate::Garbage
757 },
758 coord_handle,
759 coord_type_handle,
760 ctx,
761 ),
762 {
763 match dref_id {
764 Some(id) => {
765 let expr_lexp = self.lookup_expression.lookup(id)?;
766 let mut expr = self
767 .get_expr_handle(id, expr_lexp, ctx, emitter, block, body_idx);
768
769 if options.project {
770 let required_size = dim.required_coordinate_size();
771 let right = ctx.expressions.append(
772 crate::Expression::AccessIndex {
773 base: coord_handle,
774 index: required_size.map_or(1, |size| size as u32),
775 },
776 crate::Span::default(),
777 );
778 expr = ctx.expressions.append(
779 crate::Expression::Binary {
780 op: crate::BinaryOperator::Divide,
781 left: expr,
782 right,
783 },
784 crate::Span::default(),
785 )
786 };
787 Some(expr)
788 }
789 None => None,
790 }
791 },
792 class.is_depth(),
793 ),
794 _ => return Err(Error::InvalidImage(image_ty)),
795 };
796
797 let expr = crate::Expression::ImageSample {
798 image: si_lexp.image,
799 sampler: si_lexp.sampler,
800 gather,
801 coordinate,
802 array_index,
803 offset,
804 level,
805 depth_ref,
806 clamp_to_edge: false,
807 };
808 let image_sample_handle = ctx.expressions.append(expr, self.span_from_with_op(start));
809 let handle = if is_depth && depth_ref.is_none() {
810 let splat_expr = crate::Expression::Splat {
811 size: crate::VectorSize::Quad,
812 value: image_sample_handle,
813 };
814 ctx.expressions
815 .append(splat_expr, self.span_from_with_op(start))
816 } else {
817 image_sample_handle
818 };
819 self.lookup_expression.insert(
820 result_id,
821 LookupExpression {
822 handle,
823 type_id: result_type_id,
824 block_id,
825 },
826 );
827 Ok(())
828 }
829
830 pub(super) fn parse_image_query_size(
831 &mut self,
832 at_level: bool,
833 ctx: &mut super::BlockContext,
834 emitter: &mut crate::proc::Emitter,
835 block: &mut crate::Block,
836 block_id: spirv::Word,
837 body_idx: usize,
838 ) -> Result<(), Error> {
839 let start = self.data_offset;
840 let result_type_id = self.next()?;
841 let result_id = self.next()?;
842 let image_id = self.next()?;
843 let level = if at_level {
844 let level_id = self.next()?;
845 let level_lexp = self.lookup_expression.lookup(level_id)?;
846 Some(self.get_expr_handle(level_id, level_lexp, ctx, emitter, block, body_idx))
847 } else {
848 None
849 };
850
851 let image_lexp = self.lookup_expression.lookup(image_id)?;
855
856 let expr = crate::Expression::ImageQuery {
857 image: image_lexp.handle,
858 query: crate::ImageQuery::Size { level },
859 };
860
861 let result_type_handle = self.lookup_type.lookup(result_type_id)?.handle;
862 let maybe_scalar_kind = ctx.module.types[result_type_handle].inner.scalar_kind();
863
864 let expr = if maybe_scalar_kind == Some(crate::ScalarKind::Sint) {
865 crate::Expression::As {
866 expr: ctx.expressions.append(expr, self.span_from_with_op(start)),
867 target: Scalar::I32,
868 }
869 } else {
870 expr
871 };
872
873 self.lookup_expression.insert(
874 result_id,
875 LookupExpression {
876 handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
877 type_id: result_type_id,
878 block_id,
879 },
880 );
881
882 Ok(())
883 }
884
885 pub(super) fn parse_image_query_other(
886 &mut self,
887 query: crate::ImageQuery,
888 ctx: &mut super::BlockContext,
889 block_id: spirv::Word,
890 ) -> Result<(), Error> {
891 let start = self.data_offset;
892 let result_type_id = self.next()?;
893 let result_id = self.next()?;
894 let image_id = self.next()?;
895
896 let image_lexp = self.lookup_expression.lookup(image_id)?.clone();
899
900 let expr = crate::Expression::ImageQuery {
901 image: image_lexp.handle,
902 query,
903 };
904
905 let result_type_handle = self.lookup_type.lookup(result_type_id)?.handle;
906 let maybe_scalar_kind = ctx.module.types[result_type_handle].inner.scalar_kind();
907
908 let expr = if maybe_scalar_kind == Some(crate::ScalarKind::Sint) {
909 crate::Expression::As {
910 expr: ctx.expressions.append(expr, self.span_from_with_op(start)),
911 target: Scalar::I32,
912 }
913 } else {
914 expr
915 };
916
917 self.lookup_expression.insert(
918 result_id,
919 LookupExpression {
920 handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
921 type_id: result_type_id,
922 block_id,
923 },
924 );
925
926 Ok(())
927 }
928}