Sfoglia il codice sorgente

Improve support for SubpassInputs (#90)

- Add --no-subpass-input argument to emit a shader without subpass input support
- Add SubpassInputToTexture2DCodeMutator to:
  - Replace SubpassInput type --> Texture2D type if subpass input are not supported
  - Replace SubpassLoad function --> Load function if subpass input are not supported
  - Remove arguments from SubpassLoad function when subpass inputs are supported

Signed-off-by: Akio Gaule <[email protected]>
Akio Gaule 11 mesi fa
parent
commit
23d786bc96

+ 28 - 22
Platform/Windows/src/VulkanPlatformEmitter.cpp

@@ -40,32 +40,32 @@ namespace AZ::ShaderCompiler
         return strOut.str();        
     }
 
-    std::pair<string, string> VulkanPlatformEmitter::GetDataViewHeaderFooter(const CodeEmitter& codeEmitter, const IdentifierUID& symbol, uint32_t bindInfoRegisterIndex, string_view registerTypeLetter, optional<string> stringifiedLogicalSpace) const
+    std::pair<string, string> VulkanPlatformEmitter::GetDataViewHeaderFooter(
+        const CodeEmitter& codeEmitter,
+        const IdentifierUID& symbol,
+        uint32_t bindInfoRegisterIndex,
+        string_view registerTypeLetter,
+        optional<string> stringifiedLogicalSpace,
+        const Options& options) const
     {
         std::stringstream stream;
-        optional<AttributeInfo> inputAttachmentIndexAttribute = codeEmitter.GetIR()->m_symbols.GetAttribute(symbol, "input_attachment_index");
-        if (inputAttachmentIndexAttribute)
+        optional<AttributeInfo> inputAttachmentIndexAttribute;
+        if (options.m_useSubpassInputs)
         {
-            // example result in HLSL:
-            /*
-                   #ifdef AZ_USE_SUBPASSINPUT
-                   [[vk::binding(0,0)]]
-                   [[vk::input_attachment_index(0)]]
-                   #endif
-                   AzSubpassInput srg_myData;
-            */
-            stream << "#ifdef AZ_USE_SUBPASSINPUT\n";
-            inputAttachmentIndexAttribute->m_namespace = "vk";
-            inputAttachmentIndexAttribute->m_category = AttributeCategory::Sequence;
-            MakeOStreamStreamable soss(stream);
-            CodeEmitter::EmitAttribute(*inputAttachmentIndexAttribute, soss);
-            stream << "[[vk::binding(" << bindInfoRegisterIndex;
-            if (stringifiedLogicalSpace)
+            inputAttachmentIndexAttribute = codeEmitter.GetIR()->m_symbols.GetAttribute(symbol, "input_attachment_index");
+            if (inputAttachmentIndexAttribute)
             {
-                stream << ", " << *stringifiedLogicalSpace;
+                inputAttachmentIndexAttribute->m_namespace = "vk";
+                inputAttachmentIndexAttribute->m_category = AttributeCategory::Sequence;
+                MakeOStreamStreamable soss(stream);
+                CodeEmitter::EmitAttribute(*inputAttachmentIndexAttribute, soss);
+                stream << "[[vk::binding(" << bindInfoRegisterIndex;
+                if (stringifiedLogicalSpace)
+                {
+                    stream << ", " << *stringifiedLogicalSpace;
+                }
+                stream << ")]]\n";
             }
-            stream << ")]]\n";
-            stream << "#else\n static\n#endif\n";  // for the stub mode, we don't export the phony variable
         }
 
         string registerString;
@@ -75,8 +75,14 @@ namespace AZ::ShaderCompiler
                                                                       symbol,
                                                                       bindInfoRegisterIndex,
                                                                       registerTypeLetter,
-                                                                      stringifiedLogicalSpace).second;
+                                                                      stringifiedLogicalSpace,
+                                                                      options).second;
         }
         return { stream.str(), registerString };
     }
+
+    bool VulkanPlatformEmitter::SupportsSubpassInputs() const
+    {
+        return true;
+    }
 }

+ 9 - 1
Platform/Windows/src/VulkanPlatformEmitter.h

@@ -24,7 +24,15 @@ namespace AZ::ShaderCompiler
         string GetRootConstantsView(const CodeEmitter& codeEmitter, const RootSigDesc& rootSig, const Options& options, BindingPair::Set signatureQuery) const override final;
 
         [[nodiscard]]
-        std::pair<string, string> GetDataViewHeaderFooter(const CodeEmitter& codeEmitter, const IdentifierUID& symbol, uint32_t bindInfoRegisterIndex, string_view registerTypeLetter, optional<string> stringifiedLogicalSpace) const override final;
+        std::pair<string, string> GetDataViewHeaderFooter(
+            const CodeEmitter& codeEmitter,
+            const IdentifierUID& symbol,
+            uint32_t bindInfoRegisterIndex,
+            string_view registerTypeLetter,
+            optional<string> stringifiedLogicalSpace,
+            const Options& options) const override final;
+
+        bool SupportsSubpassInputs() const override;
 
     private:
         VulkanPlatformEmitter() : CommonVulkanPlatformEmitter {} {};

+ 17 - 12
src/AzslcBackend.cpp

@@ -251,18 +251,7 @@ namespace AZ::ShaderCompiler
 
     const PlatformEmitter& Backend::GetPlatformEmitter() const
     {
-        for (const auto& attr : m_ir->m_metaData.m_attributeNamespaceFilters)
-        {
-            // We can have multiple attribute scopes enabled
-            // By design only one can have associated platform emitter, so return the first match
-            const auto p = PlatformEmitter::GetEmitter(attr);
-            if (p)
-            {
-                return *p;
-            }
-        }
-
-        return *PlatformEmitter::GetDefaultEmitter();
+        return GetPlatformEmitter(m_ir);
     }
 
     //! Gets the next and increments tokenIndex. TokenIndex must be in the [misc::Interval.a, misc::Interval.b] range. Token cannot be nullptr.
@@ -820,6 +809,22 @@ namespace AZ::ShaderCompiler
         return hlslString;
     }
 
+    const PlatformEmitter& Backend::GetPlatformEmitter(IntermediateRepresentation* ir)
+    {
+        for (const auto& attr : ir->m_metaData.m_attributeNamespaceFilters)
+        {
+            // We can have multiple attribute scopes enabled
+            // By design only one can have associated platform emitter, so return the first match
+            const auto p = PlatformEmitter::GetEmitter(attr);
+            if (p)
+            {
+                return *p;
+            }
+        }
+
+        return *PlatformEmitter::GetDefaultEmitter();
+    }
+
     uint32_t Backend::GetNumberOf32BitConstants(const Options& options, const IdentifierUID& uid) const
     {
         // Count the number of 32 bin constants

+ 4 - 0
src/AzslcBackend.h

@@ -42,6 +42,7 @@ namespace AZ::ShaderCompiler
         Packing::Layout m_packConstantBuffers  = Packing::Layout::DirectXPacking; //!< Packing standard for constant buffers (uniform)
         Packing::Layout m_packDataBuffers      = Packing::Layout::CStylePacking;  //!< Packing standard for data buffer views
         bool m_useSpecializationConstantsForOptions = false; //!< Use specialization constants for shader options
+        bool m_useSubpassInputs = false; //< Can use subpass inputs.
     };
 
     struct Binding
