Vulkan: Move xfb position decl to translator in extension path

This change removes the @@ XFB-DECL @@ marker.  The ANGLEXfbPosition
output is unconditionally emitted in VS, TES and GS by the translator,
and is appropriately decorated or removed by the SPIR-V transformer.

Bug: angleproject:3606
Change-Id: Ia76224f5a6d147362eeb2d288f05e333aaf75481
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2617658
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Charlie Lao <cclao@google.com>
diff --git a/include/GLSLANG/ShaderLang.h b/include/GLSLANG/ShaderLang.h
index 4d3fba7..aaa089d 100644
--- a/include/GLSLANG/ShaderLang.h
+++ b/include/GLSLANG/ShaderLang.h
@@ -26,7 +26,7 @@
 
 // Version number for shader translation API.
 // It is incremented every time the API changes.
-#define ANGLE_SH_VERSION 249
+#define ANGLE_SH_VERSION 250
 
 enum ShShaderSpec
 {
@@ -348,9 +348,13 @@
 // Allow compiler to use specialization constant to do pre-rotation and y flip.
 const ShCompileOptions SH_USE_SPECIALIZATION_CONSTANT = UINT64_C(1) << 58;
 
-// Ask compiler to generate transform feedback emulation support code.
+// Ask compiler to generate Vulkan transform feedback emulation support code.
 const ShCompileOptions SH_ADD_VULKAN_XFB_EMULATION_SUPPORT_CODE = UINT64_C(1) << 59;
 
+// Ask compiler to generate Vulkan transform feedback support code when using the
+// VK_EXT_transform_feedback extension.
+const ShCompileOptions SH_ADD_VULKAN_XFB_EXTENSION_SUPPORT_CODE = UINT64_C(1) << 60;
+
 // Defines alternate strategies for implementing array index clamping.
 enum ShArrayIndexClampingStrategy
 {
@@ -873,12 +877,15 @@
 // Line raster emulation varying
 extern const char kLineRasterEmulationPosition[];
 
-// Transform feedback emulation helper function
+// Transform feedback emulation support
 extern const char kXfbEmulationGetOffsetsFunctionName[];
 extern const char kXfbEmulationBufferBlockName[];
 extern const char kXfbEmulationBufferName[];
 extern const char kXfbEmulationBufferFieldName[];
 
+// Transform feedback extension support
+extern const char kXfbExtensionPositionOutName[];
+
 }  // namespace vk
 
 namespace mtl
diff --git a/src/compiler/translator/ShaderLang.cpp b/src/compiler/translator/ShaderLang.cpp
index 5e2ed27..a07f28e 100644
--- a/src/compiler/translator/ShaderLang.cpp
+++ b/src/compiler/translator/ShaderLang.cpp
@@ -797,6 +797,8 @@
 const char kXfbEmulationBufferName[]             = "ANGLEXfb";
 const char kXfbEmulationBufferFieldName[]        = "xfbOut";
 
+const char kXfbExtensionPositionOutName[] = "ANGLEXfbPosition";
+
 }  // namespace vk
 
 }  // namespace sh
diff --git a/src/compiler/translator/TranslatorVulkan.cpp b/src/compiler/translator/TranslatorVulkan.cpp
index 8e80ed0..111202e 100644
--- a/src/compiler/translator/TranslatorVulkan.cpp
+++ b/src/compiler/translator/TranslatorVulkan.cpp
@@ -481,6 +481,48 @@
     return compiler->validateAST(root);
 }
 
