diff --git a/crates/rustc_codegen_spirv/src/attr.rs b/crates/rustc_codegen_spirv/src/attr.rs index 4054ff8700..af2a63dadb 100644 --- a/crates/rustc_codegen_spirv/src/attr.rs +++ b/crates/rustc_codegen_spirv/src/attr.rs @@ -96,6 +96,7 @@ pub enum SpirvAttribute { Binding(u32), Flat, Invariant, + InputAttachmentIndex(u32), // `fn`/closure attributes: UnrollLoops, @@ -130,6 +131,7 @@ pub struct AggregatedSpirvAttributes { pub binding: Option>, pub flat: Option>, pub invariant: Option>, + pub input_attachment_index: Option>, // `fn`/closure attributes: pub unroll_loops: Option>, @@ -215,6 +217,12 @@ impl AggregatedSpirvAttributes { Binding(value) => try_insert(&mut self.binding, value, span, "#[spirv(binding)]"), Flat => try_insert(&mut self.flat, (), span, "#[spirv(flat)]"), Invariant => try_insert(&mut self.invariant, (), span, "#[spirv(invariant)]"), + InputAttachmentIndex(value) => try_insert( + &mut self.input_attachment_index, + value, + span, + "#[spirv(attachment_index)]", + ), UnrollLoops => try_insert(&mut self.unroll_loops, (), span, "#[spirv(unroll_loops)]"), InternalBufferLoad => try_insert( &mut self.internal_buffer_load, @@ -308,7 +316,8 @@ impl CheckSpirvAttrVisitor<'_> { | SpirvAttribute::DescriptorSet(_) | SpirvAttribute::Binding(_) | SpirvAttribute::Flat - | SpirvAttribute::Invariant => match target { + | SpirvAttribute::Invariant + | SpirvAttribute::InputAttachmentIndex(_) => match target { Target::Param => { let parent_hir_id = self.tcx.hir().get_parent_node(hir_id); let parent_is_entry_point = diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs b/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs index 82c4b40ee5..e6f0d12781 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs @@ -6,7 +6,9 @@ use crate::builder_spirv::{SpirvValue, SpirvValueExt}; use crate::codegen_cx::BindlessDescriptorSets; use crate::spirv_type::SpirvType; use rspirv::dr::Operand; -use rspirv::spirv::{Capability, Decoration, ExecutionModel, FunctionControl, StorageClass, Word}; +use rspirv::spirv::{ + Capability, Decoration, Dim, ExecutionModel, FunctionControl, StorageClass, Word, +}; use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods}; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; @@ -663,6 +665,46 @@ impl<'tcx> CodegenCx<'tcx> { } } + let is_subpass_input = match self.lookup_type(value_spirv_type) { + SpirvType::Image { + dim: Dim::DimSubpassData, + .. + } => true, + SpirvType::RuntimeArray { element: elt, .. } + | SpirvType::Array { element: elt, .. } => matches!( + self.lookup_type(elt), + SpirvType::Image { + dim: Dim::DimSubpassData, + .. + } + ), + _ => false, + }; + if let Some(attachment_index) = attrs.input_attachment_index { + if is_subpass_input && self.builder.has_capability(Capability::InputAttachment) { + self.emit_global().decorate( + var, + Decoration::InputAttachmentIndex, + std::iter::once(Operand::LiteralInt32(attachment_index.value)), + ) + } else if is_subpass_input { + self.tcx + .sess + .span_err(hir_param.ty_span, "Missing capability InputAttachment") + } else { + self.tcx.sess.span_err( + attachment_index.span, + "#[spirv(input_attachment_index)] is only valid on Image types with dim = SubpassData" + ); + } + decoration_supersedes_location = true; + } else if is_subpass_input { + self.tcx.sess.span_err( + hir_param.ty_span, + "Image types with dim = SubpassData require #[spirv(input_attachment_index)] decoration", + ) + } + // Assign locations from left to right, incrementing each storage class // individually. // TODO: Is this right for UniformConstant? Do they share locations with diff --git a/crates/rustc_codegen_spirv/src/symbols.rs b/crates/rustc_codegen_spirv/src/symbols.rs index c243cc42f4..94a447e97e 100644 --- a/crates/rustc_codegen_spirv/src/symbols.rs +++ b/crates/rustc_codegen_spirv/src/symbols.rs @@ -23,6 +23,7 @@ pub struct Symbols { pub entry_point_name: Symbol, descriptor_set: Symbol, binding: Symbol, + input_attachment_index: Symbol, image_type: Symbol, dim: Symbol, depth: Symbol, @@ -380,6 +381,7 @@ impl Symbols { num_traits: Symbol::intern("num_traits"), descriptor_set: Symbol::intern("descriptor_set"), binding: Symbol::intern("binding"), + input_attachment_index: Symbol::intern("input_attachment_index"), image_type: Symbol::intern("image_type"), dim: Symbol::intern("dim"), depth: Symbol::intern("depth"), @@ -452,6 +454,8 @@ pub(crate) fn parse_attrs_for_checking<'a>( SpirvAttribute::DescriptorSet(parse_attr_int_value(arg)?) } else if arg.has_name(sym.binding) { SpirvAttribute::Binding(parse_attr_int_value(arg)?) + } else if arg.has_name(sym.input_attachment_index) { + SpirvAttribute::InputAttachmentIndex(parse_attr_int_value(arg)?) } else { let name = match arg.ident() { Some(i) => i, diff --git a/crates/spirv-std/src/image.rs b/crates/spirv-std/src/image.rs index 3d0dab3842..076833ebbf 100644 --- a/crates/spirv-std/src/image.rs +++ b/crates/spirv-std/src/image.rs @@ -5,7 +5,7 @@ #[rustfmt::skip] mod params; -pub use self::params::{ImageCoordinate, SampleType}; +pub use self::params::{ImageCoordinate, ImageCoordinateSubpassData, SampleType}; pub use crate::macros::Image; pub use spirv_types::image_params::{ AccessQualifier, Arrayed, Dimensionality, ImageDepth, ImageFormat, Multisampled, Sampled, @@ -659,6 +659,55 @@ impl< } } +impl< + SampledType: SampleType, + const DEPTH: ImageDepth, + const ARRAYED: Arrayed, + const MULTISAMPLED: Multisampled, + const FORMAT: ImageFormat, + const ACCESS_QUALIFIER: Option, + > + Image< + SampledType, + { Dimensionality::SubpassData }, + DEPTH, + ARRAYED, + MULTISAMPLED, + { Sampled::No }, + FORMAT, + ACCESS_QUALIFIER, + > +{ + /// Read a texel from subpass input attachment. + /// Note: Vulkan only allows the read if the first two components of the coordinate are zero. + #[crate::macros::gpu_only] + #[doc(alias = "OpImageRead")] + pub fn read_subpass( + &self, + coordinate: impl ImageCoordinateSubpassData, + ) -> V + where + I: Integer, + V: Vector, + { + let mut result = V::default(); + + unsafe { + asm! { + "%image = OpLoad _ {this}", + "%coordinate = OpLoad _ {coordinate}", + "%result = OpImageRead typeof*{result} %image %coordinate", + "OpStore {result} %result", + this = in(reg) self, + coordinate = in(reg) &coordinate, + result = in(reg) &mut result, + } + } + + result + } +} + impl< SampledType: SampleType, const DIM: Dimensionality, diff --git a/crates/spirv-std/src/image/params.rs b/crates/spirv-std/src/image/params.rs index b46d0eb0dd..53db294413 100644 --- a/crates/spirv-std/src/image/params.rs +++ b/crates/spirv-std/src/image/params.rs @@ -1,5 +1,5 @@ use super::{Arrayed, Dimensionality, ImageFormat}; -use crate::{scalar::Scalar, vector::Vector}; +use crate::{scalar::Scalar, vector::Vector, integer::Integer}; /// Marker trait for arguments that accept single scalar values or vectors /// of scalars. @@ -72,3 +72,8 @@ impl, S: Scalar> ImageCoordinate, S: Scalar> ImageCoordinate for V {} impl, S: Scalar> ImageCoordinate for V {} impl, S: Scalar> ImageCoordinate for V {} + +/// Marker trait for arguments that are valid for a [`crate::image::Dimensionality::SubpassData`] image query. +pub trait ImageCoordinateSubpassData {} +impl, I: Integer> ImageCoordinateSubpassData for V {} +impl, I: Integer> ImageCoordinateSubpassData for V {} diff --git a/tests/ui/image/query/query_levels_err.stderr b/tests/ui/image/query/query_levels_err.stderr index 6fa7f1f320..4472efa8f8 100644 --- a/tests/ui/image/query/query_levels_err.stderr +++ b/tests/ui/image/query/query_levels_err.stderr @@ -1,12 +1,12 @@ error: OpImageQueryLevels's image has a dimension of DimRect - --> $SPIRV_STD_SRC/image.rs:684:13 + --> $SPIRV_STD_SRC/image.rs:733:13 | -684 | / asm! { -685 | | "%image = OpLoad _ {this}", -686 | | "{result} = OpImageQueryLevels typeof{result} %image", -687 | | this = in(reg) self, -688 | | result = out(reg) result, -689 | | } +733 | / asm! { +734 | | "%image = OpLoad _ {this}", +735 | | "{result} = OpImageQueryLevels typeof{result} %image", +736 | | this = in(reg) self, +737 | | result = out(reg) result, +738 | | } | |_____________^ | = note: Allowed dimensions are 1D, 2D, 3D, and Cube diff --git a/tests/ui/image/query/query_lod_err.stderr b/tests/ui/image/query/query_lod_err.stderr index 015d851551..db310c8456 100644 --- a/tests/ui/image/query/query_lod_err.stderr +++ b/tests/ui/image/query/query_lod_err.stderr @@ -1,13 +1,13 @@ error: OpImageQueryLod's image has a dimension of DimRect - --> $SPIRV_STD_SRC/image.rs:717:13 + --> $SPIRV_STD_SRC/image.rs:766:13 | -717 | / asm! { -718 | | "%typeSampledImage = OpTypeSampledImage typeof*{this}", -719 | | "%image = OpLoad _ {this}", -720 | | "%sampler = OpLoad _ {sampler}", +766 | / asm! { +767 | | "%typeSampledImage = OpTypeSampledImage typeof*{this}", +768 | | "%image = OpLoad _ {this}", +769 | | "%sampler = OpLoad _ {sampler}", ... | -728 | | coord = in(reg) &coord -729 | | } +777 | | coord = in(reg) &coord +778 | | } | |_____________^ | = note: Allowed dimensions are 1D, 2D, 3D, and Cube diff --git a/tests/ui/image/query/query_size_err.stderr b/tests/ui/image/query/query_size_err.stderr index a54a9eb8ef..6d16ef8f57 100644 --- a/tests/ui/image/query/query_size_err.stderr +++ b/tests/ui/image/query/query_size_err.stderr @@ -1,13 +1,13 @@ error: OpImageQuerySize is invalid for this image type - --> $SPIRV_STD_SRC/image.rs:746:13 + --> $SPIRV_STD_SRC/image.rs:795:13 | -746 | / asm! { -747 | | "%image = OpLoad _ {this}", -748 | | "%result = OpImageQuerySize typeof*{result} %image", -749 | | "OpStore {result} %result", -750 | | this = in(reg) self, -751 | | result = in(reg) &mut result, -752 | | } +795 | / asm! { +796 | | "%image = OpLoad _ {this}", +797 | | "%result = OpImageQuerySize typeof*{result} %image", +798 | | "OpStore {result} %result", +799 | | this = in(reg) self, +800 | | result = in(reg) &mut result, +801 | | } | |_____________^ | = note: allowed dimensions are 1D, 2D, 3D, Buffer, Rect, or Cube. if dimension is 1D, 2D, 3D, or Cube, it must have either multisampled be true, *or* sampled of Unknown or No diff --git a/tests/ui/image/query/query_size_lod_err.stderr b/tests/ui/image/query/query_size_lod_err.stderr index ac5221dfdd..f50604a384 100644 --- a/tests/ui/image/query/query_size_lod_err.stderr +++ b/tests/ui/image/query/query_size_lod_err.stderr @@ -1,13 +1,13 @@ error: OpImageQuerySizeLod is invalid for this image type - --> $SPIRV_STD_SRC/image.rs:792:13 + --> $SPIRV_STD_SRC/image.rs:841:13 | -792 | / asm! { -793 | | "%image = OpLoad _ {this}", -794 | | "%result = OpImageQuerySizeLod typeof*{result} %image {lod}", -795 | | "OpStore {result} %result", +841 | / asm! { +842 | | "%image = OpLoad _ {this}", +843 | | "%result = OpImageQuerySizeLod typeof*{result} %image {lod}", +844 | | "OpStore {result} %result", ... | -798 | | result = in(reg) &mut result, -799 | | } +847 | | result = in(reg) &mut result, +848 | | } | |_____________^ | = note: The image's dimension must be 1D, 2D, 3D, or Cube. Multisampled must be false. diff --git a/tests/ui/image/read_subpass.rs b/tests/ui/image/read_subpass.rs new file mode 100644 index 0000000000..9ef6610742 --- /dev/null +++ b/tests/ui/image/read_subpass.rs @@ -0,0 +1,13 @@ +// build-pass +// compile-flags: -C target-feature=+InputAttachment + +use spirv_std::{arch, Image}; + +#[spirv(fragment)] +pub fn main( + #[spirv(descriptor_set = 0, binding = 0, input_attachment_index = 0)] image: &Image!(subpass, type=f32, sampled=false), + output: &mut glam::Vec4, +) { + let coords = image.read_subpass(glam::IVec2::new(0, 0)); + *output = coords; +}