@@ -156,6 +157,9 @@ namespace AZ::ShaderCompiler
         //! \param banned is the Flag you can setup to list a collection of type qualifiers you don't want to reproduce.
         string GetExtendedTypeInfo(const ExtendedTypeInfo& extTypeInfo, const Options& options, Modifiers banned, std::function<string(const TypeRefInfo&)> translator) const;
 
+        //! Returns the Platform Emitter that is registered to the namespace in the "ir".
+        static const PlatformEmitter& GetPlatformEmitter(IntermediateRepresentation* ir);
+
     protected:
         //! Obtains a supplement emitter which provides per-platform emission functionality.
         const PlatformEmitter& GetPlatformEmitter() const;

+ 19 - 37
src/AzslcEmitter.cpp

@@ -17,30 +17,6 @@ namespace StdFs = std::filesystem;
 // Every specific implementation is supplied via a factory get method
 #include "AzslcPlatformEmitter.h"
 
-namespace AZ
-{
-    namespace
-    {
-        //  spec: https://github.com/microsoft/DirectXShaderCompiler/blob/master/docs/SPIR-V.rst#subpass-inputs
-        /// extract: "Subpasses are read through two new builtin resource types, available only in pixel shader"
-        //  because we have a unified file for all stages, we need to ensure the source remains buildable for other stages.
-        static constexpr char StubSubpassInputTypes[] = R"(
-#if !defined(AZ_USE_SUBPASSINPUT)
-  class SubpassInputStub
-  {
-    float4 SubpassLoad(){return (float4)0;}
-  };
-  class SubpassInputStubMS
-  {
-    float4 SubpassLoad(int sampleIndex){return (float4)0;}
-  };
-  #define SubpassInput SubpassInputStub
-  #define SubpassInputMS SubpassInputStubMS
-#endif
-)";
-    }
-}
-
 namespace AZ::ShaderCompiler
 {
     // to activate argument dependent lookup from template utilities in AzslcUtils, this must be in a reachable namespace
@@ -104,11 +80,6 @@ namespace AZ::ShaderCompiler
         {
             EmitAttribute(attr);
         }
-
-        if (m_ir->m_sema.m_subpassInputSeen)
-        {
-            m_out << StubSubpassInputTypes << "\n";
-        }
         
         EmitGetterFunctionDeclarationsForRootConstants(m_ir->m_rootConstantStructUID);
 
@@ -834,6 +805,19 @@ namespace AZ::ShaderCompiler
         }
     }
 
+    const CodeMutation* CodeEmitter::GetCodeMutation(size_t tokenIndex) const
+    {
+        for (auto* codeMutator : m_codeMutators)
+        {
+            if (const CodeMutation* codeMutation = codeMutator->GetMutation(tokenIndex))
+            {
+                return codeMutation;
+            }
+        }
+        return nullptr;
+    }
+
+
     void CodeEmitter::EmitVariableDeclaration(const VarInfo& varInfo, const IdentifierUID& uid, const Options& options, VarDeclHasFlag declOptions) const
     {
         // from MSDN: https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-variable-syntax
@@ -841,13 +825,13 @@ namespace AZ::ShaderCompiler
         // example of valid HLSL statement:
         //    static const int dim = 2;
         //    extern uniform bool stuff[dim][1] : Color0 : register(b0) <int blabla=27; string blacksheep="Hello There";> = {{false, true}};
-        const ICodeEmissionMutator* codeMutator = m_codeMutator;
+
         const CodeMutation* codeMutation = nullptr;
-        if (codeMutator && varInfo.m_declNode)
+        if (varInfo.m_declNode)
         {
             const auto tokenIndex = varInfo.m_declNode->start->getTokenIndex();
-            codeMutation = codeMutator->GetMutation(tokenIndex);
-        }
+            codeMutation = GetCodeMutation(tokenIndex);
+        }   
 
         if (codeMutation && codeMutation->m_prepend)
         {
@@ -1093,7 +1077,7 @@ namespace AZ::ShaderCompiler
         optional<string> stringifiedLogicalSpace = std::to_string(bindInfo.m_registerBinding.m_pair[bindSet].m_logicalSpace);
 
         // depending on platforms we may have supplementary attributes or/and type modifier.
-        auto [prefix, suffix] = GetPlatformEmitter().GetDataViewHeaderFooter(*this, tId, bindInfo.m_registerBinding.m_pair[bindSet].m_registerIndex, registerTypeLetter, stringifiedLogicalSpace);
+        auto [prefix, suffix] = GetPlatformEmitter().GetDataViewHeaderFooter(*this, tId, bindInfo.m_registerBinding.m_pair[bindSet].m_registerIndex, registerTypeLetter, stringifiedLogicalSpace, options);
         m_out << prefix;
         // declaration of the view variable on the global scope.
         // type unmangled_path_to_symbol [optional_array_dimension] : optional_register_binding_as_suffix
@@ -1290,8 +1274,6 @@ namespace AZ::ShaderCompiler
     // override of the base method, to incorporate symbol and expression mutations
     void CodeEmitter::EmitTranspiledTokens(misc::Interval interval, Streamable& output) const
     {
-        const ICodeEmissionMutator* codeMutator = m_codeMutator;
-
         ssize_t ii = interval.a;
         while (ii <= interval.b)
         {
@@ -1299,7 +1281,7 @@ namespace AZ::ShaderCompiler
             
             const auto tokenIndex = token->getTokenIndex();
 
-            const CodeMutation* codeMutation = codeMutator ? codeMutator->GetMutation(tokenIndex) : nullptr;
+            const CodeMutation* codeMutation = GetCodeMutation(tokenIndex);
             if (codeMutation && codeMutation->m_prepend)
             {
                 output << codeMutation->m_prepend.value();

+ 5 - 2
src/AzslcEmitter.h

@@ -54,7 +54,7 @@ namespace AZ::ShaderCompiler
         //! Write the HLSL formatted shape of an attribute into a stream
         static void EmitAttribute(const AttributeInfo& attrInfo, Streamable& outstream);
 
-        void SetCodeMutator(ICodeEmissionMutator* codeMutator) { m_codeMutator = codeMutator; }
+        void AddCodeMutator(ICodeEmissionMutator* codeMutator) { m_codeMutators.push_back(codeMutator); }
 
         //! It would be nice that the clients don't push text through the passed "out" stream since it's not observed by the line counter;
         //! use this API in case of custom client text pushing.
@@ -218,7 +218,7 @@ namespace AZ::ShaderCompiler
         
         //! If not null it will be used during code emission to produce
         //! the mutations. 
-        ICodeEmissionMutator* m_codeMutator = nullptr;
+        vector<ICodeEmissionMutator*> m_codeMutators;
 
         //! We keep track here of the number of lines that have been emitted.
         //! Each symbol has an original line number (virtual and physical) where it appeared,
@@ -236,5 +236,8 @@ namespace AZ::ShaderCompiler
 
         //! Given an SRG parameter, determines the space it belongs to based on the platform
         int ResolveBindingSpace(const RootSigDesc::SrgParamDesc& bindInfo, BindingPair::Set bindSet) const;
+
+        //! Returns the code mutation for a token index if it exist.
+        const CodeMutation* GetCodeMutation(size_t tokenIndex) const;
     };
 }

+ 2 - 2
src/AzslcListener.cpp

@@ -440,9 +440,9 @@ namespace AZ::ShaderCompiler
 
     void SemaCheckListener::enterFunctionCallExpression(azslParser::FunctionCallExpressionContext* ctx)
     {
-        if (m_functionCallMutator)
+        for(azslParserBaseListener* mutator : m_functionCallMutators)
         {
-            m_functionCallMutator->enterFunctionCallExpression(ctx);
+            mutator->enterFunctionCallExpression(ctx);
         }
     }
 

+ 2 - 2
src/AzslcListener.h

@@ -65,10 +65,10 @@ namespace AZ::ShaderCompiler
         IntermediateRepresentation* m_ir;
         bool m_silentPrintExtensions = false;
 
-        //! If not null, this other parser listener will be used during
+        //! If not empty, these other parser listeners will be used during
         //! enterFunctionCallExpression(), its data type is azslParserBaseListener because
         //! in the future it can be convenient to do mutations at different stages of parsing
         //! the AST.
-        azslParserBaseListener* m_functionCallMutator = nullptr;
+        vector<azslParserBaseListener*> m_functionCallMutators;
     };
 }

