Skip to main content

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                    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        // In hlsl etc, the write value may not be the vector 4.
320        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        // No need to call get_expr_handle here since only globals/arguments are
418        // allowed as images and they are always in the root scope
419        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            // The return type of `OpImageRead` can be a scalar or vector.
462            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                    // VUID-StandaloneSpirv-OpImageGather-04664:
638                    // The “Component” operand of OpImageGather, and OpImageSparseGather must be the
639                    // <id> of a constant instruction.
640                    crate::Expression::Constant(const_handle) => {
641                        let constant = &ctx.module.constants[const_handle];
642                        match ctx.module.global_expressions[constant.init] {
643                            // SPIR-V specification: "It must be a 32-bit integer type scalar."
644                            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                // SPIR-V specification: "Behavior is undefined if its value is not 0, 1, 2 or 3."
664                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        // No need to call get_expr_handle here since only globals/arguments are
852        // allowed as images and they are always in the root scope
853        //TODO: handle arrays and cubes
854        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        // No need to call get_expr_handle here since only globals/arguments are
897        // allowed as images and they are always in the root scope
898        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}