1 use crate::arena::{Arena, Handle, UniqueArena};
2
3 use super::{Error, LookupExpression, LookupHelper as _};
4
5 #[derive(Clone, Debug)]
6 pub(super) struct LookupSampledImage {
7 image: Handle<crate::Expression>,
8 sampler: Handle<crate::Expression>,
9 }
10
11 bitflags::bitflags! {
12 /// Flags describing sampling method.
13 pub struct SamplingFlags: u32 {
14 /// Regular sampling.
15 const REGULAR = 0x1;
16 /// Comparison sampling.
17 const COMPARISON = 0x2;
18 }
19 }
20
21 impl<'function> super::BlockContext<'function> {
get_image_expr_ty( &self, handle: Handle<crate::Expression>, ) -> Result<Handle<crate::Type>, Error>22 fn get_image_expr_ty(
23 &self,
24 handle: Handle<crate::Expression>,
25 ) -> Result<Handle<crate::Type>, Error> {
26 match self.expressions[handle] {
27 crate::Expression::GlobalVariable(handle) => Ok(self.global_arena[handle].ty),
28 crate::Expression::FunctionArgument(i) => Ok(self.arguments[i as usize].ty),
29 ref other => Err(Error::InvalidImageExpression(other.clone())),
30 }
31 }
32 }
33
34 /// Options of a sampling operation.
35 #[derive(Debug)]
36 pub struct SamplingOptions {
37 /// Projection sampling: the division by W is expected to happen
38 /// in the texture unit.
39 pub project: bool,
40 /// Depth comparison sampling with a reference value.
41 pub compare: bool,
42 }
43
44 enum ExtraCoordinate {
45 ArrayLayer,
46 Projection,
47 Garbage,
48 }
49
50 /// Return the texture coordinates separated from the array layer,
51 /// and/or divided by the projection term.
52 ///
53 /// The Proj sampling ops expect an extra coordinate for the W.
54 /// The arrayed (can't be Proj!) images expect an extra coordinate for the layer.
extract_image_coordinates( image_dim: crate::ImageDimension, extra_coordinate: ExtraCoordinate, base: Handle<crate::Expression>, coordinate_ty: Handle<crate::Type>, ctx: &mut super::BlockContext, ) -> (Handle<crate::Expression>, Option<Handle<crate::Expression>>)55 fn extract_image_coordinates(
56 image_dim: crate::ImageDimension,
57 extra_coordinate: ExtraCoordinate,
58 base: Handle<crate::Expression>,
59 coordinate_ty: Handle<crate::Type>,
60 ctx: &mut super::BlockContext,
61 ) -> (Handle<crate::Expression>, Option<Handle<crate::Expression>>) {
62 let (given_size, kind) = match ctx.type_arena[coordinate_ty].inner {
63 crate::TypeInner::Scalar { kind, .. } => (None, kind),
64 crate::TypeInner::Vector { size, kind, .. } => (Some(size), kind),
65 ref other => unreachable!("Unexpected texture coordinate {:?}", other),
66 };
67
68 let required_size = image_dim.required_coordinate_size();
69 let required_ty = required_size.map(|size| {
70 ctx.type_arena
71 .get(&crate::Type {
72 name: None,
73 inner: crate::TypeInner::Vector {
74 size,
75 kind,
76 width: 4,
77 },
78 })
79 .expect("Required coordinate type should have been set up by `parse_type_image`!")
80 });
81 let extra_expr = crate::Expression::AccessIndex {
82 base,
83 index: required_size.map_or(1, |size| size as u32),
84 };
85
86 let base_span = ctx.expressions.get_span(base);
87
88 match extra_coordinate {
89 ExtraCoordinate::ArrayLayer => {
90 let extracted = match required_size {
91 None => ctx
92 .expressions
93 .append(crate::Expression::AccessIndex { base, index: 0 }, base_span),
94 Some(size) => {
95 let mut components = Vec::with_capacity(size as usize);
96 for index in 0..size as u32 {
97 let comp = ctx
98 .expressions
99 .append(crate::Expression::AccessIndex { base, index }, base_span);
100 components.push(comp);
101 }
102 ctx.expressions.append(
103 crate::Expression::Compose {
104 ty: required_ty.unwrap(),
105 components,
106 },
107 base_span,
108 )
109 }
110 };
111 let array_index_f32 = ctx.expressions.append(extra_expr, base_span);
112 let array_index = ctx.expressions.append(
113 crate::Expression::As {
114 kind: crate::ScalarKind::Sint,
115 expr: array_index_f32,
116 convert: Some(4),
117 },
118 base_span,
119 );
120 (extracted, Some(array_index))
121 }
122 ExtraCoordinate::Projection => {
123 let projection = ctx.expressions.append(extra_expr, base_span);
124 let divided = match required_size {
125 None => {
126 let temp = ctx
127 .expressions
128 .append(crate::Expression::AccessIndex { base, index: 0 }, base_span);
129 ctx.expressions.append(
130 crate::Expression::Binary {
131 op: crate::BinaryOperator::Divide,
132 left: temp,
133 right: projection,
134 },
135 base_span,
136 )
137 }
138 Some(size) => {
139 let mut components = Vec::with_capacity(size as usize);
140 for index in 0..size as u32 {
141 let temp = ctx
142 .expressions
143 .append(crate::Expression::AccessIndex { base, index }, base_span);
144 let comp = ctx.expressions.append(
145 crate::Expression::Binary {
146 op: crate::BinaryOperator::Divide,
147 left: temp,
148 right: projection,
149 },
150 base_span,
151 );
152 components.push(comp);
153 }
154 ctx.expressions.append(
155 crate::Expression::Compose {
156 ty: required_ty.unwrap(),
157 components,
158 },
159 base_span,
160 )
161 }
162 };
163 (divided, None)
164 }
165 ExtraCoordinate::Garbage if given_size == required_size => (base, None),
166 ExtraCoordinate::Garbage => {
167 use crate::SwizzleComponent as Sc;
168 let cut_expr = match required_size {
169 None => crate::Expression::AccessIndex { base, index: 0 },
170 Some(size) => crate::Expression::Swizzle {
171 size,
172 vector: base,
173 pattern: [Sc::X, Sc::Y, Sc::Z, Sc::W],
174 },
175 };
176 (ctx.expressions.append(cut_expr, base_span), None)
177 }
178 }
179 }
180
patch_comparison_type( flags: SamplingFlags, var: &mut crate::GlobalVariable, arena: &mut UniqueArena<crate::Type>, ) -> bool181 pub(super) fn patch_comparison_type(
182 flags: SamplingFlags,
183 var: &mut crate::GlobalVariable,
184 arena: &mut UniqueArena<crate::Type>,
185 ) -> bool {
186 if !flags.contains(SamplingFlags::COMPARISON) {
187 return true;
188 }
189 if flags == SamplingFlags::all() {
190 return false;
191 }
192
193 log::debug!("Flipping comparison for {:?}", var);
194 let original_ty = &arena[var.ty];
195 let original_ty_span = arena.get_span(var.ty);
196 let ty_inner = match original_ty.inner {
197 crate::TypeInner::Image {
198 class: crate::ImageClass::Sampled { multi, .. },
199 dim,
200 arrayed,
201 } => crate::TypeInner::Image {
202 class: crate::ImageClass::Depth { multi },
203 dim,
204 arrayed,
205 },
206 crate::TypeInner::Sampler { .. } => crate::TypeInner::Sampler { comparison: true },
207 ref other => unreachable!("Unexpected type for comparison mutation: {:?}", other),
208 };
209
210 let name = original_ty.name.clone();
211 var.ty = arena.insert(
212 crate::Type {
213 name,
214 inner: ty_inner,
215 },
216 original_ty_span,
217 );
218 true
219 }
220
221 impl<I: Iterator<Item = u32>> super::Parser<I> {
parse_image_couple(&mut self) -> Result<(), Error>222 pub(super) fn parse_image_couple(&mut self) -> Result<(), Error> {
223 let _result_type_id = self.next()?;
224 let result_id = self.next()?;
225 let image_id = self.next()?;
226 let sampler_id = self.next()?;
227 let image_lexp = self.lookup_expression.lookup(image_id)?;
228 let sampler_lexp = self.lookup_expression.lookup(sampler_id)?;
229 self.lookup_sampled_image.insert(
230 result_id,
231 LookupSampledImage {
232 image: image_lexp.handle,
233 sampler: sampler_lexp.handle,
234 },
235 );
236 Ok(())
237 }
238
parse_image_uncouple(&mut self, block_id: spirv::Word) -> Result<(), Error>239 pub(super) fn parse_image_uncouple(&mut self, block_id: spirv::Word) -> Result<(), Error> {
240 let result_type_id = self.next()?;
241 let result_id = self.next()?;
242 let sampled_image_id = self.next()?;
243 self.lookup_expression.insert(
244 result_id,
245 LookupExpression {
246 handle: self.lookup_sampled_image.lookup(sampled_image_id)?.image,
247 type_id: result_type_id,
248 block_id,
249 },
250 );
251 Ok(())
252 }
253
parse_image_write( &mut self, words_left: u16, ctx: &mut super::BlockContext, emitter: &mut crate::front::Emitter, block: &mut crate::Block, body_idx: usize, ) -> Result<crate::Statement, Error>254 pub(super) fn parse_image_write(
255 &mut self,
256 words_left: u16,
257 ctx: &mut super::BlockContext,
258 emitter: &mut crate::front::Emitter,
259 block: &mut crate::Block,
260 body_idx: usize,
261 ) -> Result<crate::Statement, Error> {
262 let image_id = self.next()?;
263 let coordinate_id = self.next()?;
264 let value_id = self.next()?;
265
266 let image_ops = if words_left != 0 { self.next()? } else { 0 };
267
268 if image_ops != 0 {
269 let other = spirv::ImageOperands::from_bits_truncate(image_ops);
270 log::warn!("Unknown image write ops {:?}", other);
271 for _ in 1..words_left {
272 self.next()?;
273 }
274 }
275
276 let image_lexp = self.lookup_expression.lookup(image_id)?;
277 let image_ty = ctx.get_image_expr_ty(image_lexp.handle)?;
278
279 let coord_lexp = self.lookup_expression.lookup(coordinate_id)?;
280 let coord_handle =
281 self.get_expr_handle(coordinate_id, coord_lexp, ctx, emitter, block, body_idx);
282 let coord_type_handle = self.lookup_type.lookup(coord_lexp.type_id)?.handle;
283 let (coordinate, array_index) = match ctx.type_arena[image_ty].inner {
284 crate::TypeInner::Image {
285 dim,
286 arrayed,
287 class: _,
288 } => extract_image_coordinates(
289 dim,
290 if arrayed {
291 ExtraCoordinate::ArrayLayer
292 } else {
293 ExtraCoordinate::Garbage
294 },
295 coord_handle,
296 coord_type_handle,
297 ctx,
298 ),
299 _ => return Err(Error::InvalidImage(image_ty)),
300 };
301
302 let value_lexp = self.lookup_expression.lookup(value_id)?;
303 let value = self.get_expr_handle(value_id, value_lexp, ctx, emitter, block, body_idx);
304
305 Ok(crate::Statement::ImageStore {
306 image: image_lexp.handle,
307 coordinate,
308 array_index,
309 value,
310 })
311 }
312
parse_image_load( &mut self, mut words_left: u16, ctx: &mut super::BlockContext, emitter: &mut crate::front::Emitter, block: &mut crate::Block, block_id: spirv::Word, body_idx: usize, ) -> Result<(), Error>313 pub(super) fn parse_image_load(
314 &mut self,
315 mut words_left: u16,
316 ctx: &mut super::BlockContext,
317 emitter: &mut crate::front::Emitter,
318 block: &mut crate::Block,
319 block_id: spirv::Word,
320 body_idx: usize,
321 ) -> Result<(), Error> {
322 let start = self.data_offset;
323 let result_type_id = self.next()?;
324 let result_id = self.next()?;
325 let image_id = self.next()?;
326 let coordinate_id = self.next()?;
327
328 let mut image_ops = if words_left != 0 {
329 words_left -= 1;
330 self.next()?
331 } else {
332 0
333 };
334
335 let mut index = None;
336 while image_ops != 0 {
337 let bit = 1 << image_ops.trailing_zeros();
338 match spirv::ImageOperands::from_bits_truncate(bit) {
339 spirv::ImageOperands::LOD => {
340 let lod_expr = self.next()?;
341 let lod_lexp = self.lookup_expression.lookup(lod_expr)?;
342 let lod_handle =
343 self.get_expr_handle(lod_expr, lod_lexp, ctx, emitter, block, body_idx);
344 index = Some(lod_handle);
345 words_left -= 1;
346 }
347 spirv::ImageOperands::SAMPLE => {
348 let sample_expr = self.next()?;
349 let sample_handle = self.lookup_expression.lookup(sample_expr)?.handle;
350 index = Some(sample_handle);
351 words_left -= 1;
352 }
353 other => {
354 log::warn!("Unknown image load op {:?}", other);
355 for _ in 0..words_left {
356 self.next()?;
357 }
358 break;
359 }
360 }
361 image_ops ^= bit;
362 }
363
364 // No need to call get_expr_handle here since only globals/arguments are
365 // allowed as images and they are always in the root scope
366 let image_lexp = self.lookup_expression.lookup(image_id)?;
367 let image_ty = ctx.get_image_expr_ty(image_lexp.handle)?;
368
369 let coord_lexp = self.lookup_expression.lookup(coordinate_id)?;
370 let coord_handle =
371 self.get_expr_handle(coordinate_id, coord_lexp, ctx, emitter, block, body_idx);
372 let coord_type_handle = self.lookup_type.lookup(coord_lexp.type_id)?.handle;
373 let (coordinate, array_index) = match ctx.type_arena[image_ty].inner {
374 crate::TypeInner::Image {
375 dim,
376 arrayed,
377 class: _,
378 } => extract_image_coordinates(
379 dim,
380 if arrayed {
381 ExtraCoordinate::ArrayLayer
382 } else {
383 ExtraCoordinate::Garbage
384 },
385 coord_handle,
386 coord_type_handle,
387 ctx,
388 ),
389 _ => return Err(Error::InvalidImage(image_ty)),
390 };
391
392 let expr = crate::Expression::ImageLoad {
393 image: image_lexp.handle,
394 coordinate,
395 array_index,
396 index,
397 };
398 self.lookup_expression.insert(
399 result_id,
400 LookupExpression {
401 handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
402 type_id: result_type_id,
403 block_id,
404 },
405 );
406 Ok(())
407 }
408
409 #[allow(clippy::too_many_arguments)]
parse_image_sample( &mut self, mut words_left: u16, options: SamplingOptions, ctx: &mut super::BlockContext, emitter: &mut crate::front::Emitter, block: &mut crate::Block, block_id: spirv::Word, body_idx: usize, ) -> Result<(), Error>410 pub(super) fn parse_image_sample(
411 &mut self,
412 mut words_left: u16,
413 options: SamplingOptions,
414 ctx: &mut super::BlockContext,
415 emitter: &mut crate::front::Emitter,
416 block: &mut crate::Block,
417 block_id: spirv::Word,
418 body_idx: usize,
419 ) -> Result<(), Error> {
420 let start = self.data_offset;
421 let result_type_id = self.next()?;
422 let result_id = self.next()?;
423 let sampled_image_id = self.next()?;
424 let coordinate_id = self.next()?;
425 let dref_id = if options.compare {
426 Some(self.next()?)
427 } else {
428 None
429 };
430
431 let mut image_ops = if words_left != 0 {
432 words_left -= 1;
433 self.next()?
434 } else {
435 0
436 };
437
438 let mut level = crate::SampleLevel::Auto;
439 let mut offset = None;
440 while image_ops != 0 {
441 let bit = 1 << image_ops.trailing_zeros();
442 match spirv::ImageOperands::from_bits_truncate(bit) {
443 spirv::ImageOperands::BIAS => {
444 let bias_expr = self.next()?;
445 let bias_lexp = self.lookup_expression.lookup(bias_expr)?;
446 let bias_handle =
447 self.get_expr_handle(bias_expr, bias_lexp, ctx, emitter, block, body_idx);
448 level = crate::SampleLevel::Bias(bias_handle);
449 words_left -= 1;
450 }
451 spirv::ImageOperands::LOD => {
452 let lod_expr = self.next()?;
453 let lod_lexp = self.lookup_expression.lookup(lod_expr)?;
454 let lod_handle =
455 self.get_expr_handle(lod_expr, lod_lexp, ctx, emitter, block, body_idx);
456 level = if options.compare {
457 log::debug!("Assuming {:?} is zero", lod_handle);
458 crate::SampleLevel::Zero
459 } else {
460 crate::SampleLevel::Exact(lod_handle)
461 };
462 words_left -= 1;
463 }
464 spirv::ImageOperands::GRAD => {
465 let grad_x_expr = self.next()?;
466 let grad_x_lexp = self.lookup_expression.lookup(grad_x_expr)?;
467 let grad_x_handle = self.get_expr_handle(
468 grad_x_expr,
469 grad_x_lexp,
470 ctx,
471 emitter,
472 block,
473 body_idx,
474 );
475 let grad_y_expr = self.next()?;
476 let grad_y_lexp = self.lookup_expression.lookup(grad_y_expr)?;
477 let grad_y_handle = self.get_expr_handle(
478 grad_y_expr,
479 grad_y_lexp,
480 ctx,
481 emitter,
482 block,
483 body_idx,
484 );
485 level = if options.compare {
486 log::debug!(
487 "Assuming gradients {:?} and {:?} are not greater than 1",
488 grad_x_handle,
489 grad_y_handle
490 );
491 crate::SampleLevel::Zero
492 } else {
493 crate::SampleLevel::Gradient {
494 x: grad_x_handle,
495 y: grad_y_handle,
496 }
497 };
498 words_left -= 2;
499 }
500 spirv::ImageOperands::CONST_OFFSET => {
501 let offset_constant = self.next()?;
502 let offset_handle = self.lookup_constant.lookup(offset_constant)?.handle;
503 offset = Some(offset_handle);
504 words_left -= 1;
505 }
506 other => {
507 log::warn!("Unknown image sample operand {:?}", other);
508 for _ in 0..words_left {
509 self.next()?;
510 }
511 break;
512 }
513 }
514 image_ops ^= bit;
515 }
516
517 let si_lexp = self.lookup_sampled_image.lookup(sampled_image_id)?;
518 let coord_lexp = self.lookup_expression.lookup(coordinate_id)?;
519 let coord_handle =
520 self.get_expr_handle(coordinate_id, coord_lexp, ctx, emitter, block, body_idx);
521 let coord_type_handle = self.lookup_type.lookup(coord_lexp.type_id)?.handle;
522
523 let sampling_bit = if options.compare {
524 SamplingFlags::COMPARISON
525 } else {
526 SamplingFlags::REGULAR
527 };
528
529 let image_ty = match ctx.expressions[si_lexp.image] {
530 crate::Expression::GlobalVariable(handle) => {
531 if let Some(flags) = self.handle_sampling.get_mut(&handle) {
532 *flags |= sampling_bit;
533 }
534
535 ctx.global_arena[handle].ty
536 }
537 crate::Expression::FunctionArgument(i) => {
538 ctx.parameter_sampling[i as usize] |= sampling_bit;
539 ctx.arguments[i as usize].ty
540 }
541 ref other => return Err(Error::InvalidGlobalVar(other.clone())),
542 };
543 match ctx.expressions[si_lexp.sampler] {
544 crate::Expression::GlobalVariable(handle) => {
545 *self.handle_sampling.get_mut(&handle).unwrap() |= sampling_bit
546 }
547 crate::Expression::FunctionArgument(i) => {
548 ctx.parameter_sampling[i as usize] |= sampling_bit;
549 }
550 ref other => return Err(Error::InvalidGlobalVar(other.clone())),
551 }
552
553 let ((coordinate, array_index), depth_ref) = match ctx.type_arena[image_ty].inner {
554 crate::TypeInner::Image {
555 dim,
556 arrayed,
557 class: _,
558 } => (
559 extract_image_coordinates(
560 dim,
561 if options.project {
562 ExtraCoordinate::Projection
563 } else if arrayed {
564 ExtraCoordinate::ArrayLayer
565 } else {
566 ExtraCoordinate::Garbage
567 },
568 coord_handle,
569 coord_type_handle,
570 ctx,
571 ),
572 {
573 match dref_id {
574 Some(id) => {
575 let expr_lexp = self.lookup_expression.lookup(id)?;
576 let mut expr =
577 self.get_expr_handle(id, expr_lexp, ctx, emitter, block, body_idx);
578
579 if options.project {
580 let required_size = dim.required_coordinate_size();
581 let right = ctx.expressions.append(
582 crate::Expression::AccessIndex {
583 base: coord_handle,
584 index: required_size.map_or(1, |size| size as u32),
585 },
586 crate::Span::default(),
587 );
588 expr = ctx.expressions.append(
589 crate::Expression::Binary {
590 op: crate::BinaryOperator::Divide,
591 left: expr,
592 right,
593 },
594 crate::Span::default(),
595 )
596 };
597 Some(expr)
598 }
599 None => None,
600 }
601 },
602 ),
603 _ => return Err(Error::InvalidImage(image_ty)),
604 };
605
606 let expr = crate::Expression::ImageSample {
607 image: si_lexp.image,
608 sampler: si_lexp.sampler,
609 gather: None, //TODO
610 coordinate,
611 array_index,
612 offset,
613 level,
614 depth_ref,
615 };
616 self.lookup_expression.insert(
617 result_id,
618 LookupExpression {
619 handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
620 type_id: result_type_id,
621 block_id,
622 },
623 );
624 Ok(())
625 }
626
parse_image_query_size( &mut self, at_level: bool, ctx: &mut super::BlockContext, emitter: &mut crate::front::Emitter, block: &mut crate::Block, block_id: spirv::Word, body_idx: usize, ) -> Result<(), Error>627 pub(super) fn parse_image_query_size(
628 &mut self,
629 at_level: bool,
630 ctx: &mut super::BlockContext,
631 emitter: &mut crate::front::Emitter,
632 block: &mut crate::Block,
633 block_id: spirv::Word,
634 body_idx: usize,
635 ) -> Result<(), Error> {
636 let start = self.data_offset;
637 let result_type_id = self.next()?;
638 let result_id = self.next()?;
639 let image_id = self.next()?;
640 let level = if at_level {
641 let level_id = self.next()?;
642 let level_lexp = self.lookup_expression.lookup(level_id)?;
643 Some(self.get_expr_handle(level_id, level_lexp, ctx, emitter, block, body_idx))
644 } else {
645 None
646 };
647
648 // No need to call get_expr_handle here since only globals/arguments are
649 // allowed as images and they are always in the root scope
650 //TODO: handle arrays and cubes
651 let image_lexp = self.lookup_expression.lookup(image_id)?;
652
653 let expr = crate::Expression::ImageQuery {
654 image: image_lexp.handle,
655 query: crate::ImageQuery::Size { level },
656 };
657 self.lookup_expression.insert(
658 result_id,
659 LookupExpression {
660 handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
661 type_id: result_type_id,
662 block_id,
663 },
664 );
665 Ok(())
666 }
667
parse_image_query_other( &mut self, query: crate::ImageQuery, expressions: &mut Arena<crate::Expression>, block_id: spirv::Word, ) -> Result<(), Error>668 pub(super) fn parse_image_query_other(
669 &mut self,
670 query: crate::ImageQuery,
671 expressions: &mut Arena<crate::Expression>,
672 block_id: spirv::Word,
673 ) -> Result<(), Error> {
674 let start = self.data_offset;
675 let result_type_id = self.next()?;
676 let result_id = self.next()?;
677 let image_id = self.next()?;
678
679 // No need to call get_expr_handle here since only globals/arguments are
680 // allowed as images and they are always in the root scope
681 let image_lexp = self.lookup_expression.lookup(image_id)?.clone();
682
683 let expr = crate::Expression::ImageQuery {
684 image: image_lexp.handle,
685 query,
686 };
687 self.lookup_expression.insert(
688 result_id,
689 LookupExpression {
690 handle: expressions.append(expr, self.span_from_with_op(start)),
691 type_id: result_type_id,
692 block_id,
693 },
694 );
695 Ok(())
696 }
697 }
698