+ 27 - 7
src/AzslcMain.cpp

@@ -11,7 +11,9 @@
 #include "AzslcReflection.h"
 #include "AzslcEmitter.h"
 #include "AzslcHomonymVisitor.h"
+#include "AzslcPlatformEmitter.h"
 #include "Texture2DMSto2DCodeMutator.h"
+#include "SubpassInputToTexture2DCodeMutator.h"
 
 #include <cstddef>
 #include <filesystem>
@@ -425,6 +427,9 @@ int main(int argc, const char* argv[])
     bool useSpecializationConstants = false;
     cli.add_flag("--sc-options", useSpecializationConstants, "Use specialization constants for shader options.");
 
+    bool noSubpassInput = false;
+    cli.add_flag("--no-subpass-input", noSubpassInput, "Transform usage of SubpassInput/SubpassInputMS into Texture2D/Texture2DMS");
+
     std::array<bool, Warn::EndEnumeratorSentinel_> warningOpts;
     for (const auto e : Warn::Enumerate{})
     {
@@ -540,8 +545,16 @@ int main(int argc, const char* argv[])
                 StdFs::path inSource{ inputFile };
                 ir.m_metaData.m_insource = StdFs::absolute(inSource).lexically_normal().generic_string();
             }
+
+            // Enable attribute namespaces
+            std::for_each(namespaces.begin(), namespaces.end(),
+                [&](const string& space) { ir.AddAttributeNamespaceFilter(space); });
+
+            bool subpassSupport = Backend::GetPlatformEmitter(&ir).SupportsSubpassInputs() && !noSubpassInput;
+
             tree::ParseTreeWalker walker;
             Texture2DMSto2DCodeMutator texture2DMSto2DCodeMutator(&ir, &tokens);
+            SubpassInputToTexture2DCodeMutator subpassInputToTexture2DCodeMutator(&ir, &tokens, subpassSupport);
             SemaCheckListener semanticListener{&ir};
             warningCout.m_onErrorCallback = [](string_view message) {
                 throw AzslcException{WX_WARNINGS_AS_ERRORS, "as-error", string{message}};
@@ -552,14 +565,11 @@ int main(int argc, const char* argv[])
             semanticListener.m_silentPrintExtensions = !semantic || verbose; // print-extensions are useful for interested parties; but not normal operation.
             if (noMS)
             {
-                semanticListener.m_functionCallMutator = &texture2DMSto2DCodeMutator;
+                semanticListener.m_functionCallMutators.push_back(&texture2DMSto2DCodeMutator);
             }
+            semanticListener.m_functionCallMutators.push_back(&subpassInputToTexture2DCodeMutator);
             warningCout.m_on = !anyNonValidativeOption; // warnings are interesting for emission, and explicit semantic check modes.
 
-            // Enable attribute namespaces
-            std::for_each(namespaces.begin(), namespaces.end(),
-                [&](const string& space) { ir.AddAttributeNamespaceFilter(space); });
-
             UnboundedArraysValidator::Options unboundedArraysValidationOptions;
             unboundedArraysValidationOptions.m_useUniqueIndicesEnabled = uniqueIdx;
             if (*maxSpacesOpt)
@@ -578,6 +588,7 @@ int main(int argc, const char* argv[])
             emitOptions.m_padRootConstantCB = padRootConst;
             emitOptions.m_skipAlignmentValidation = noAlignmentValidation;
             emitOptions.m_useSpecializationConstantsForOptions = useSpecializationConstants;
+            emitOptions.m_useSubpassInputs = subpassSupport;
 
             if (*rootConstOpt)
             {
@@ -644,6 +655,7 @@ int main(int argc, const char* argv[])
             {
                 texture2DMSto2DCodeMutator.RunMiddleEndMutations();
             }
+            bool hasSubpassInputMutations = subpassInputToTexture2DCodeMutator.RunMiddleEndMutations();
 
             // If ir fails to find any root members in the source, overwrite the m_numOfRootConstants to 0
             if (ir.m_rootConstantStructUID.m_name == "")
@@ -725,7 +737,11 @@ int main(int argc, const char* argv[])
                     CodeEmitter emitter{&ir, &tokens, out, &lineFinder};
                     if (noMS)
                     {
-                        emitter.SetCodeMutator(&texture2DMSto2DCodeMutator);
+                        emitter.AddCodeMutator(&texture2DMSto2DCodeMutator);
+                    }
+                    if (hasSubpassInputMutations)
+                    {
+                        emitter.AddCodeMutator(&subpassInputToTexture2DCodeMutator);
                     }
                     emitter << "// HLSL emission by " << versionString << "\n";
                     emitter.Run(emitOptions);
@@ -761,7 +777,11 @@ int main(int argc, const char* argv[])
                     CodeEmitter emitter{&ir, &tokens, out, &lineFinder};
                     if (noMS)
                     {
-                        emitter.SetCodeMutator(&texture2DMSto2DCodeMutator);
+                        emitter.AddCodeMutator(&texture2DMSto2DCodeMutator);
+                    }
+                    if (hasSubpassInputMutations)
+                    {
+                        emitter.AddCodeMutator(&subpassInputToTexture2DCodeMutator);
                     }
                     emitter << "// HLSL emission by " << versionString << "\n";
                     emitter.Run(emitOptions);

+ 12 - 9
src/AzslcPlatformEmitter.cpp

@@ -76,16 +76,14 @@ namespace AZ::ShaderCompiler
         return strOut.str();
     }
 
-    std::pair<string, string> PlatformEmitter::GetDataViewHeaderFooter(const CodeEmitter& codeEmitter, const IdentifierUID& symbol, uint32_t bindInfoRegisterIndex, string_view registerTypeLetter, optional<string> stringifiedLogicalSpace) const
+    std::pair<string, string> PlatformEmitter::GetDataViewHeaderFooter(
+        const CodeEmitter& codeEmitter,
+        const IdentifierUID& symbol,
+        uint32_t bindInfoRegisterIndex, 
+        string_view registerTypeLetter, 
+        optional<string> stringifiedLogicalSpace,
+        const Options& options) const
     {
-        // there is an exception for subpassinput variables since they are not yet supported on our DX12 emission
-        // we need to neutralize it (otherwise DXC will complain with "register space cannot be specified on global constants")
-        // example result: [azslc: SubpassInput var;   hlsl: SubpassInputStub srg_var : register(t0); ]
-        optional<AttributeInfo> inputAttachmentIndexAttribute = codeEmitter.GetIR()->m_symbols.GetAttribute(symbol, "input_attachment_index");
-        if (inputAttachmentIndexAttribute)
-        {
-            return { "static ", {} };
-        }
         // in the general case, we output normal HLSL `var decl : register(b0, space0);`
         // no special header, but the post colon part is the footer
         string bindingSpaceStringlet { stringifiedLogicalSpace ? ", space" + *stringifiedLogicalSpace : "" };
@@ -102,4 +100,9 @@ namespace AZ::ShaderCompiler
     {
         return "";
     }
+
+    bool PlatformEmitter::SupportsSubpassInputs() const
+    {
+        return false;
+    }
 }

+ 11 - 1
src/AzslcPlatformEmitter.h

@@ -67,9 +67,16 @@ namespace AZ::ShaderCompiler
         //! @param symbol                   The symbol path of the original variable we are emitting as an extern data view
         //! @param bindInfoRegisterIndex    Register index of the resource
         //! @param stringifiedLogicalSpace  Optional register space
+        //! @param options                  Emission options
         //! \return                         first: header to emit before the dataview declaration.   second: footer to emit after the dataview declaration
         [[nodiscard]]
-        virtual std::pair<string, string> GetDataViewHeaderFooter(const CodeEmitter& codeEmitter, const IdentifierUID& symbol, uint32_t bindInfoRegisterIndex, string_view registerTypeLetter, optional<string> stringifiedLogicalSpace) const;
+        virtual std::pair<string, string> GetDataViewHeaderFooter(
+            const CodeEmitter& codeEmitter,
+            const IdentifierUID& symbol, 
+            uint32_t bindInfoRegisterIndex,
+            string_view registerTypeLetter,
+            optional<string> stringifiedLogicalSpace,
+            const Options& options) const;
 
         //! Aligns the size for the data containing the root constants.
         //! @param size  The size of stride
@@ -79,5 +86,8 @@ namespace AZ::ShaderCompiler
 
         [[nodiscard]]
         virtual string GetSpecializationConstant(const CodeEmitter& codeEmitter, const IdentifierUID& symbol, const Options& options) const;
+
+        //! Returns true if the emitter supports subpass inputs. 
+        virtual bool SupportsSubpassInputs() const;
     };
 }

