diff --git a/docs/SPIR-V.rst b/docs/SPIR-V.rst index 9a4059a7e..a1408169c 100644 --- a/docs/SPIR-V.rst +++ b/docs/SPIR-V.rst @@ -166,6 +166,44 @@ Descriptors To specify which Vulkan descriptor a particular resource binds to, use the ``[[vk::binding(X[, Y])]]`` attribute. +Subpass inputs +~~~~~~~~~~~~~~ + +Within a Vulkan `rendering pass `_, +a subpass can write results to an output target that can then be read by the +next subpass as an input subpass. The "Subpass Input" feature regards the +ability to read an output target. + +Subpasses are read through two new builtin resource types, available only in +pixel shader: + +.. code:: hlsl + + class SubpassInput { + T SubpassLoad(); + }; + + class SubpassInputMS { + T SubpassLoad(int sampleIndex); + }; + +In the above, ``T`` is a scalar or vector type. If omitted, it will defaults to +``float4``. + +Subpass inputs are implicitly addressed by the pixel's (x, y, layer) coordinate. +These objects support reading the subpass input through the methods as shown +in the above. + +A subpass input is selected by using a new attribute ``vk::input_attachment_index``. +For example: + +.. code:: hlsl + + [[vk::input_attachment_index(i)]] SubpassInput input; + +An ``vk::input_attachment_index`` of ``i`` selects the ith entry in the input +pass list. (See Vulkan API spec for more information.) + Push constants ~~~~~~~~~~~~~~ @@ -209,6 +247,9 @@ The namespace ``vk`` will be used for all Vulkan attributes: - ``push_constant``: For marking a variable as the push constant block. Allowed on global variables of struct type. At most one variable can be marked as ``push_constant`` in a shader. +- ``input_attachment_index(X)``: To associate the Xth entry in the input pass + list to the annotated object. Only allowed on objects whose type are + ``SubpassInput`` or ``SubpassInputMS``. - ``builtin("X")``: For specifying an entity should be translated into a certain Vulkan builtin variable. Allowed on function parameters, function returns, and struct fields. diff --git a/tools/clang/include/clang/SPIRV/ModuleBuilder.h b/tools/clang/include/clang/SPIRV/ModuleBuilder.h index 8bcce27b9..834876f83 100644 --- a/tools/clang/include/clang/SPIRV/ModuleBuilder.h +++ b/tools/clang/include/clang/SPIRV/ModuleBuilder.h @@ -357,6 +357,10 @@ public: void decorateDSetBinding(uint32_t targetId, uint32_t setNumber, uint32_t bindingNumber); + /// \brief Decorates the given target with the given input + /// attchment index number. + void decorateInputAttachmentIndex(uint32_t targetId, uint32_t indexNumber); + /// \brief Decorates the given target with the given decoration /// (without additional parameters). void decorate(uint32_t targetId, spv::Decoration); diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp index 9bdbf34a5..4a199f473 100644 --- a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp +++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp @@ -412,6 +412,9 @@ uint32_t DeclResultIdMapper::createExternVar(const VarDecl *var) { resourceVars.emplace_back(id, getResourceCategory(var->getType()), regAttr, bindingAttr, counterBindingAttr); + if (const auto *inputAttachment = var->getAttr()) + theBuilder.decorateInputAttachmentIndex(id, inputAttachment->getIndex()); + if (isACRWSBuffer) { // For {Append|Consume|RW}StructuredBuffer, we need to always create another // variable for its associated counter. @@ -874,7 +877,7 @@ bool DeclResultIdMapper::finalizeStageIOLocations(bool forInput) { // We have checked that not all of the stage variables have explicit // location assignment. emitError("partial explicit stage %select{output|input}0 location " - "assignment via [[vk::location(X)]] unsupported", + "assignment via vk::location(X) unsupported", {}) << forInput; return false; diff --git a/tools/clang/lib/SPIRV/ModuleBuilder.cpp b/tools/clang/lib/SPIRV/ModuleBuilder.cpp index e9f22cfe8..16ca1815f 100644 --- a/tools/clang/lib/SPIRV/ModuleBuilder.cpp +++ b/tools/clang/lib/SPIRV/ModuleBuilder.cpp @@ -761,6 +761,11 @@ void ModuleBuilder::decorateDSetBinding(uint32_t targetId, uint32_t setNumber, d = Decoration::getBinding(theContext, bindingNumber); theModule.addDecoration(d, targetId); } +void ModuleBuilder::decorateInputAttachmentIndex(uint32_t targetId, + uint32_t indexNumber) { + const auto *d = Decoration::getInputAttachmentIndex(theContext, indexNumber); + theModule.addDecoration(d, targetId); +} void ModuleBuilder::decorateLocation(uint32_t targetId, uint32_t location) { const Decoration *d = @@ -973,10 +978,10 @@ uint32_t ModuleBuilder::getImageType(uint32_t sampledType, spv::Dim dim, } else { requireCapability(spv::Capability::Sampled1D); } - } - - if (dim == spv::Dim::Buffer) { + } else if (dim == spv::Dim::Buffer) { requireCapability(spv::Capability::SampledBuffer); + } else if (dim == spv::Dim::SubpassData) { + requireCapability(spv::Capability::InputAttachment); } if (isArray && ms) { diff --git a/tools/clang/lib/SPIRV/SPIRVEmitter.cpp b/tools/clang/lib/SPIRV/SPIRVEmitter.cpp index f009df4b3..dfaf70bfb 100644 --- a/tools/clang/lib/SPIRV/SPIRVEmitter.cpp +++ b/tools/clang/lib/SPIRV/SPIRVEmitter.cpp @@ -879,7 +879,59 @@ void SPIRVEmitter::doFunctionDecl(const FunctionDecl *decl) { theBuilder.endFunction(); } -void SPIRVEmitter::validateVKAttributes(const NamedDecl *decl) { +bool SPIRVEmitter::validateVKAttributes(const NamedDecl *decl) { + bool success = true; + + if (decl->hasAttr()) { + emitWarning("row_major attribute for stand-alone matrix is not supported", + decl->getAttr()->getLocation()); + } + if (decl->hasAttr()) { + emitWarning( + "column_major attribute for stand-alone matrix is not supported", + decl->getAttr()->getLocation()); + } + + if (const auto *varDecl = dyn_cast(decl)) { + const auto varType = varDecl->getType(); + if ((TypeTranslator::isSubpassInput(varType) || + TypeTranslator::isSubpassInputMS(varType)) && + !varDecl->hasAttr()) { + emitError("missing vk::input_attachment_index attribute", + varDecl->getLocation()); + success = false; + } + } + + if (const auto *iaiAttr = decl->getAttr()) { + if (!shaderModel.IsPS()) { + emitError("SubpassInput(MS) only allowed in pixel shader", + decl->getLocation()); + success = false; + } + + if (!decl->isExternallyVisible()) { + emitError("SubpassInput(MS) must be externally visible", + decl->getLocation()); + success = false; + } + + // We only allow VKInputAttachmentIndexAttr to be attached to global + // variables. So it should be fine to cast here. + const auto elementType = + hlsl::GetHLSLResourceResultType(cast(decl)->getType()); + + if (!TypeTranslator::isScalarType(elementType) && + !TypeTranslator::isVectorType(elementType)) { + emitError( + "only scalar/vector types allowed as SubpassInput(MS) parameter type", + decl->getLocation()); + // Return directly to avoid further type processing, which will hit + // asserts in TypeTranslator. + return false; + } + } + // The frontend will make sure that // * vk::push_constant applies to global variables of struct type // * vk::binding applies to global variables or cbuffers/tbuffers @@ -903,14 +955,18 @@ void SPIRVEmitter::validateVKAttributes(const NamedDecl *decl) { emitError("cannot have more than one push constant block", loc); emitNote("push constant block previously defined here", seenPushConstantAt); + success = false; } if (decl->hasAttr()) { - emitError("'push_constant' attribute cannot be used together with " - "'binding' attribute", + emitError("vk::push_constant attribute cannot be used together with " + "vk::binding attribute", loc); + success = false; } } + + return success; } void SPIRVEmitter::doHLSLBufferDecl(const HLSLBufferDecl *bufferDecl) { @@ -930,7 +986,8 @@ void SPIRVEmitter::doHLSLBufferDecl(const HLSLBufferDecl *bufferDecl) { emitWarning("packoffset ignored since not supported", packing->Loc); } } - validateVKAttributes(bufferDecl); + if (!validateVKAttributes(bufferDecl)) + return; (void)declIdMapper.createCTBuffer(bufferDecl); } @@ -953,17 +1010,8 @@ void SPIRVEmitter::doRecordDecl(const RecordDecl *recordDecl) { } void SPIRVEmitter::doVarDecl(const VarDecl *decl) { - validateVKAttributes(decl); - - if (decl->hasAttr()) { - emitWarning("row_major attribute for stand-alone matrix is not supported", - decl->getAttr()->getLocation()); - } - if (decl->hasAttr()) { - emitWarning( - "column_major attribute for stand-alone matrix is not supported", - decl->getAttr()->getLocation()); - } + if (!validateVKAttributes(decl)) + return; if (decl->hasAttr()) { // This is a VarDecl for PushConstant block. @@ -2338,6 +2386,18 @@ uint32_t SPIRVEmitter::processGetSamplePosition(const CXXMemberCallExpr *expr) { return emitGetSamplePosition(sampleCount, doExpr(expr->getArg(0))); } +SpirvEvalInfo SPIRVEmitter::processSubpassLoad(const CXXMemberCallExpr *expr) { + const auto *object = expr->getImplicitObjectArgument()->IgnoreParens(); + const uint32_t sample = expr->getNumArgs() == 1 ? doExpr(expr->getArg(0)) : 0; + const uint32_t zero = theBuilder.getConstantInt32(0); + const uint32_t location = theBuilder.getConstantComposite( + theBuilder.getVecType(theBuilder.getInt32Type(), 2), {zero, zero}); + + return processBufferTextureLoad(object, location, /*constOffset*/ 0, + /*varOffset*/ 0, /*lod*/ sample, + /*residencyCode*/ 0); +} + uint32_t SPIRVEmitter::processBufferTextureGetDimensions(const CXXMemberCallExpr *expr) { const auto *object = expr->getImplicitObjectArgument(); @@ -2625,7 +2685,10 @@ SpirvEvalInfo SPIRVEmitter::processBufferTextureLoad( // The result type of an OpImageFetch must be a vec4 of float or int. const auto type = object->getType(); assert(TypeTranslator::isBuffer(type) || TypeTranslator::isRWBuffer(type) || - TypeTranslator::isTexture(type) || TypeTranslator::isRWTexture(type)); + TypeTranslator::isTexture(type) || TypeTranslator::isRWTexture(type) || + TypeTranslator::isSubpassInput(type) || + TypeTranslator::isSubpassInputMS(type)); + const bool doFetch = TypeTranslator::isBuffer(type) || TypeTranslator::isTexture(type); @@ -2633,7 +2696,8 @@ SpirvEvalInfo SPIRVEmitter::processBufferTextureLoad( // For Texture2DMS and Texture2DMSArray, Sample must be used rather than Lod. uint32_t sampleNumber = 0; - if (TypeTranslator::isTextureMS(type)) { + if (TypeTranslator::isTextureMS(type) || + TypeTranslator::isSubpassInputMS(type)) { sampleNumber = lod; lod = 0; } @@ -3351,6 +3415,9 @@ SPIRVEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr, case IntrinsicOp::MOP_GetSamplePosition: retVal = processGetSamplePosition(expr); break; + case IntrinsicOp::MOP_SubpassLoad: + retVal = processSubpassLoad(expr); + break; case IntrinsicOp::MOP_GatherCmpGreen: case IntrinsicOp::MOP_GatherCmpBlue: case IntrinsicOp::MOP_GatherCmpAlpha: diff --git a/tools/clang/lib/SPIRV/SPIRVEmitter.h b/tools/clang/lib/SPIRV/SPIRVEmitter.h index f5c25db3c..abede6fd8 100644 --- a/tools/clang/lib/SPIRV/SPIRVEmitter.h +++ b/tools/clang/lib/SPIRV/SPIRVEmitter.h @@ -275,8 +275,9 @@ private: const llvm::SmallVector &indices); private: - /// Validates that vk::* attributes are used correctly. - void validateVKAttributes(const NamedDecl *decl); + /// Validates that vk::* attributes are used correctly and returns false if + /// errors are found. + bool validateVKAttributes(const NamedDecl *decl); private: /// Processes the given expr, casts the result into the given bool (vector) @@ -732,6 +733,10 @@ private: /// Texture2DMS(Array). uint32_t processGetSamplePosition(const CXXMemberCallExpr *); + /// \brief Processes the SubpassLoad intrinsic function call on a + /// SubpassInput(MS). + SpirvEvalInfo processSubpassLoad(const CXXMemberCallExpr *); + /// \brief Generates SPIR-V instructions for the .Append()/.Consume() call on /// the given {Append|Consume}StructuredBuffer. Returns the of /// the loaded value for .Consume; returns zero for .Append(). diff --git a/tools/clang/lib/SPIRV/TypeTranslator.cpp b/tools/clang/lib/SPIRV/TypeTranslator.cpp index 62bc12a9e..73e361b40 100644 --- a/tools/clang/lib/SPIRV/TypeTranslator.cpp +++ b/tools/clang/lib/SPIRV/TypeTranslator.cpp @@ -613,6 +613,20 @@ bool TypeTranslator::isSampler(QualType type) { return false; } +bool TypeTranslator::isSubpassInput(QualType type) { + if (const auto *rt = type->getAs()) + return rt->getDecl()->getName() == "SubpassInput"; + + return false; +} + +bool TypeTranslator::isSubpassInputMS(QualType type) { + if (const auto *rt = type->getAs()) + return rt->getDecl()->getName() == "SubpassInputMS"; + + return false; +} + bool TypeTranslator::isVectorType(QualType type, QualType *elemType, uint32_t *elemCount) { bool isVec = false; @@ -999,6 +1013,14 @@ uint32_t TypeTranslator::translateResourceType(QualType type, LayoutRule rule) { return translateType(hlsl::GetHLSLResourceResultType(type), rule); } + if (name == "SubpassInput" || name == "SubpassInputMS") { + const auto sampledType = hlsl::GetHLSLResourceResultType(type); + return theBuilder.getImageType( + translateType(getElementType(sampledType)), spv::Dim::SubpassData, + /*depth*/ 0, /*isArray*/ false, /*ms*/ name == "SubpassInputMS", + /*sampled*/ 2); + } + return 0; } diff --git a/tools/clang/lib/SPIRV/TypeTranslator.h b/tools/clang/lib/SPIRV/TypeTranslator.h index 500ea5a3e..5e1a05675 100644 --- a/tools/clang/lib/SPIRV/TypeTranslator.h +++ b/tools/clang/lib/SPIRV/TypeTranslator.h @@ -119,6 +119,12 @@ public: /// \brief Returns true if the given type is an HLSL sampler type. static bool isSampler(QualType); + /// \brief Returns true if the given type is SubpassInput. + static bool isSubpassInput(QualType); + + /// \brief Returns true if the given type is SubpassInputMS. + static bool isSubpassInputMS(QualType); + /// \brief Returns true if the given type will be translated into a SPIR-V /// scalar type. This includes normal scalar types, vectors of size 1, and /// 1x1 matrices. If scalarType is not nullptr, writes the scalar type to diff --git a/tools/clang/test/CodeGenSPIRV/vk.attribute.invalid.hlsl b/tools/clang/test/CodeGenSPIRV/vk.attribute.invalid.hlsl index 1f02c2e2d..2e6ae0c32 100644 --- a/tools/clang/test/CodeGenSPIRV/vk.attribute.invalid.hlsl +++ b/tools/clang/test/CodeGenSPIRV/vk.attribute.invalid.hlsl @@ -1,4 +1,4 @@ -// Run: %dxc -T ps_6_0 -E main +// Run: %dxc -T vs_6_0 -E main struct S { float f; @@ -11,4 +11,4 @@ float main() : A { return 1.0; } -// CHECK: :7:3: error: 'push_constant' attribute cannot be used together with 'binding' attribute +// CHECK: :7:3: error: vk::push_constant attribute cannot be used together with vk::binding attribute diff --git a/tools/clang/test/CodeGenSPIRV/vk.subpass-input.binding.hlsl b/tools/clang/test/CodeGenSPIRV/vk.subpass-input.binding.hlsl new file mode 100644 index 000000000..3e95de521 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.subpass-input.binding.hlsl @@ -0,0 +1,27 @@ +// Run: %dxc -T ps_6_0 -E main + +// CHECK: OpDecorate %SI0 InputAttachmentIndex 0 +// CHECK: OpDecorate %SI1 InputAttachmentIndex 1 +// CHECK: OpDecorate %SI2 InputAttachmentIndex 2 + +// CHECK: OpDecorate %SI1 DescriptorSet 0 +// CHECK: OpDecorate %SI1 Binding 5 + +// CHECK: OpDecorate %SI2 DescriptorSet 3 +// CHECK: OpDecorate %SI2 Binding 5 + +// CHECK: OpDecorate %SI0 DescriptorSet 0 +// CHECK: OpDecorate %SI0 Binding 0 + +[[vk::input_attachment_index(0)]] +SubpassInput SI0; + +[[vk::input_attachment_index(1), vk::binding(5)]] +SubpassInput SI1; + +[[vk::input_attachment_index(2), vk::binding(5, 3)]] +SubpassInput SI2; + +void main() { + +} diff --git a/tools/clang/test/CodeGenSPIRV/vk.subpass-input.error.hlsl b/tools/clang/test/CodeGenSPIRV/vk.subpass-input.error.hlsl new file mode 100644 index 000000000..1cb4f0e74 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.subpass-input.error.hlsl @@ -0,0 +1,23 @@ +// Run: %dxc -T ps_6_0 -E main + +struct S { + float a; + float2 b; + float c; +}; + +SubpassInput SI0; // error + +[[vk::input_attachment_index(0)]] +SubpassInput SI1; // error + +[[vk::input_attachment_index(1)]] +static SubpassInput SI2; // error + +void main() { + +} + +// CHECK: :9:14: error: missing vk::input_attachment_index attribute +// CHECK: :12:17: error: only scalar/vector types allowed as SubpassInput(MS) parameter type +// CHECK: :15:21: error: SubpassInput(MS) must be externally visible \ No newline at end of file diff --git a/tools/clang/test/CodeGenSPIRV/vk.subpass-input.hlsl b/tools/clang/test/CodeGenSPIRV/vk.subpass-input.hlsl new file mode 100644 index 000000000..8353cf5f6 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.subpass-input.hlsl @@ -0,0 +1,85 @@ +// Run: %dxc -T ps_6_0 -E main + +// CHECK: OpCapability InputAttachment + +// CHECK: %type_subpass_image = OpTypeImage %float SubpassData 0 0 0 2 Unknown +// CHECK: %_ptr_UniformConstant_type_subpass_image = OpTypePointer UniformConstant %type_subpass_image + +// CHECK: %type_subpass_image_0 = OpTypeImage %int SubpassData 0 0 0 2 Unknown +// CHECK: %_ptr_UniformConstant_type_subpass_image_0 = OpTypePointer UniformConstant %type_subpass_image_0 + +// CHECK: %type_subpass_image_1 = OpTypeImage %uint SubpassData 0 0 0 2 Unknown +// CHECK: %_ptr_UniformConstant_type_subpass_image_1 = OpTypePointer UniformConstant %type_subpass_image_1 + +// CHECK: %type_subpass_image_2 = OpTypeImage %uint SubpassData 0 0 1 2 Unknown +// CHECK: %_ptr_UniformConstant_type_subpass_image_2 = OpTypePointer UniformConstant %type_subpass_image_2 + +// CHECK: %type_subpass_image_3 = OpTypeImage %float SubpassData 0 0 1 2 Unknown +// CHECK: %_ptr_UniformConstant_type_subpass_image_3 = OpTypePointer UniformConstant %type_subpass_image_3 + +// CHECK: %type_subpass_image_4 = OpTypeImage %int SubpassData 0 0 1 2 Unknown +// CHECK: %_ptr_UniformConstant_type_subpass_image_4 = OpTypePointer UniformConstant %type_subpass_image_4 + +// CHECK: [[v2i00:%\d+]] = OpConstantComposite %v2int %int_0 %int_0 + +// CHCK: %SI_f4 = OpVariable %_ptr_UniformConstant_type_subpass_image UniformConstant +[[vk::input_attachment_index(0)]] SubpassInput SI_f4; +// CHCK: %SI_i3 = OpVariable %_ptr_UniformConstant_type_subpass_image_0 UniformConstant +[[vk::input_attachment_index(1)]] SubpassInput SI_i3; +// CHCK: %SI_u2 = OpVariable %_ptr_UniformConstant_type_subpass_image_1 UniformConstant +[[vk::input_attachment_index(2)]] SubpassInput SI_u2; +// CHCK: %SI_f1 = OpVariable %_ptr_UniformConstant_type_subpass_image UniformConstant +[[vk::input_attachment_index(3)]] SubpassInput SI_f1; + +// CHCK: %SIMS_u4 = OpVariable %_ptr_UniformConstant_type_subpass_image_2 UniformConstant +[[vk::input_attachment_index(10)]] SubpassInputMS SIMS_u4; +// CHCK: %SIMS_f3 = OpVariable %_ptr_UniformConstant_type_subpass_image_3 UniformConstant +[[vk::input_attachment_index(11)]] SubpassInputMS SIMS_f3; +// CHCK: %SIMS_i2 = OpVariable %_ptr_UniformConstant_type_subpass_image_4 UniformConstant +[[vk::input_attachment_index(12)]] SubpassInputMS SIMS_i2; +// CHCK: %SIMS_u1 = OpVariable %_ptr_UniformConstant_type_subpass_image_2 UniformConstant +[[vk::input_attachment_index(13)]] SubpassInputMS SIMS_u1; + +float4 main() : SV_Target { +// CHECK: [[img:%\d+]] = OpLoad %type_subpass_image %SI_f4 +// CHECK-NEXT: [[texel:%\d+]] = OpImageRead %v4float [[img]] [[v2i00]] None +// CHECK-NEXT: OpStore %v0 [[texel]] + float4 v0 = SI_f4.SubpassLoad(); +// CHECK: [[img:%\d+]] = OpLoad %type_subpass_image_0 %SI_i3 +// CHECK-NEXT: [[texel:%\d+]] = OpImageRead %v4int [[img]] [[v2i00]] None +// CHECK-NEXT: [[val:%\d+]] = OpVectorShuffle %v3int [[texel]] [[texel]] 0 1 2 +// CHECK-NEXT: OpStore %v1 [[val]] + int3 v1 = SI_i3.SubpassLoad(); +// CHECK: [[img:%\d+]] = OpLoad %type_subpass_image_1 %SI_u2 +// CHECK-NEXT: [[texel:%\d+]] = OpImageRead %v4uint [[img]] [[v2i00]] None +// CHECK-NEXT: [[val:%\d+]] = OpVectorShuffle %v2uint [[texel]] [[texel]] 0 1 +// CHECK-NEXT: OpStore %v2 [[val]] + uint2 v2 = SI_u2.SubpassLoad(); +// CHECK: [[img:%\d+]] = OpLoad %type_subpass_image %SI_f1 +// CHECK-NEXT: [[texel:%\d+]] = OpImageRead %v4float [[img]] [[v2i00]] None +// CHECK-NEXT: [[val:%\d+]] = OpCompositeExtract %float [[texel]] 0 +// CHECK-NEXT: OpStore %v3 [[val]] + float v3 = SI_f1.SubpassLoad(); + +// CHECK: [[img:%\d+]] = OpLoad %type_subpass_image_2 %SIMS_u4 +// CHECK-NEXT: [[texel:%\d+]] = OpImageRead %v4uint [[img]] [[v2i00]] Sample %int_1 +// CHECK-NEXT: OpStore %v10 [[texel]] + uint4 v10 = SIMS_u4.SubpassLoad(1); +// CHECK: [[img:%\d+]] = OpLoad %type_subpass_image_3 %SIMS_f3 +// CHECK-NEXT: [[texel:%\d+]] = OpImageRead %v4float [[img]] [[v2i00]] Sample %int_2 +// CHECK-NEXT: [[val:%\d+]] = OpVectorShuffle %v3float [[texel]] [[texel]] 0 1 2 +// CHECK-NEXT: OpStore %v11 [[val]] + float3 v11 = SIMS_f3.SubpassLoad(2); +// CHECK: [[img:%\d+]] = OpLoad %type_subpass_image_4 %SIMS_i2 +// CHECK-NEXT: [[texel:%\d+]] = OpImageRead %v4int [[img]] [[v2i00]] Sample %int_3 +// CHECK-NEXT: [[val:%\d+]] = OpVectorShuffle %v2int [[texel]] [[texel]] 0 1 +// CHECK-NEXT: OpStore %v12 [[val]] + int2 v12 = SIMS_i2.SubpassLoad(3); +// CHECK: [[img:%\d+]] = OpLoad %type_subpass_image_2 %SIMS_u1 +// CHECK-NEXT: [[texel:%\d+]] = OpImageRead %v4uint [[img]] [[v2i00]] Sample %int_4 +// CHECK-NEXT: [[val:%\d+]] = OpCompositeExtract %uint [[texel]] 0 +// CHECK-NEXT: OpStore %v13 [[val]] + uint v13 = SIMS_u1.SubpassLoad(4); + + return v0.x + v1.y + v2.x + v3 + v10.x + v11.y + v12.x + v13; +} diff --git a/tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp b/tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp index 15f57050f..81336f273 100644 --- a/tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp +++ b/tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp @@ -1151,6 +1151,14 @@ TEST_F(FileTest, VulkanLayoutCBufferPackOffset) { runFileTest("vk.layout.cbuffer.packoffset.hlsl", Expect::Warning); } +TEST_F(FileTest, VulkanSubpassInput) { runFileTest("vk.subpass-input.hlsl"); } +TEST_F(FileTest, VulkanSubpassInputBinding) { + runFileTest("vk.subpass-input.binding.hlsl"); +} +TEST_F(FileTest, VulkanSubpassInputError) { + runFileTest("vk.subpass-input.error.hlsl", Expect::Failure); +} + // HS: for different Patch Constant Functions TEST_F(FileTest, HullShaderPCFVoid) { runFileTest("hs.pcf.void.hlsl"); } TEST_F(FileTest, HullShaderPCFTakesInputPatch) {