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