+ANGLE_NO_DISCARD bool AddXfbExtensionSupport(TCompiler *compiler,
+                                             TIntermBlock *root,
+                                             TSymbolTable *symbolTable,
+                                             const DriverUniform *driverUniforms)
+{
+    // Generate the following output varying declaration used to capture transform feedback output
+    // from gl_Position, as it can't be captured directly due to changes that are applied to it for
+    // clip-space correction and pre-rotation.
+    //
+    //     out vec4 ANGLEXfbPosition;
+
+    const TType *vec4Type = nullptr;
+
+    switch (compiler->getShaderType())
+    {
+        case GL_VERTEX_SHADER:
+            vec4Type = StaticType::Get<EbtFloat, EbpHigh, EvqVertexOut, 4, 1>();
+            break;
+        case GL_TESS_EVALUATION_SHADER_EXT:
+            vec4Type = StaticType::Get<EbtFloat, EbpHigh, EvqTessEvaluationOut, 4, 1>();
+            break;
+        case GL_GEOMETRY_SHADER_EXT:
+            vec4Type = StaticType::Get<EbtFloat, EbpHigh, EvqGeometryOut, 4, 1>();
+            break;
+        default:
+            UNREACHABLE();
+    }
+
+    TVariable *varyingVar =
+        new TVariable(symbolTable, ImmutableString(vk::kXfbExtensionPositionOutName), vec4Type,
+                      SymbolType::AngleInternal);
+
+    TIntermDeclaration *varyingDecl = new TIntermDeclaration();
+    varyingDecl->appendDeclarator(new TIntermSymbol(varyingVar));
+
+    // Insert the varying declaration before the first function.
+    const size_t firstFunctionIndex = FindFirstFunctionDefinitionIndex(root);
+    root->insertChildNodes(firstFunctionIndex, {varyingDecl});
+
+    return compiler->validateAST(root);
+}
+
 ANGLE_NO_DISCARD bool InsertFragCoordCorrection(TCompiler *compiler,
                                                 ShCompileOptions compileOptions,
                                                 TIntermBlock *root,
@@ -829,8 +871,14 @@
 
     if (gl::ShaderTypeSupportsTransformFeedback(packedShaderType))
     {
-        // Add a macro to declare transform feedback buffers.
-        sink << "@@ XFB-DECL @@\n\n";
+        if (compileOptions & SH_ADD_VULKAN_XFB_EXTENSION_SUPPORT_CODE)
+        {
+            // Add support code for transform feedback extension.
+            if (!AddXfbExtensionSupport(this, root, &getSymbolTable(), driverUniforms))
+            {
+                return false;
+            }
+        }
 
         // Append a macro for transform feedback substitution prior to modifying depth.
         if (!AppendTransformFeedbackOutputToMain(this, root, &getSymbolTable()))
diff --git a/src/libANGLE/renderer/glslang_wrapper_utils.cpp b/src/libANGLE/renderer/glslang_wrapper_utils.cpp
index cd6f880..ec4457b 100644
--- a/src/libANGLE/renderer/glslang_wrapper_utils.cpp
+++ b/src/libANGLE/renderer/glslang_wrapper_utils.cpp
@@ -59,9 +59,7 @@
 {
 namespace
 {
-constexpr char kXfbDeclMarker[]    = "@@ XFB-DECL @@";
-constexpr char kXfbOutMarker[]     = "@@ XFB-OUT @@;";
-constexpr char kXfbBuiltInPrefix[] = "xfbANGLE";
+constexpr char kXfbOutMarker[] = "@@ XFB-OUT @@;";
 
 template <size_t N>
 constexpr size_t ConstStrLen(const char (&)[N])
@@ -298,30 +296,22 @@
 }
 
 std::string SubstituteTransformFeedbackMarkers(const std::string &originalSource,
-                                               const std::string &xfbDecl,
                                                const std::string &xfbOut)
 {
-    const size_t xfbDeclMarkerStart = originalSource.find(kXfbDeclMarker);
-    const size_t xfbDeclMarkerEnd   = xfbDeclMarkerStart + ConstStrLen(kXfbDeclMarker);
-
-    const size_t xfbOutMarkerStart = originalSource.find(kXfbOutMarker, xfbDeclMarkerStart);
+    const size_t xfbOutMarkerStart = originalSource.find(kXfbOutMarker);
     const size_t xfbOutMarkerEnd   = xfbOutMarkerStart + ConstStrLen(kXfbOutMarker);
 
     // The shader is the following form:
     //
     // ..part1..
-    // @@ XFB-DECL @@
-    // ..part2..
     // @@ XFB-OUT @@;
-    // ..part3..
+    // ..part2..
     //
-    // Construct the string by concatenating these five pieces, replacing the markers with the given
-    // values.
+    // Construct the string by concatenating these three pieces, replacing the marker with the given
+    // value.
     std::string result;
 
-    result.append(&originalSource[0], &originalSource[xfbDeclMarkerStart]);
-    result.append(xfbDecl);
-    result.append(&originalSource[xfbDeclMarkerEnd], &originalSource[xfbOutMarkerStart]);
+    result.append(&originalSource[0], &originalSource[xfbOutMarkerStart]);
     result.append(xfbOut);
     result.append(&originalSource[xfbOutMarkerEnd], &originalSource[originalSource.size()]);
 
@@ -403,6 +393,50 @@
     }
 }
 
+void AssignTransformFeedbackExtensionLocations(gl::ShaderType shaderType,
+                                               const gl::ProgramState &programState,
+                                               bool isTransformFeedbackStage,
+                                               GlslangProgramInterfaceInfo *programInterfaceInfo,
+                                               ShaderInterfaceVariableInfoMap *variableInfoMapOut)
+{
+    // The only varying that requires additional resources is gl_Position, as it's indirectly
+    // captured through ANGLEXfbPosition.
+
+    const std::vector<gl::TransformFeedbackVarying> &tfVaryings =
+        programState.getLinkedTransformFeedbackVaryings();
+
+    bool capturesPosition = false;
+
+    if (isTransformFeedbackStage)
+    {
+        for (uint32_t varyingIndex = 0; varyingIndex < tfVaryings.size(); ++varyingIndex)
+        {
+            const gl::TransformFeedbackVarying &tfVarying = tfVaryings[varyingIndex];
+            const std::string &tfVaryingName              = tfVarying.mappedName;
+
+            if (tfVaryingName == "gl_Position")
+            {
+                ASSERT(tfVarying.isBuiltIn());
+                capturesPosition = true;
+                break;
+            }
+        }
+    }
+
+    if (capturesPosition)
+    {
+        AddLocationInfo(variableInfoMapOut, shaderType, sh::vk::kXfbExtensionPositionOutName,
+                        programInterfaceInfo->locationsUsedForXfbExtension, 0, 0, 0);
+        ++programInterfaceInfo->locationsUsedForXfbExtension;
+    }
+    else
+    {
+        // Make sure this varying is removed from the other stages, or if position is not captured
+        // at all.
+        variableInfoMapOut->add(shaderType, sh::vk::kXfbExtensionPositionOutName);
+    }
+}
+
 void GenerateTransformFeedbackEmulationOutputs(const GlslangSourceOptions &options,
                                                gl::ShaderType shaderType,
                                                const gl::ProgramState &programState,
@@ -459,7 +493,7 @@
     }
     xfbOut << "}\n";
 
-    *vertexShader = SubstituteTransformFeedbackMarkers(*vertexShader, "", xfbOut.str());
+    *vertexShader = SubstituteTransformFeedbackMarkers(*vertexShader, xfbOut.str());
 }
 
 bool IsFirstRegisterOfVarying(const gl::PackedVaryingRegister &varyingReg, bool allowFields)
@@ -495,14 +529,11 @@
 // Calculates XFB layout qualifier arguments for each tranform feedback varying.  Stores calculated
 // values for the SPIR-V transformation.
 void GenerateTransformFeedbackExtensionOutputs(const gl::ProgramState &programState,
-                                               const gl::VaryingPacking &varyingPacking,
-                                               std::string *xfbShaderSource,
-                                               uint32_t *locationsUsedForXfbExtensionOut)
+                                               std::string *xfbShaderSource)
 {
     const std::vector<gl::TransformFeedbackVarying> &tfVaryings =
         programState.getLinkedTransformFeedbackVaryings();
 
-    std::string xfbDecl;
     std::string xfbOut;
 
     for (uint32_t varyingIndex = 0; varyingIndex < tfVaryings.size(); ++varyingIndex)
@@ -514,25 +545,14 @@
         {
             ASSERT(tfVarying.isBuiltIn());
 
-            // For gl_Position, create a copy of the builtin so xfb qualifiers could be added to
-            // that instead.  gl_Position is modified by the shader (to account for Vulkan depth
-            // clip space and prerotation), so it cannot be captured directly.
-            //
-            // The rest of the builtins are captured by decorating gl_PerVertex directly.
-            uint32_t xfbVaryingLocation =
-                varyingPacking.getMaxSemanticIndex() + ++(*locationsUsedForXfbExtensionOut);
-
-            std::string xfbVaryingName = kXfbBuiltInPrefix + tfVaryingName;
-
-            // Add declaration and initialization code for the new varying.
-            std::string varyingType = gl::GetGLSLTypeString(tfVarying.type);
-            xfbDecl += "layout(location = " + Str(xfbVaryingLocation) + ") out " + varyingType +
-                       " " + xfbVaryingName + ";\n";
-            xfbOut += xfbVaryingName + " = " + tfVaryingName + ";\n";
+            // Add initialization code for the position varying.
+            xfbOut = sh::vk::kXfbExtensionPositionOutName;
+            xfbOut += " = " + tfVaryingName + ";\n";
+            break;
         }
     }
 
