naga/front/spv/
image.rs

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    /// Flags describing sampling method.
18    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
19    pub struct SamplingFlags: u32 {
20        /// Regular sampling.
21        const REGULAR = 0x1;
22        /// Comparison sampling.
23        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/// Options of a sampling operation.
44#[derive(Debug)]
45pub struct SamplingOptions {
46    /// Projection sampling: the division by W is expected to happen
47    /// in the texture unit.
48    pub project: bool,
49    /// Depth comparison sampling with a reference value.
50    pub compare: bool,
51    /// Gather sampling: Operates on four samples of one channel.
52    pub gather: bool,
53}
54
55enum ExtraCoordinate {
56    ArrayLayer,
57    Projection,
58    Garbage,
59}
60
61/// Return the texture coordinates separated from the array layer,
62/// and/or divided by the projection term.
63///
64/// The Proj sampling ops expect an extra coordinate for the W.
65/// The arrayed (can't be Proj!) images expect an extra coordinate for the layer.
66fn 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        // In hlsl etc, the write value may not be the vector 4.
321        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        // No need to call get_expr_handle here since only globals/arguments are
419        // allowed as images and they are always in the root scope
420        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            // The return type of `OpImageRead` can be a scalar or vector.
463            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                    // VUID-StandaloneSpirv-OpImageGather-04664:
640                    // The “Component” operand of OpImageGather, and OpImageSparseGather must be the
641                    // <id> of a constant instruction.
642                    crate::Expression::Constant(const_handle) => {
643                        let constant = &ctx.module.constants[const_handle];
644                        match ctx.module.global_expressions[constant.init] {
645                            // SPIR-V specification: "It must be a 32-bit integer type scalar."
646                            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                // SPIR-V specification: "Behavior is undefined if its value is not 0, 1, 2 or 3."
666                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        // No need to call get_expr_handle here since only globals/arguments are
854        // allowed as images and they are always in the root scope
855        //TODO: handle arrays and cubes
856        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        // No need to call get_expr_handle here since only globals/arguments are
900        // allowed as images and they are always in the root scope
901        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}