+ 7 - 4
src/AzslcPredefinedTypes.h

@@ -199,7 +199,11 @@ static constexpr std::array<const char*, 5> StructuredBuffer = {
 "RasterizerOrderedStructuredBuffer",
 "StructuredBuffer"};
 
-static constexpr std::array<const char*, 19> Texture = {
+static constexpr std::array<const char*, 2> SubpassInput = {
+"SubpassInput",
+"SubpassInputMS"};
+
+static constexpr std::array<const char*, 17> Texture = {
 "RWTexture1D",
 "RWTexture1DArray",
 "RWTexture2D",
@@ -210,8 +214,6 @@ static constexpr std::array<const char*, 19> Texture = {
 "RasterizerOrderedTexture2D",
 "RasterizerOrderedTexture2DArray",
 "RasterizerOrderedTexture3D",
-"SubpassInput",
-"SubpassInputMS",
 "Texture1D",
 "Texture1DArray",
 "Texture2D",
@@ -268,7 +270,8 @@ static constexpr auto All = std::make_tuple(Bag<3>{"Buffer", Buffer},
                                             Bag<13>{"Scalar", Scalar},
                                             Bag<3>{"StreamOutput", StreamOutput},
                                             Bag<5>{"StructuredBuffer", StructuredBuffer},
-                                            Bag<19>{"Texture", Texture},
+                                            Bag<2>{"SubpassInput", SubpassInput},
+                                            Bag<17>{"Texture", Texture},
                                             Bag<29>{"Vector", Vector},
                                             Bag<1>{"Void", Void});
 

+ 0 - 1
src/AzslcSemanticOrchestrator.cpp

@@ -540,7 +540,6 @@ namespace AZ::ShaderCompiler
             }
             // erase the generic type, until the SubpassInputStub type can be templated:
             varInfo.m_typeInfoExt.m_genericParameter = TypeRefInfo{};
-            m_subpassInputSeen = true;
         }
         // get enclosing scope:
         auto& [curScopeId, curScopeKind] = GetCurrentScopeIdAndKind();

+ 0 - 3
src/AzslcSemanticOrchestrator.h

@@ -389,9 +389,6 @@ namespace AZ::ShaderCompiler
         azslLexer*        m_lexer;
         UnboundedArraysValidator m_unboundedArraysValidator;
 
-        //! cached property informing of the presence of at least one input attachment use.
-        bool              m_subpassInputSeen = false;
-
     private:
         // remember frequencyId to srg association, for semantic validation
         unordered_map<int64_t, IdentifierUID> m_frequencyToSrg;

+ 0 - 1
src/AzslcSymbolAggregator.cpp

@@ -85,7 +85,6 @@ namespace AZ::ShaderCompiler
         static const std::unordered_set<string_view> ReservedNames =
         {
             "/Root_Constants",
-            "AZ_USE_SUBPASSINPUT",
             RootConstantsViewName
         };
 

+ 10 - 4
src/AzslcTypes.h

@@ -27,6 +27,8 @@ namespace AZ::ShaderCompiler
         Texture,
         GenericTexture,
         MultisampledTexture,
+        SubpassInput,
+        GenericSubpassInput,
         Sampler,
         StructuredBuffer,
         Buffer,
@@ -96,7 +98,7 @@ namespace AZ::ShaderCompiler
 
     inline bool HasGenericParameter(TypeClass typeClass)
     { // TODO: to add InputPath/OutputPatch because it has a generic parameter. (when you do it, update ExtractGenericTypeParameterNameFromAstContext)
-        return IsChameleon(typeClass) || IsGenericArithmetic(typeClass) || typeClass.IsOneOf(TypeClass::GenericTexture, TypeClass::MultisampledTexture);
+        return IsChameleon(typeClass) || IsGenericArithmetic(typeClass) || typeClass.IsOneOf(TypeClass::GenericTexture, TypeClass::MultisampledTexture, TypeClass::GenericSubpassInput);
     }
 
     inline bool IsViewTypeBuffer(TypeClass typeClass)
@@ -107,7 +109,7 @@ namespace AZ::ShaderCompiler
     inline bool IsViewType(TypeClass typeClass)
     {   // note that a constant buffer is not a view type
         return IsViewTypeBuffer(typeClass)
-            || typeClass.IsOneOf(TypeClass::Texture, TypeClass::GenericTexture, TypeClass::MultisampledTexture, TypeClass::Sampler);
+            || typeClass.IsOneOf(TypeClass::Texture, TypeClass::GenericTexture, TypeClass::MultisampledTexture, TypeClass::Sampler, TypeClass::SubpassInput, TypeClass::GenericSubpassInput);
     }
 
     //Example Texture2D m_myTex[]; is supported.
@@ -122,14 +124,16 @@ namespace AZ::ShaderCompiler
         TypeClass toReturn = TypeClass::OtherPredefined;  // default value if nothing of the under was non null.
         using PredefinedNodeT = std::remove_pointer_t<decltype(predefinedNode)>;  // DRY
         // map TypeClasses to the same indexes than the context functions list
-        array<TypeClass, 16> contextClasses = { TypeClass::Scalar,
+        array<TypeClass, 18> contextClasses = { TypeClass::Scalar,
                                                 TypeClass::Vector,
                                                 TypeClass::GenericVector,
                                                 TypeClass::Matrix,
                                                 TypeClass::GenericMatrix,
                                                 TypeClass::Texture,
                                                 TypeClass::MultisampledTexture,
+                                                TypeClass::SubpassInput,
                                                 TypeClass::GenericTexture,
+                                                TypeClass::GenericSubpassInput,
                                                 TypeClass::Sampler,
                                                 TypeClass::StructuredBuffer,
                                                 TypeClass::Buffer,
@@ -146,7 +150,9 @@ namespace AZ::ShaderCompiler
                                            &PredefinedNodeT::genericMatrixPredefinedType,
                                            &PredefinedNodeT::texturePredefinedType,
                                            &PredefinedNodeT::msTexturePredefinedType,
+                                           &PredefinedNodeT::subpassInputPredefinedType,
                                            &PredefinedNodeT::genericTexturePredefinedType,
+                                           &PredefinedNodeT::genericSubpassInputPredefinedType,
                                            &PredefinedNodeT::samplerStatePredefinedType,
                                            &PredefinedNodeT::structuredBufferPredefinedType,
                                            &PredefinedNodeT::bufferPredefinedType,
@@ -154,7 +160,7 @@ namespace AZ::ShaderCompiler
                                            &PredefinedNodeT::constantBufferTemplated,
                                            &PredefinedNodeT::streamOutputPredefinedType,
                                            &PredefinedNodeT::otherViewResourceType,
-                                           &PredefinedNodeT::subobjectType >;
+                                           &PredefinedNodeT::subobjectType>;
         static_assert( countTemplateParameters_v<FunctionList> == contextClasses.size() );
         // This meta-loop will be unfolded at build time, therefore the generic lambda will be instantiated individually (by each type)
         ForEachValue<FunctionList>([&toReturn, &predefinedNode, &contextClasses](auto ctxMemFn, auto index)

+ 5 - 0
src/AzslcUtils.h

@@ -1281,6 +1281,11 @@ namespace AZ::ShaderCompiler
             return {ExtractedTypeExt{UnqualifiedName{coreName}},                           // m_core
                     ExtractedTypeExt{UnqualifiedName{genericCtx->getText()}, genericCtx}};  // same as previous 2 comments above
         }
+        else if (ctx->genericSubpassInputPredefinedType())
+        {
+            return { ExtractedTypeExt{UnqualifiedName{ctx->genericSubpassInputPredefinedType()->subpassInputType()->getText()}},    // m_core
+                    ExtractedTypeExt{UnqualifiedName{ctx->genericSubpassInputPredefinedType()->scalarOrVectorType()->getText()}} }; // m_genericParam
+        }
         // all other rules are getable without artefacts for sure from a simple getText
         return {ExtractedTypeExt{UnqualifiedName{ctx->getText()}}};     // only core, no generic part.
     }

+ 172 - 0
src/SubpassInputToTexture2DCodeMutator.cpp

@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "SubpassInputToTexture2DCodeMutator.h"
+
+
+namespace AZ::ShaderCompiler
+{
+    static constexpr char FunctionNameLoad[] = "SubpassLoad";
+
+    ///////////////////////////////////////////////////////////////////////
+    // azslParserBaseListener Overrides ...
+    void SubpassInputToTexture2DCodeMutator::enterFunctionCallExpression(azslParser::FunctionCallExpressionContext* ctx)
+    {
+        const auto expressionCtx = ctx->expression();
+        const std::string functionName = expressionCtx->stop->getText();
+
+        if (functionName == FunctionNameLoad)
+        {
+            OnEnterLoad(ctx);
+        }
+    }
+    ///////////////////////////////////////////////////////////////////////
+
+
+    ///////////////////////////////////////////////////////////////////////
+    // ICodeEmissionMutator Overrides ...
+    const CodeMutation* SubpassInputToTexture2DCodeMutator::GetMutation(ssize_t tokenId) const
+    {
+        auto itor = m_mutations.find(tokenId);
+        if (itor == m_mutations.end())
+        {
+            return nullptr;
+        }
+        return &itor->second;
+    }
+
+    bool SubpassInputToTexture2DCodeMutator::RunMiddleEndMutations()
+    {
+        // Get all variables that are members of something of type Texture2DMS
+        // We use this function pointer to find SRGs that have no references.
+        auto subpassInputFilterFunc = +[](KindInfo* kindInfo) {
+            const auto* varInfo = kindInfo->GetSubAs<VarInfo>();
+            return varInfo->m_typeInfoExt.m_coreType.m_typeClass == TypeClass::SubpassInput;
+            };
+
+        vector<IdentifierUID> subpassInputVariables = m_ir->GetFilteredSymbolsOfSubType<VarInfo>(subpassInputFilterFunc);
+        if (!m_supportsSubpassInputs)
+        {
+            MutateTypeOfMultiSampleVariables(subpassInputVariables);
+        }
+        return !subpassInputVariables.empty();
+    }
+
+    ///////////////////////////////////////////////////////////////////////
+
+    static UnqualifiedName GetSymbolName(azslParser::ExpressionContext* expressionCtx)
+    {
+        const auto& children = expressionCtx->children;
+        // We only care for cases with three children:
+        // "<Symbol>", ".", "<funcName>"
+        if (children.size() == 3)
+        {
+            string symbolName = Replace(children[0]->getText(), "::", "/");
+            return UnqualifiedName{ symbolName };
+        }
+        return UnqualifiedName();
+    }
+
+    void SubpassInputToTexture2DCodeMutator::OnEnterLoad(azslParser::FunctionCallExpressionContext* ctx)
+    {
+        // First we must capture the complete name of the symbol that called <Symbol>.Load(...)
+        const auto expressionCtx = ctx->expression();
+        const UnqualifiedName uqSymbolName = GetSymbolName(expressionCtx);
+        const SubpassInputType subpassInputType = GetSubpassInputClass(uqSymbolName);
+        if (subpassInputType == SubpassInputType::None)
+        {
+            return;
+        }
+
+        // Define the mutations.
+        if (m_supportsSubpassInputs)
+        {
+            const auto argumentListCtx = ctx->argumentList();
+            if (argumentListCtx)
+            {
+                const auto argumentsCtx = argumentListCtx->arguments();
+                auto vectorOfArguments = argumentsCtx->expression();
+
+                // Found a "SubpassLoad" call and SubpassInputs are supported.
+                // We need to remove all parameters.
+                // m_subpassInput.SubpassLoad(int3(1, 2, 0)) ----> m_subpassInput.SubpassLoad()
+                for (uint32_t i = 0; i < vectorOfArguments.size(); ++i)
+                {
+                    auto secondArgContext = vectorOfArguments[i];
+                    const ssize_t startingTokenIndex = secondArgContext->start->getTokenIndex();
+                    const ssize_t stoppingTokenIndex = secondArgContext->stop->getTokenIndex();
+                    for (ssize_t tokenIndex = startingTokenIndex; tokenIndex <= stoppingTokenIndex; ++tokenIndex)
+                    {
+                        CodeMutation codeMutation;
+                        codeMutation.m_replace.emplace("");
+                        m_mutations.emplace(tokenIndex, codeMutation);
+                    }
+                }
+            }
+        }
+        else
+        {
+            // Replace SubpassLoad -> Load and keed the arguments
+            // m_subpassInput.SubpassLoad(int3(1, 2, 0)) ----> m_subpassInput.Load(int3(1, 2, 0))
+            CodeMutation codeMutation;
+            codeMutation.m_replace.emplace("Load");
+            m_mutations.emplace(expressionCtx->stop->getTokenIndex(), codeMutation);
+        }
+    }
+
+    SubpassInputToTexture2DCodeMutator::SubpassInputType SubpassInputToTexture2DCodeMutator::GetSubpassInputClass(const UnqualifiedName& uqSymbolName)
+    {
+        if (uqSymbolName.empty())
+        {
+            return SubpassInputType::None;
+        }
+        // We only care if the symbol that is calling SubpassLoad(...) is of type SubpassInput or SubpassInputMS
+        IdAndKind* idkind = m_ir->m_sema.LookupSymbol(uqSymbolName);
+        if (!idkind)
+        {
+            return SubpassInputType::None;
+        }
+        auto& [uid, kind] = *idkind;
+        if (kind.GetKind() != Kind::Variable)
+        {
+            return SubpassInputType::None;
+        }
+        auto varInfo = kind.GetSubAs<VarInfo>();
+        if (varInfo->GetTypeClass() != TypeClass::SubpassInput)
+        {
+            return SubpassInputType::None;
+        }
+        if (EndsWith(varInfo->m_typeInfoExt.m_coreType.m_typeId.GetName(), "MS"))
+        {
+            return SubpassInputType::SubpassInputMS;
+        }
+        return SubpassInputType::SubpassInput;
+    }
+
+    size_t SubpassInputToTexture2DCodeMutator::MutateTypeOfMultiSampleVariables(const vector<IdentifierUID>& subpassInputVariables)
+    {
+        size_t mutationCount = 0;
+        for (const auto& uid : subpassInputVariables)
+        {
+            auto varInfo = m_ir->GetSymbolSubAs<VarInfo>(uid.GetName());
+            auto& typeId = varInfo->m_typeInfoExt.m_coreType.m_typeId;
+            auto typeName = typeId.GetName();
+            if (typeName == "?SubpassInput")
+            {
+                typeId.m_name = QualifiedName{ "?Texture2D" };
+            }
+            else
+            {
+                typeId.m_name = QualifiedName{ "?Texture2DMS" };
+            }
+            ++mutationCount;
+        }
+
+        return mutationCount;
+    }
+} //namespace AZ::ShaderCompiler

+ 78 - 0
src/SubpassInputToTexture2DCodeMutator.h

@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * 
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "GenericUtils.h"
+#include "AzslcUtils.h"
+#include "AzslcCodeEmissionMutator.h"
+#include "AzslcIntermediateRepresentation.h"
+
+namespace AZ::ShaderCompiler
+{
+    //! This is the main class that handles coversion of SubpassInput related
+    //! variables, function calls and system semantics to the Texture2D versions.
+    //! 
+    //!     - As a first step, it implements azslParserBaseListener::enterFunctionCallExpression() so
+    //!       the SemaCheckListener can invoke it for the "SubpassLoad" function.
+    //!     - After AST parsing is done, RunMiddleEndMutations() is called at the end of IntermediateRepresentation::MiddleEnd() step to
+    //!      calculate further code mutations.
+    //!     - During code emission, the CodeEmitter invokes the ICodeEmissionMutator interface to see if token
+    //!       variables or lines of codes have mutations available.
+    struct SubpassInputToTexture2DCodeMutator 
+        : public azslParserBaseListener
+        , public ICodeEmissionMutator
+    {
+        SubpassInputToTexture2DCodeMutator() = delete;
+        explicit SubpassInputToTexture2DCodeMutator(IntermediateRepresentation* ir, CommonTokenStream* stream, bool supportsSubpassInput) 
+            : m_ir(ir)
+            , m_stream(stream)
+            , m_supportsSubpassInputs(supportsSubpassInput) {}
+        virtual ~SubpassInputToTexture2DCodeMutator() = default;
+
+        ///////////////////////////////////////////////////////////////////////
+        // azslParserBaseListener Overrides ...
+        void enterFunctionCallExpression(azslParser::FunctionCallExpressionContext* ctx) override;
+        ///////////////////////////////////////////////////////////////////////
+
+        ///////////////////////////////////////////////////////////////////////
+        // ICodeEmissionMutator Overrides ...
+        const CodeMutation* GetMutation(ssize_t tokenId) const override;
+        ///////////////////////////////////////////////////////////////////////
+
+        //! Should be called at the end of the IntermediateRepresentation::MiddleEnd() transformations.
+        bool RunMiddleEndMutations();
+
+    private:
+        //! Called when parsing a function call expression of type
+        //! SubpassInput.SubpasLoad(...)
+        void OnEnterLoad(azslParser::FunctionCallExpressionContext* ctx);
+
+        //! Given an unqualified symbol name, checks within the current parsing scope
+        //! if the symbol is a SubpassInput type of variable. 
+        enum class SubpassInputType { None, SubpassInput, SubpassInputMS };
+        SubpassInputType GetSubpassInputClass(const UnqualifiedName& uqSymbolName);
+
+        //! Changes the variable types:
+        //!     SubpassInput to Texture2D.
+        //!     SubpassInputMS to Texture2DMS. 
+        //! Returns the number of variables whose type was mutated
+        size_t MutateTypeOfMultiSampleVariables(const vector<IdentifierUID>& subpassInputVariables);
+        
+        //! Cached when RunMiddleEndMutations is called.
+        IntermediateRepresentation* m_ir = nullptr;
+
+        CommonTokenStream* m_stream = nullptr;
+
+        //! A map of TokenIndex to Mutation. If a TokenIndex is present,
+        //! it means it should produce mutated text during emission.
+        unordered_map<ssize_t, CodeMutation > m_mutations;
+
+        //! If subpass inputs are allowed.
+        bool m_supportsSubpassInputs = false;
+    };
+} // namespace AZ::ShaderCompiler

+ 15 - 2
src/azslParser.g4

@@ -447,7 +447,9 @@ predefinedType:
     |   structuredBufferPredefinedType
     |   texturePredefinedType
     |   genericTexturePredefinedType
+    |   genericSubpassInputPredefinedType
     |   msTexturePredefinedType
+    |   subpassInputPredefinedType
     |   vectorType
     |   genericVectorType
     |   constantBufferTemplated   // needed here to get recognized as a predefined type
@@ -569,8 +571,6 @@ textureType:
     |   RWTexture2D
     |   RWTexture2DArray
     |   RWTexture3D
-    |   SubpassInput
-    |   SubpassInputMS
 ;
 
 texturePredefinedType:
@@ -590,6 +590,19 @@ msTexturePredefinedType:
     textureTypeMS Less scalarOrVectorType (Comma Samples=IntegerLiteral)? Greater
 ;
 
+subpassInputType:    
+        SubpassInput
+    |   SubpassInputMS
+;
+
+subpassInputPredefinedType:
+    subpassInputType
+;
+
+genericSubpassInputPredefinedType:
+    subpassInputType Less scalarOrVectorType Greater
+;
+
 vectorType:
         Vector
     |   Bool1

File diff suppressed because it is too large
+ 456 - 449
src/generated/azslParser.cpp


+ 70 - 23
src/generated/azslParser.h

@@ -132,27 +132,28 @@ public:
     RuleScalarType = 73, RuleStreamOutputPredefinedType = 74, RuleStreamOutputObjectType = 75, 
     RuleStructuredBufferPredefinedType = 76, RuleStructuredBufferName = 77, 
     RuleTextureType = 78, RuleTexturePredefinedType = 79, RuleGenericTexturePredefinedType = 80, 
-    RuleTextureTypeMS = 81, RuleMsTexturePredefinedType = 82, RuleVectorType = 83, 
-    RuleGenericVectorType = 84, RuleScalarOrVectorType = 85, RuleScalarOrVectorOrMatrixType = 86, 
-    RuleMatrixType = 87, RuleGenericMatrixPredefinedType = 88, RuleRegisterAllocation = 89, 
-    RuleSamplerStateProperty = 90, RuleLiteral = 91, RuleLeadingTypeFunctionSignature = 92, 
-    RuleHlslFunctionDefinition = 93, RuleHlslFunctionDeclaration = 94, RuleUserDefinedType = 95, 
-    RuleAssociatedTypeDeclaration = 96, RuleTypedefStatement = 97, RuleTypealiasStatement = 98, 
-    RuleTypeAliasingDefinitionStatement = 99, RuleTypeofExpression = 100, 
-    RuleGenericParameterList = 101, RuleGenericTypeDefinition = 102, RuleGenericConstraint = 103, 
-    RuleLanguageDefinedConstraint = 104, RuleFunctionDeclaration = 105, 
-    RuleAttributedFunctionDeclaration = 106, RuleFunctionDefinition = 107, 
-    RuleAttributedFunctionDefinition = 108, RuleCompilerExtensionStatement = 109, 
-    RuleSrgDefinition = 110, RuleAttributedSrgDefinition = 111, RuleSrgMemberDeclaration = 112, 
-    RuleSrgSemantic = 113, RuleAttributedSrgSemantic = 114, RuleSrgSemanticBodyDeclaration = 115, 
-    RuleSrgSemanticMemberDeclaration = 116, RuleSamplerBodyDeclaration = 117, 
-    RuleSamplerMemberDeclaration = 118, RuleMaxAnisotropyOption = 119, RuleMinFilterOption = 120, 
-    RuleMagFilterOption = 121, RuleMipFilterOption = 122, RuleReductionTypeOption = 123, 
-    RuleComparisonFunctionOption = 124, RuleAddressUOption = 125, RuleAddressVOption = 126, 
-    RuleAddressWOption = 127, RuleMinLodOption = 128, RuleMaxLodOption = 129, 
-    RuleMipLodBiasOption = 130, RuleBorderColorOption = 131, RuleFilterModeEnum = 132, 
-    RuleReductionTypeEnum = 133, RuleAddressModeEnum = 134, RuleComparisonFunctionEnum = 135, 
-    RuleBorderColorEnum = 136
+    RuleTextureTypeMS = 81, RuleMsTexturePredefinedType = 82, RuleSubpassInputType = 83, 
+    RuleSubpassInputPredefinedType = 84, RuleGenericSubpassInputPredefinedType = 85, 
+    RuleVectorType = 86, RuleGenericVectorType = 87, RuleScalarOrVectorType = 88, 
+    RuleScalarOrVectorOrMatrixType = 89, RuleMatrixType = 90, RuleGenericMatrixPredefinedType = 91, 
+    RuleRegisterAllocation = 92, RuleSamplerStateProperty = 93, RuleLiteral = 94, 
+    RuleLeadingTypeFunctionSignature = 95, RuleHlslFunctionDefinition = 96, 
+    RuleHlslFunctionDeclaration = 97, RuleUserDefinedType = 98, RuleAssociatedTypeDeclaration = 99, 
+    RuleTypedefStatement = 100, RuleTypealiasStatement = 101, RuleTypeAliasingDefinitionStatement = 102, 
+    RuleTypeofExpression = 103, RuleGenericParameterList = 104, RuleGenericTypeDefinition = 105, 
+    RuleGenericConstraint = 106, RuleLanguageDefinedConstraint = 107, RuleFunctionDeclaration = 108, 
+    RuleAttributedFunctionDeclaration = 109, RuleFunctionDefinition = 110, 
+    RuleAttributedFunctionDefinition = 111, RuleCompilerExtensionStatement = 112, 
+    RuleSrgDefinition = 113, RuleAttributedSrgDefinition = 114, RuleSrgMemberDeclaration = 115, 
+    RuleSrgSemantic = 116, RuleAttributedSrgSemantic = 117, RuleSrgSemanticBodyDeclaration = 118, 
+    RuleSrgSemanticMemberDeclaration = 119, RuleSamplerBodyDeclaration = 120, 
+    RuleSamplerMemberDeclaration = 121, RuleMaxAnisotropyOption = 122, RuleMinFilterOption = 123, 
+    RuleMagFilterOption = 124, RuleMipFilterOption = 125, RuleReductionTypeOption = 126, 
+    RuleComparisonFunctionOption = 127, RuleAddressUOption = 128, RuleAddressVOption = 129, 
+    RuleAddressWOption = 130, RuleMinLodOption = 131, RuleMaxLodOption = 132, 
+    RuleMipLodBiasOption = 133, RuleBorderColorOption = 134, RuleFilterModeEnum = 135, 
+    RuleReductionTypeEnum = 136, RuleAddressModeEnum = 137, RuleComparisonFunctionEnum = 138, 
+    RuleBorderColorEnum = 139
   };
 
   explicit azslParser(antlr4::TokenStream *input);
@@ -255,6 +256,9 @@ public:
   class GenericTexturePredefinedTypeContext;
   class TextureTypeMSContext;
   class MsTexturePredefinedTypeContext;
+  class SubpassInputTypeContext;
+  class SubpassInputPredefinedTypeContext;
+  class GenericSubpassInputPredefinedTypeContext;
   class VectorTypeContext;
   class GenericVectorTypeContext;
   class ScalarOrVectorTypeContext;
@@ -1839,7 +1843,9 @@ public:
     StructuredBufferPredefinedTypeContext *structuredBufferPredefinedType();
     TexturePredefinedTypeContext *texturePredefinedType();
     GenericTexturePredefinedTypeContext *genericTexturePredefinedType();
+    GenericSubpassInputPredefinedTypeContext *genericSubpassInputPredefinedType();
     MsTexturePredefinedTypeContext *msTexturePredefinedType();
+    SubpassInputPredefinedTypeContext *subpassInputPredefinedType();
     VectorTypeContext *vectorType();
     GenericVectorTypeContext *genericVectorType();
     ConstantBufferTemplatedContext *constantBufferTemplated();
@@ -2109,8 +2115,6 @@ public:
     antlr4::tree::TerminalNode *RWTexture2D();
     antlr4::tree::TerminalNode *RWTexture2DArray();
     antlr4::tree::TerminalNode *RWTexture3D();
-    antlr4::tree::TerminalNode *SubpassInput();
-    antlr4::tree::TerminalNode *SubpassInputMS();
 
     virtual void enterRule(antlr4::tree::ParseTreeListener *listener) override;
     virtual void exitRule(antlr4::tree::ParseTreeListener *listener) override;
@@ -2181,6 +2185,49 @@ public:
 
   MsTexturePredefinedTypeContext* msTexturePredefinedType();
 
+  class  SubpassInputTypeContext : public antlr4::ParserRuleContext {
+  public:
+    SubpassInputTypeContext(antlr4::ParserRuleContext *parent, size_t invokingState);
+    virtual size_t getRuleIndex() const override;
+    antlr4::tree::TerminalNode *SubpassInput();
+    antlr4::tree::TerminalNode *SubpassInputMS();
+
+    virtual void enterRule(antlr4::tree::ParseTreeListener *listener) override;
+    virtual void exitRule(antlr4::tree::ParseTreeListener *listener) override;
+   
+  };
+
+  SubpassInputTypeContext* subpassInputType();
+
+  class  SubpassInputPredefinedTypeContext : public antlr4::ParserRuleContext {
+  public:
+    SubpassInputPredefinedTypeContext(antlr4::ParserRuleContext *parent, size_t invokingState);
+    virtual size_t getRuleIndex() const override;
+    SubpassInputTypeContext *subpassInputType();
+
+    virtual void enterRule(antlr4::tree::ParseTreeListener *listener) override;
+    virtual void exitRule(antlr4::tree::ParseTreeListener *listener) override;
+   
+  };
+
+  SubpassInputPredefinedTypeContext* subpassInputPredefinedType();
+
+  class  GenericSubpassInputPredefinedTypeContext : public antlr4::ParserRuleContext {
+  public:
+    GenericSubpassInputPredefinedTypeContext(antlr4::ParserRuleContext *parent, size_t invokingState);
+    virtual size_t getRuleIndex() const override;
+    SubpassInputTypeContext *subpassInputType();
+    antlr4::tree::TerminalNode *Less();
+    ScalarOrVectorTypeContext *scalarOrVectorType();
+    antlr4::tree::TerminalNode *Greater();
+
+    virtual void enterRule(antlr4::tree::ParseTreeListener *listener) override;
+    virtual void exitRule(antlr4::tree::ParseTreeListener *listener) override;
+   
+  };
+
+  GenericSubpassInputPredefinedTypeContext* genericSubpassInputPredefinedType();
+
   class  VectorTypeContext : public antlr4::ParserRuleContext {
   public:
     VectorTypeContext(antlr4::ParserRuleContext *parent, size_t invokingState);

+ 9 - 0
src/generated/azslParserBaseListener.h

@@ -352,6 +352,15 @@ public:
   virtual void enterMsTexturePredefinedType(azslParser::MsTexturePredefinedTypeContext * /*ctx*/) override { }
   virtual void exitMsTexturePredefinedType(azslParser::MsTexturePredefinedTypeContext * /*ctx*/) override { }
 
+  virtual void enterSubpassInputType(azslParser::SubpassInputTypeContext * /*ctx*/) override { }
+  virtual void exitSubpassInputType(azslParser::SubpassInputTypeContext * /*ctx*/) override { }
+
+  virtual void enterSubpassInputPredefinedType(azslParser::SubpassInputPredefinedTypeContext * /*ctx*/) override { }
+  virtual void exitSubpassInputPredefinedType(azslParser::SubpassInputPredefinedTypeContext * /*ctx*/) override { }
+
+  virtual void enterGenericSubpassInputPredefinedType(azslParser::GenericSubpassInputPredefinedTypeContext * /*ctx*/) override { }
+  virtual void exitGenericSubpassInputPredefinedType(azslParser::GenericSubpassInputPredefinedTypeContext * /*ctx*/) override { }
+
   virtual void enterVectorType(azslParser::VectorTypeContext * /*ctx*/) override { }
   virtual void exitVectorType(azslParser::VectorTypeContext * /*ctx*/) override { }
 

+ 9 - 0
src/generated/azslParserListener.h

@@ -350,6 +350,15 @@ public:
   virtual void enterMsTexturePredefinedType(azslParser::MsTexturePredefinedTypeContext *ctx) = 0;
   virtual void exitMsTexturePredefinedType(azslParser::MsTexturePredefinedTypeContext *ctx) = 0;
 
+  virtual void enterSubpassInputType(azslParser::SubpassInputTypeContext *ctx) = 0;
+  virtual void exitSubpassInputType(azslParser::SubpassInputTypeContext *ctx) = 0;
+
+  virtual void enterSubpassInputPredefinedType(azslParser::SubpassInputPredefinedTypeContext *ctx) = 0;
+  virtual void exitSubpassInputPredefinedType(azslParser::SubpassInputPredefinedTypeContext *ctx) = 0;
+
+  virtual void enterGenericSubpassInputPredefinedType(azslParser::GenericSubpassInputPredefinedTypeContext *ctx) = 0;
+  virtual void exitGenericSubpassInputPredefinedType(azslParser::GenericSubpassInputPredefinedTypeContext *ctx) = 0;
+
   virtual void enterVectorType(azslParser::VectorTypeContext *ctx) = 0;
   virtual void exitVectorType(azslParser::VectorTypeContext *ctx) = 0;
 

+ 37 - 0
tests/Emission/gmem-no-subpass-input.azsl

@@ -0,0 +1,37 @@
+ShaderResourceGroupSemantic slot1
+{
+    FrequencyId = 1;
+};
+
+/* tester */
+ShaderResourceGroup SRG : slot1
+{
+    struct CB
+    {
+        float4 color;
+    };
+    ConstantBuffer<CB> m_uniforms;
+
+    [[input_attachment_index(0)]]
+    SubpassInput<float4> m_sub;
+
+    float4x4 projection;
+};
+
+float4 ReadSourceFromTile(SubpassInput spi)
+{
+    return spi.SubpassLoad(int3(0,0,0));
+}
+
+// vertex
+float4 MainVS(float4 position : POSITION0) : POSITION0
+{
+    return mul(SRG::projection, position );
+}
+
+// pixel
+float4 MainPS(float2 uv : TEXCOORD0) : SV_Target0
+{
+    // SubpassInput si = SRG::m_sub;
+    return SRG::m_uniforms.color + ReadSourceFromTile(SRG::m_sub);
+}

+ 6 - 0
tests/Emission/gmem-no-subpass-input.txt

@@ -0,0 +1,6 @@
+/*
+    Cmdargs: ['--namespace', 'vk', '--no-subpass-input']
+*/
+"Texture2D SRG_m_sub : register ( t0 , space0 ) ;"
+"return spi . Load ( int3 ( 0 , 0 , 0 ) ) ;"
+"MainPS"

+ 1 - 1
tests/Emission/gmem.azsl

@@ -20,7 +20,7 @@ ShaderResourceGroup SRG : slot1
 
 float4 ReadSourceFromTile(SubpassInput spi)
 {
-    return spi.SubpassLoad();
+    return spi.SubpassLoad(int3(0,0,0));
 }
 
 // vertex

+ 1 - 7
tests/Emission/gmem.txt

@@ -1,14 +1,8 @@
 /*
     Cmdargs: ['--namespace', 'vk']
 */
-"# if ! defined ( AZ_USE_SUBPASSINPUT )"
-"class SubpassInputStub"
-"float4 SubpassLoad ( )"
-"class SubpassInputStubMS"
-"# define SubpassInput SubpassInputStub"
-"# ifdef AZ_USE_SUBPASSINPUT"
 "[[ vk :: input_attachment_index ( 0 ) ]]"
 "[[ vk :: binding ( 0 , 0 ) ]]"
-"# else"
 "SubpassInput SRG_m_sub ;"
+"return spi . SubpassLoad ( ) ;"
 "MainPS"

Some files were not shown because too many files changed in this diff