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}
52
53enum ExtraCoordinate {
54    ArrayLayer,
55    Projection,
56    Garbage,
57}
58
59/// Return the texture coordinates separated from the array layer,
60/// and/or divided by the projection term.
61///
62/// The Proj sampling ops expect an extra coordinate for the W.
63/// The arrayed (can't be Proj!) images expect an extra coordinate for the layer.
64fn extract_image_coordinates(
65    image_dim: crate::ImageDimension,
66    extra_coordinate: ExtraCoordinate,
67    base: Handle<crate::Expression>,
68    coordinate_ty: Handle<crate::Type>,
69    ctx: &mut super::BlockContext,
70) -> (Handle<crate::Expression>, Option<Handle<crate::Expression>>) {
71    let (given_size, kind) = match ctx.module.types[coordinate_ty].inner {
72        crate::TypeInner::Scalar(Scalar { kind, .. }) => (None, kind),
73        crate::TypeInner::Vector {
74            size,
75            scalar: Scalar { kind, .. },
76        } => (Some(size), kind),
77        ref other => unreachable!("Unexpected texture coordinate {:?}", other),
78    };
79
80    let required_size = image_dim.required_coordinate_size();
81    let required_ty = required_size.map(|size| {
82        ctx.module
83            .types
84            .get(&crate::Type {
85                name: None,
86                inner: crate::TypeInner::Vector {
87                    size,
88                    scalar: Scalar { kind, width: 4 },
89                },
90            })
91            .expect("Required coordinate type should have been set up by `parse_type_image`!")
92    });
93    let extra_expr = crate::Expression::AccessIndex {
94        base,
95        index: required_size.map_or(1, |size| size as u32),
96    };
97
98    let base_span = ctx.expressions.get_span(base);
99
100    match extra_coordinate {
101        ExtraCoordinate::ArrayLayer => {
102            let extracted = match required_size {
103                None => ctx
104                    .expressions
105                    .append(crate::Expression::AccessIndex { base, index: 0 }, base_span),
106                Some(size) => {
107                    let mut components = Vec::with_capacity(size as usize);
108                    for index in 0..size as u32 {
109                        let comp = ctx
110                            .expressions
111                            .append(crate::Expression::AccessIndex { base, index }, base_span);
112                        components.push(comp);
113                    }
114                    ctx.expressions.append(
115                        crate::Expression::Compose {
116                            ty: required_ty.unwrap(),
117                            components,
118                        },
119                        base_span,
120                    )
121                }
122            };
123            let array_index_f32 = ctx.expressions.append(extra_expr, base_span);
124            let array_index = ctx.expressions.append(
125                crate::Expression::As {
126                    kind: crate::ScalarKind::Sint,
127                    expr: array_index_f32,
128                    convert: Some(4),
129                },
130                base_span,
131            );
132            (extracted, Some(array_index))
133        }
134        ExtraCoordinate::Projection => {
135            let projection = ctx.expressions.append(extra_expr, base_span);
136            let divided = match required_size {
137                None => {
138                    let temp = ctx
139                        .expressions
140                        .append(crate::Expression::AccessIndex { base, index: 0 }, base_span);
141                    ctx.expressions.append(
142                        crate::Expression::Binary {
143                            op: crate::BinaryOperator::Divide,
144                            left: temp,
145                            right: projection,
146                        },
147                        base_span,
148                    )
149                }
150                Some(size) => {
151                    let mut components = Vec::with_capacity(size as usize);
152                    for index in 0..size as u32 {
153                        let temp = ctx
154                            .expressions
155                            .append(crate::Expression::AccessIndex { base, index }, base_span);
156                        let comp = ctx.expressions.append(
157                            crate::Expression::Binary {
158                                op: crate::BinaryOperator::Divide,
159                                left: temp,
160                                right: projection,
161                            },
162                            base_span,
163                        );
164                        components.push(comp);
165                    }
166                    ctx.expressions.append(
167                        crate::Expression::Compose {
168                            ty: required_ty.unwrap(),
169                            components,
170                        },
171                        base_span,
172                    )
173                }
174            };
175            (divided, None)
176        }
177        ExtraCoordinate::Garbage if given_size == required_size => (base, None),
178        ExtraCoordinate::Garbage => {
179            use crate::SwizzleComponent as Sc;
180            let cut_expr = match required_size {
181                None => crate::Expression::AccessIndex { base, index: 0 },
182                Some(size) => crate::Expression::Swizzle {
183                    size,
184                    vector: base,
185                    pattern: [Sc::X, Sc::Y, Sc::Z, Sc::W],
186                },
187            };
188            (ctx.expressions.append(cut_expr, base_span), None)
189        }
190    }
191}
192
193pub(super) fn patch_comparison_type(
194    flags: SamplingFlags,
195    var: &mut crate::GlobalVariable,
196    arena: &mut UniqueArena<crate::Type>,
197) -> bool {
198    if !flags.contains(SamplingFlags::COMPARISON) {
199        return true;
200    }
201    if flags == SamplingFlags::all() {
202        return false;
203    }
204
205    log::debug!("Flipping comparison for {var:?}");
206    let original_ty = &arena[var.ty];
207    let original_ty_span = arena.get_span(var.ty);
208    let ty_inner = match original_ty.inner {
209        crate::TypeInner::Image {
210            class: crate::ImageClass::Sampled { multi, .. },
211            dim,
212            arrayed,
213        } => crate::TypeInner::Image {
214            class: crate::ImageClass::Depth { multi },
215            dim,
216            arrayed,
217        },
218        crate::TypeInner::Sampler { .. } => crate::TypeInner::Sampler { comparison: true },
219        ref other => unreachable!("Unexpected type for comparison mutation: {:?}", other),
220    };
221
222    let name = original_ty.name.clone();
223    var.ty = arena.insert(
224        crate::Type {
225            name,
226            inner: ty_inner,
227        },
228        original_ty_span,
229    );
230    true
231}
232
233impl<I: Iterator<Item = u32>> super::Frontend<I> {
234    pub(super) fn parse_image_couple(&mut self) -> Result<(), Error> {
235        let _result_type_id = self.next()?;
236        let result_id = self.next()?;
237        let image_id = self.next()?;
238        let sampler_id = self.next()?;
239        let image_lexp = self.lookup_expression.lookup(image_id)?;
240        let sampler_lexp = self.lookup_expression.lookup(sampler_id)?;
241        self.lookup_sampled_image.insert(
242            result_id,
243            LookupSampledImage {
244                image: image_lexp.handle,
245                sampler: sampler_lexp.handle,
246            },
247        );
248        Ok(())
249    }
250
251    pub(super) fn parse_image_uncouple(&mut self, block_id: spirv::Word) -> Result<(), Error> {
252        let result_type_id = self.next()?;
253        let result_id = self.next()?;
254        let sampled_image_id = self.next()?;
255        self.lookup_expression.insert(
256            result_id,
257            LookupExpression {
258                handle: self.lookup_sampled_image.lookup(sampled_image_id)?.image,
259                type_id: result_type_id,
260                block_id,
261            },
262        );
263        Ok(())
264    }
265
266    pub(super) fn parse_image_write(
267        &mut self,
268        words_left: u16,
269        ctx: &mut super::BlockContext,
270        emitter: &mut crate::proc::Emitter,
271        block: &mut crate::Block,
272        body_idx: usize,
273    ) -> Result<crate::Statement, Error> {
274        let image_id = self.next()?;
275        let coordinate_id = self.next()?;
276        let value_id = self.next()?;
277
278        let image_ops = if words_left != 0 { self.next()? } else { 0 };
279
280        if image_ops != 0 {
281            let other = spirv::ImageOperands::from_bits_truncate(image_ops);
282            log::warn!("Unknown image write ops {other:?}");
283            for _ in 1..words_left {
284                self.next()?;
285            }
286        }
287
288        let image_lexp = self.lookup_expression.lookup(image_id)?;
289        let image_ty = ctx.get_image_expr_ty(image_lexp.handle)?;
290
291        let coord_lexp = self.lookup_expression.lookup(coordinate_id)?;
292        let coord_handle =
293            self.get_expr_handle(coordinate_id, coord_lexp, ctx, emitter, block, body_idx);
294        let coord_type_handle = self.lookup_type.lookup(coord_lexp.type_id)?.handle;
295        let (coordinate, array_index) = match ctx.module.types[image_ty].inner {
296            crate::TypeInner::Image {
297                dim,
298                arrayed,
299                class: _,
300            } => extract_image_coordinates(
301                dim,
302                if arrayed {
303                    ExtraCoordinate::ArrayLayer
304                } else {
305                    ExtraCoordinate::Garbage
306                },
307                coord_handle,
308                coord_type_handle,
309                ctx,
310            ),
311            _ => return Err(Error::InvalidImage(image_ty)),
312        };
313
314        let value_lexp = self.lookup_expression.lookup(value_id)?;
315        let value = self.get_expr_handle(value_id, value_lexp, ctx, emitter, block, body_idx);
316        let value_type = self.lookup_type.lookup(value_lexp.type_id)?.handle;
317
318        // In hlsl etc, the write value may not be the vector 4.
319        let expanded_value = match ctx.module.types[value_type].inner {
320            crate::TypeInner::Scalar(_) => Some(crate::Expression::Splat {
321                value,
322                size: crate::VectorSize::Quad,
323            }),
324            crate::TypeInner::Vector { size, .. } => match size {
325                crate::VectorSize::Bi => Some(crate::Expression::Swizzle {
326                    size: crate::VectorSize::Quad,
327                    vector: value,
328                    pattern: [
329                        crate::SwizzleComponent::X,
330                        crate::SwizzleComponent::Y,
331                        crate::SwizzleComponent::Y,
332                        crate::SwizzleComponent::Y,
333                    ],
334                }),
335                crate::VectorSize::Tri => Some(crate::Expression::Swizzle {
336                    size: crate::VectorSize::Quad,
337                    vector: value,
338                    pattern: [
339                        crate::SwizzleComponent::X,
340                        crate::SwizzleComponent::Y,
341                        crate::SwizzleComponent::Z,
342                        crate::SwizzleComponent::Z,
343                    ],
344                }),
345                crate::VectorSize::Quad => None,
346            },
347            _ => return Err(Error::InvalidVectorType(value_type)),
348        };
349
350        let value_patched = if let Some(s) = expanded_value {
351            ctx.expressions.append(s, crate::Span::default())
352        } else {
353            value
354        };
355
356        Ok(crate::Statement::ImageStore {
357            image: image_lexp.handle,
358            coordinate,
359            array_index,
360            value: value_patched,
361        })
362    }
363
364    pub(super) fn parse_image_load(
365        &mut self,
366        mut words_left: u16,
367        ctx: &mut super::BlockContext,
368        emitter: &mut crate::proc::Emitter,
369        block: &mut crate::Block,
370        block_id: spirv::Word,
371        body_idx: usize,
372    ) -> Result<(), Error> {
373        let start = self.data_offset;
374        let result_type_id = self.next()?;
375        let result_id = self.next()?;
376        let image_id = self.next()?;
377        let coordinate_id = self.next()?;
378
379        let mut image_ops = if words_left != 0 {
380            words_left -= 1;
381            self.next()?
382        } else {
383            0
384        };
385
386        let mut sample = None;
387        let mut level = None;
388        while image_ops != 0 {
389            let bit = 1 << image_ops.trailing_zeros();
390            match spirv::ImageOperands::from_bits_truncate(bit) {
391                spirv::ImageOperands::LOD => {
392                    let lod_expr = self.next()?;
393                    let lod_lexp = self.lookup_expression.lookup(lod_expr)?;
394                    let lod_handle =
395                        self.get_expr_handle(lod_expr, lod_lexp, ctx, emitter, block, body_idx);
396                    level = Some(lod_handle);
397                    words_left -= 1;
398                }
399                spirv::ImageOperands::SAMPLE => {
400                    let sample_expr = self.next()?;
401                    let sample_handle = self.lookup_expression.lookup(sample_expr)?.handle;
402                    sample = Some(sample_handle);
403                    words_left -= 1;
404                }
405                other => {
406                    log::warn!("Unknown image load op {other:?}");
407                    for _ in 0..words_left {
408                        self.next()?;
409                    }
410                    break;
411                }
412            }
413            image_ops ^= bit;
414        }
415
416        // No need to call get_expr_handle here since only globals/arguments are
417        // allowed as images and they are always in the root scope
418        let image_lexp = self.lookup_expression.lookup(image_id)?;
419        let image_ty = ctx.get_image_expr_ty(image_lexp.handle)?;
420
421        let coord_lexp = self.lookup_expression.lookup(coordinate_id)?;
422        let coord_handle =
423            self.get_expr_handle(coordinate_id, coord_lexp, ctx, emitter, block, body_idx);
424        let coord_type_handle = self.lookup_type.lookup(coord_lexp.type_id)?.handle;
425        let (coordinate, array_index, is_depth) = match ctx.module.types[image_ty].inner {
426            crate::TypeInner::Image {
427                dim,
428                arrayed,
429                class,
430            } => {
431                let (coord, array_index) = extract_image_coordinates(
432                    dim,
433                    if arrayed {
434                        ExtraCoordinate::ArrayLayer
435                    } else {
436                        ExtraCoordinate::Garbage
437                    },
438                    coord_handle,
439                    coord_type_handle,
440                    ctx,
441                );
442                (coord, array_index, class.is_depth())
443            }
444            _ => return Err(Error::InvalidImage(image_ty)),
445        };
446
447        let image_load_expr = crate::Expression::ImageLoad {
448            image: image_lexp.handle,
449            coordinate,
450            array_index,
451            sample,
452            level,
453        };
454        let image_load_handle = ctx
455            .expressions
456            .append(image_load_expr, self.span_from_with_op(start));
457
458        let handle = if is_depth {
459            let result_ty = self.lookup_type.lookup(result_type_id)?;
460            // The return type of `OpImageRead` can be a scalar or vector.
461            match ctx.module.types[result_ty.handle].inner {
462                crate::TypeInner::Vector { size, .. } => {
463                    let splat_expr = crate::Expression::Splat {
464                        size,
465                        value: image_load_handle,
466                    };
467                    ctx.expressions
468                        .append(splat_expr, self.span_from_with_op(start))
469                }
470                _ => image_load_handle,
471            }
472        } else {
473            image_load_handle
474        };
475
476        self.lookup_expression.insert(
477            result_id,
478            LookupExpression {
479                handle,
480                type_id: result_type_id,
481                block_id,
482            },
483        );
484        Ok(())
485    }
486
487    #[allow(clippy::too_many_arguments)]
488    pub(super) fn parse_image_sample(
489        &mut self,
490        mut words_left: u16,
491        options: SamplingOptions,
492        ctx: &mut super::BlockContext,
493        emitter: &mut crate::proc::Emitter,
494        block: &mut crate::Block,
495        block_id: spirv::Word,
496        body_idx: usize,
497    ) -> Result<(), Error> {
498        let start = self.data_offset;
499        let result_type_id = self.next()?;
500        let result_id = self.next()?;
501        let sampled_image_id = self.next()?;
502        let coordinate_id = self.next()?;
503        let dref_id = if options.compare {
504            Some(self.next()?)
505        } else {
506            None
507        };
508        let span = self.span_from_with_op(start);
509
510        let mut image_ops = if words_left != 0 {
511            words_left -= 1;
512            self.next()?
513        } else {
514            0
515        };
516
517        let mut level = crate::SampleLevel::Auto;
518        let mut offset = None;
519        while image_ops != 0 {
520            let bit = 1 << image_ops.trailing_zeros();
521            match spirv::ImageOperands::from_bits_truncate(bit) {
522                spirv::ImageOperands::BIAS => {
523                    let bias_expr = self.next()?;
524                    let bias_lexp = self.lookup_expression.lookup(bias_expr)?;
525                    let bias_handle =
526                        self.get_expr_handle(bias_expr, bias_lexp, ctx, emitter, block, body_idx);
527                    level = crate::SampleLevel::Bias(bias_handle);
528                    words_left -= 1;
529                }
530                spirv::ImageOperands::LOD => {
531                    let lod_expr = self.next()?;
532                    let lod_lexp = self.lookup_expression.lookup(lod_expr)?;
533                    let lod_handle =
534                        self.get_expr_handle(lod_expr, lod_lexp, ctx, emitter, block, body_idx);
535
536                    let is_depth_image = {
537                        let image_lexp = self.lookup_sampled_image.lookup(sampled_image_id)?;
538                        let image_ty = ctx.get_image_expr_ty(image_lexp.image)?;
539                        matches!(
540                            ctx.module.types[image_ty].inner,
541                            crate::TypeInner::Image {
542                                class: crate::ImageClass::Depth { .. },
543                                ..
544                            }
545                        )
546                    };
547
548                    level = if options.compare {
549                        log::debug!("Assuming {lod_handle:?} is zero");
550                        crate::SampleLevel::Zero
551                    } else if is_depth_image {
552                        log::debug!(
553                            "Assuming level {lod_handle:?} converts losslessly to an integer"
554                        );
555                        let expr = crate::Expression::As {
556                            expr: lod_handle,
557                            kind: crate::ScalarKind::Sint,
558                            convert: Some(4),
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 sampling_bit = if options.compare {
633            SamplingFlags::COMPARISON
634        } else {
635            SamplingFlags::REGULAR
636        };
637
638        let image_ty = match ctx.expressions[si_lexp.image] {
639            crate::Expression::GlobalVariable(handle) => {
640                if let Some(flags) = self.handle_sampling.get_mut(&handle) {
641                    *flags |= sampling_bit;
642                }
643
644                ctx.module.global_variables[handle].ty
645            }
646
647            crate::Expression::FunctionArgument(i) => {
648                ctx.parameter_sampling[i as usize] |= sampling_bit;
649                ctx.arguments[i as usize].ty
650            }
651
652            crate::Expression::Access { base, .. } => match ctx.expressions[base] {
653                crate::Expression::GlobalVariable(handle) => {
654                    if let Some(flags) = self.handle_sampling.get_mut(&handle) {
655                        *flags |= sampling_bit;
656                    }
657
658                    match ctx.module.types[ctx.module.global_variables[handle].ty].inner {
659                        crate::TypeInner::BindingArray { base, .. } => base,
660                        _ => return Err(Error::InvalidGlobalVar(ctx.expressions[base].clone())),
661                    }
662                }
663
664                ref other => return Err(Error::InvalidGlobalVar(other.clone())),
665            },
666
667            ref other => return Err(Error::InvalidGlobalVar(other.clone())),
668        };
669
670        match ctx.expressions[si_lexp.sampler] {
671            crate::Expression::GlobalVariable(handle) => {
672                *self.handle_sampling.get_mut(&handle).unwrap() |= sampling_bit;
673            }
674
675            crate::Expression::FunctionArgument(i) => {
676                ctx.parameter_sampling[i as usize] |= sampling_bit;
677            }
678
679            crate::Expression::Access { base, .. } => match ctx.expressions[base] {
680                crate::Expression::GlobalVariable(handle) => {
681                    *self.handle_sampling.get_mut(&handle).unwrap() |= sampling_bit;
682                }
683
684                ref other => return Err(Error::InvalidGlobalVar(other.clone())),
685            },
686
687            ref other => return Err(Error::InvalidGlobalVar(other.clone())),
688        }
689
690        let ((coordinate, array_index), depth_ref, is_depth) =
691            match ctx.module.types[image_ty].inner {
692                crate::TypeInner::Image {
693                    dim,
694                    arrayed,
695                    class,
696                } => (
697                    extract_image_coordinates(
698                        dim,
699                        if options.project {
700                            ExtraCoordinate::Projection
701                        } else if arrayed {
702                            ExtraCoordinate::ArrayLayer
703                        } else {
704                            ExtraCoordinate::Garbage
705                        },
706                        coord_handle,
707                        coord_type_handle,
708                        ctx,
709                    ),
710                    {
711                        match dref_id {
712                            Some(id) => {
713                                let expr_lexp = self.lookup_expression.lookup(id)?;
714                                let mut expr = self
715                                    .get_expr_handle(id, expr_lexp, ctx, emitter, block, body_idx);
716
717                                if options.project {
718                                    let required_size = dim.required_coordinate_size();
719                                    let right = ctx.expressions.append(
720                                        crate::Expression::AccessIndex {
721                                            base: coord_handle,
722                                            index: required_size.map_or(1, |size| size as u32),
723                                        },
724                                        crate::Span::default(),
725                                    );
726                                    expr = ctx.expressions.append(
727                                        crate::Expression::Binary {
728                                            op: crate::BinaryOperator::Divide,
729                                            left: expr,
730                                            right,
731                                        },
732                                        crate::Span::default(),
733                                    )
734                                };
735                                Some(expr)
736                            }
737                            None => None,
738                        }
739                    },
740                    class.is_depth(),
741                ),
742                _ => return Err(Error::InvalidImage(image_ty)),
743            };
744
745        let expr = crate::Expression::ImageSample {
746            image: si_lexp.image,
747            sampler: si_lexp.sampler,
748            gather: None, //TODO
749            coordinate,
750            array_index,
751            offset,
752            level,
753            depth_ref,
754            clamp_to_edge: false,
755        };
756        let image_sample_handle = ctx.expressions.append(expr, self.span_from_with_op(start));
757        let handle = if is_depth && depth_ref.is_none() {
758            let splat_expr = crate::Expression::Splat {
759                size: crate::VectorSize::Quad,
760                value: image_sample_handle,
761            };
762            ctx.expressions
763                .append(splat_expr, self.span_from_with_op(start))
764        } else {
765            image_sample_handle
766        };
767        self.lookup_expression.insert(
768            result_id,
769            LookupExpression {
770                handle,
771                type_id: result_type_id,
772                block_id,
773            },
774        );
775        Ok(())
776    }
777
778    pub(super) fn parse_image_query_size(
779        &mut self,
780        at_level: bool,
781        ctx: &mut super::BlockContext,
782        emitter: &mut crate::proc::Emitter,
783        block: &mut crate::Block,
784        block_id: spirv::Word,
785        body_idx: usize,
786    ) -> Result<(), Error> {
787        let start = self.data_offset;
788        let result_type_id = self.next()?;
789        let result_id = self.next()?;
790        let image_id = self.next()?;
791        let level = if at_level {
792            let level_id = self.next()?;
793            let level_lexp = self.lookup_expression.lookup(level_id)?;
794            Some(self.get_expr_handle(level_id, level_lexp, ctx, emitter, block, body_idx))
795        } else {
796            None
797        };
798
799        // No need to call get_expr_handle here since only globals/arguments are
800        // allowed as images and they are always in the root scope
801        //TODO: handle arrays and cubes
802        let image_lexp = self.lookup_expression.lookup(image_id)?;
803
804        let expr = crate::Expression::ImageQuery {
805            image: image_lexp.handle,
806            query: crate::ImageQuery::Size { level },
807        };
808
809        let result_type_handle = self.lookup_type.lookup(result_type_id)?.handle;
810        let maybe_scalar_kind = ctx.module.types[result_type_handle].inner.scalar_kind();
811
812        let expr = if maybe_scalar_kind == Some(crate::ScalarKind::Sint) {
813            crate::Expression::As {
814                expr: ctx.expressions.append(expr, self.span_from_with_op(start)),
815                kind: crate::ScalarKind::Sint,
816                convert: Some(4),
817            }
818        } else {
819            expr
820        };
821
822        self.lookup_expression.insert(
823            result_id,
824            LookupExpression {
825                handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
826                type_id: result_type_id,
827                block_id,
828            },
829        );
830
831        Ok(())
832    }
833
834    pub(super) fn parse_image_query_other(
835        &mut self,
836        query: crate::ImageQuery,
837        ctx: &mut super::BlockContext,
838        block_id: spirv::Word,
839    ) -> Result<(), Error> {
840        let start = self.data_offset;
841        let result_type_id = self.next()?;
842        let result_id = self.next()?;
843        let image_id = self.next()?;
844
845        // No need to call get_expr_handle here since only globals/arguments are
846        // allowed as images and they are always in the root scope
847        let image_lexp = self.lookup_expression.lookup(image_id)?.clone();
848
849        let expr = crate::Expression::ImageQuery {
850            image: image_lexp.handle,
851            query,
852        };
853
854        let result_type_handle = self.lookup_type.lookup(result_type_id)?.handle;
855        let maybe_scalar_kind = ctx.module.types[result_type_handle].inner.scalar_kind();
856
857        let expr = if maybe_scalar_kind == Some(crate::ScalarKind::Sint) {
858            crate::Expression::As {
859                expr: ctx.expressions.append(expr, self.span_from_with_op(start)),
860                kind: crate::ScalarKind::Sint,
861                convert: Some(4),
862            }
863        } else {
864            expr
865        };
866
867        self.lookup_expression.insert(
868            result_id,
869            LookupExpression {
870                handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
871                type_id: result_type_id,
872                block_id,
873            },
874        );
875
876        Ok(())
877    }
878}