-    *xfbShaderSource = SubstituteTransformFeedbackMarkers(*xfbShaderSource, xfbDecl, xfbOut);
+    *xfbShaderSource = SubstituteTransformFeedbackMarkers(*xfbShaderSource, xfbOut);
 }
 
 void AssignAttributeLocations(const gl::ProgramExecutable &programExecutable,
@@ -717,7 +737,6 @@
 // values for the SPIR-V transformation.
 void AssignTransformFeedbackExtensionQualifiers(const gl::ProgramExecutable &programExecutable,
                                                 const gl::VaryingPacking &varyingPacking,
-                                                uint32_t locationsUsedForXfbExtension,
                                                 const gl::ShaderType shaderType,
                                                 ShaderInterfaceVariableInfoMap *variableInfoMapOut)
 {
@@ -727,12 +746,9 @@
     const bool isInterleaved =
         programExecutable.getTransformFeedbackBufferMode() == GL_INTERLEAVED_ATTRIBS;
 
-    std::string xfbDecl;
-    std::string xfbOut;
-    uint32_t currentOffset          = 0;
-    uint32_t currentStride          = 0;
-    uint32_t bufferIndex            = 0;
-    uint32_t currentBuiltinLocation = 0;
+    uint32_t currentOffset = 0;
+    uint32_t currentStride = 0;
+    uint32_t bufferIndex   = 0;
 
     for (uint32_t varyingIndex = 0; varyingIndex < tfVaryings.size(); ++varyingIndex)
     {
@@ -759,15 +775,8 @@
         {
             if (tfVarying.name == "gl_Position")
             {
-                uint32_t xfbVaryingLocation = currentBuiltinLocation++;
-                std::string xfbVaryingName  = kXfbBuiltInPrefix + tfVarying.mappedName;
-
-                ASSERT(xfbVaryingLocation < locationsUsedForXfbExtension);
-
-                AddLocationInfo(variableInfoMapOut, shaderType, xfbVaryingName, xfbVaryingLocation,
-                                ShaderInterfaceVariableInfo::kInvalid, 0, 0);
-                SetXfbInfo(variableInfoMapOut, shaderType, xfbVaryingName, -1, bufferIndex,
-                           currentOffset, currentStride);
+                SetXfbInfo(variableInfoMapOut, shaderType, sh::vk::kXfbExtensionPositionOutName, -1,
+                           bufferIndex, currentOffset, currentStride);
             }
             else
             {
@@ -4386,6 +4395,15 @@
         const gl::VaryingPacking &inputPacking  = varyingPacking.getInputPacking(shaderType);
         const gl::VaryingPacking &outputPacking = varyingPacking.getOutputPacking(shaderType);
 
+        // Assign location to varyings generated for transform feedback capture
+        if (options.supportsTransformFeedbackExtension &&
+            gl::ShaderTypeSupportsTransformFeedback(shaderType))
+        {
+            AssignTransformFeedbackExtensionLocations(shaderType, programState,
+                                                      isTransformFeedbackStage,
+                                                      programInterfaceInfo, variableInfoMapOut);
+        }
+
         // Assign varying locations.
         if (shaderType != gl::ShaderType::Vertex)
         {
@@ -4398,13 +4416,13 @@
                                    programInterfaceInfo, variableInfoMapOut);
         }
 
+        // Assign qualifiers to all varyings captured by transform feedback
         if (!programExecutable.getLinkedTransformFeedbackVaryings().empty() &&
             options.supportsTransformFeedbackExtension &&
             (shaderType == programExecutable.getLinkedTransformFeedbackStage()))
         {
-            AssignTransformFeedbackExtensionQualifiers(
-                programExecutable, outputPacking,
-                programInterfaceInfo->locationsUsedForXfbExtension, shaderType, variableInfoMapOut);
+            AssignTransformFeedbackExtensionQualifiers(programExecutable, outputPacking, shaderType,
+                                                       variableInfoMapOut);
         }
     }
 
@@ -4445,9 +4463,7 @@
         {
             if (options.supportsTransformFeedbackExtension)
             {
-                GenerateTransformFeedbackExtensionOutputs(
-                    programState, resources.varyingPacking.getOutputPacking(xfbStage), xfbSource,
-                    &programInterfaceInfo->locationsUsedForXfbExtension);
+                GenerateTransformFeedbackExtensionOutputs(programState, xfbSource);
             }
             else if (options.emulateTransformFeedback)
             {
@@ -4458,25 +4474,25 @@
             }
             else
             {
-                *xfbSource = SubstituteTransformFeedbackMarkers(*xfbSource, "", "");
+                *xfbSource = SubstituteTransformFeedbackMarkers(*xfbSource, "");
             }
         }
         else
         {
-            *xfbSource = SubstituteTransformFeedbackMarkers(*xfbSource, "", "");
+            *xfbSource = SubstituteTransformFeedbackMarkers(*xfbSource, "");
         }
     }
 
     std::string *tessEvalSources = &(*shaderSourcesOut)[gl::ShaderType::TessEvaluation];
     if (xfbStage > gl::ShaderType::TessEvaluation && !tessEvalSources->empty())
     {
-        *tessEvalSources = SubstituteTransformFeedbackMarkers(*tessEvalSources, "", "");
+        *tessEvalSources = SubstituteTransformFeedbackMarkers(*tessEvalSources, "");
     }
 
     std::string *vertexSource = &(*shaderSourcesOut)[gl::ShaderType::Vertex];
     if (xfbStage > gl::ShaderType::Vertex && !vertexSource->empty())
     {
-        *vertexSource = SubstituteTransformFeedbackMarkers(*vertexSource, "", "");
+        *vertexSource = SubstituteTransformFeedbackMarkers(*vertexSource, "");
     }
 
     gl::ShaderType frontShaderType = gl::ShaderType::InvalidEnum;
diff --git a/src/libANGLE/renderer/vulkan/ShaderVk.cpp b/src/libANGLE/renderer/vulkan/ShaderVk.cpp
index 8a99044..055c78a 100644
--- a/src/libANGLE/renderer/vulkan/ShaderVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ShaderVk.cpp
@@ -87,7 +87,11 @@
         compileOptions |= SH_ADD_PRE_ROTATION;
     }
 
-    if (contextVk->getFeatures().emulateTransformFeedback.enabled)
+    if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
+    {
+        compileOptions |= SH_ADD_VULKAN_XFB_EXTENSION_SUPPORT_CODE;
+    }
+    else if (contextVk->getFeatures().emulateTransformFeedback.enabled)
     {
         compileOptions |= SH_ADD_VULKAN_XFB_EMULATION_SUPPORT_CODE;
     }