GL: Support clip and cull distance redeclarations

* Fixed gl_PerVertex qualifier string.
* Updated ValidateClipCullDistanceTraverser to output
redeclared array sizes and maximum constant indices.
* Made DeclarePerVertexBlocks available for non-Vulkan
outputs.
* Updated DeclarePerVertexBlocks to remove gl_ClipDistance
and gl_CullDistance redeclarations.
* Enabled DeclarePerVertexBlocks for ESSL output when
gl_ClipDistance or gl_CullDistance are redeclared.
* Updated ESSL output to use 3.10 shading language version,
when the context has GL_EXT_clip_cull_distance enabled.
* Updated ESSL output to enable GL_EXT_shader_io_blocks
when gl_ClipDistance or gl_CullDistance are redeclared.
* Updated extension exposure conditions.
* Fixed typos in ParseContext.

Bug: angleproject:7763
Change-Id: Ib87368a1953ad546a407d634d8b00f71cf92c40c
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4083705
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Alexey Knyazev <lexa.knyazev@gmail.com>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
diff --git a/src/compiler.gni b/src/compiler.gni
index a264c35..3d5edbf 100644
--- a/src/compiler.gni
+++ b/src/compiler.gni
@@ -131,6 +131,8 @@
   "src/compiler/translator/tree_ops/ConvertUnsupportedConstructorsToFunctionCalls.h",
   "src/compiler/translator/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.cpp",
   "src/compiler/translator/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.h",
+  "src/compiler/translator/tree_ops/DeclarePerVertexBlocks.cpp",
+  "src/compiler/translator/tree_ops/DeclarePerVertexBlocks.h",
   "src/compiler/translator/tree_ops/DeferGlobalInitializers.cpp",
   "src/compiler/translator/tree_ops/DeferGlobalInitializers.h",
   "src/compiler/translator/tree_ops/EmulateGLFragColorBroadcast.cpp",
@@ -343,8 +345,6 @@
   "src/compiler/translator/OutputSPIRV.cpp",
   "src/compiler/translator/OutputSPIRV.h",
   "src/compiler/translator/TranslatorVulkan.cpp",
-  "src/compiler/translator/tree_ops/vulkan/DeclarePerVertexBlocks.cpp",
-  "src/compiler/translator/tree_ops/vulkan/DeclarePerVertexBlocks.h",
   "src/compiler/translator/tree_ops/vulkan/EmulateAdvancedBlendEquations.cpp",
   "src/compiler/translator/tree_ops/vulkan/EmulateAdvancedBlendEquations.h",
   "src/compiler/translator/tree_ops/vulkan/EmulateDithering.cpp",
diff --git a/src/compiler/translator/BaseTypes.h b/src/compiler/translator/BaseTypes.h
index c99953e..8da1a95 100644
--- a/src/compiler/translator/BaseTypes.h
+++ b/src/compiler/translator/BaseTypes.h
@@ -1615,7 +1615,7 @@
     case EvqPatchOut:               return "patch out";
     case EvqTessControlIn:          return "in";
     case EvqTessControlOut:         return "out";
-    case EvqPerVertexOut:           return "gl_out";
+    case EvqPerVertexOut:           return "out";
     case EvqPatchVerticesIn:        return "PatchVerticesIn";
     case EvqTessLevelOuter:         return "TessLevelOuter";
     case EvqTessLevelInner:         return "TessLevelInner";
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index 2e5e335..92de5a9 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -841,11 +841,13 @@
         return false;
     }
 
-    if (parseContext.isExtensionEnabled(TExtension::EXT_clip_cull_distance))
+    if (parseContext.isExtensionEnabled(TExtension::EXT_clip_cull_distance) ||
+        parseContext.isExtensionEnabled(TExtension::APPLE_clip_distance))
     {
-        if (!ValidateClipCullDistance(root, &mDiagnostics,
-                                      mResources.MaxCombinedClipAndCullDistances,
-                                      compileOptions.limitSimultaneousClipAndCullDistanceUsage))
+        if (!ValidateClipCullDistance(
+                root, &mDiagnostics, mResources.MaxCombinedClipAndCullDistances,
+                compileOptions.limitSimultaneousClipAndCullDistanceUsage, &mClipDistanceSize,
+                &mCullDistanceSize, &mClipDistanceMaxIndex, &mCullDistanceMaxIndex))
         {
             return false;
         }
@@ -1436,6 +1438,11 @@
 
     mNumViews = -1;
 
+    mClipDistanceSize     = 0;
+    mCullDistanceSize     = 0;
+    mClipDistanceMaxIndex = -1;
+    mCullDistanceMaxIndex = -1;
+
     mGeometryShaderInputPrimitiveType  = EptUndefined;
     mGeometryShaderOutputPrimitiveType = EptUndefined;
     mGeometryShaderInvocations         = 0;
diff --git a/src/compiler/translator/Compiler.h b/src/compiler/translator/Compiler.h
index 8a87d5e..f209997 100644
--- a/src/compiler/translator/Compiler.h
+++ b/src/compiler/translator/Compiler.h
@@ -207,6 +207,21 @@
     // it's expected to no longer transform.
     void enableValidateNoMoreTransformations();
 
+    bool areClipDistanceOrCullDistanceRedeclared() const
+    {
+        return mClipDistanceSize != 0 || mCullDistanceSize != 0;
+    }
+
+    uint8_t getClipDistanceArraySize() const
+    {
+        return mClipDistanceSize ? mClipDistanceSize : (mClipDistanceMaxIndex + 1);
+    }
+
+    uint8_t getCullDistanceArraySize() const
+    {
+        return mCullDistanceSize ? mCullDistanceSize : (mCullDistanceMaxIndex + 1);
+    }
+
   protected:
     // Add emulated functions to the built-in function emulator.
     virtual void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu,
@@ -339,6 +354,12 @@
     // GL_OVR_multiview num_views.
     int mNumViews;
 
+    // Track gl_ClipDistance / gl_CullDistance usage.
+    uint8_t mClipDistanceSize;
+    uint8_t mCullDistanceSize;
+    int8_t mClipDistanceMaxIndex;
+    int8_t mCullDistanceMaxIndex;
+
     // geometry shader parameters.
     int mGeometryShaderMaxVertices;
     int mGeometryShaderInvocations;
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index 5878d50..46195be 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -4991,7 +4991,7 @@
             if (field->name() == "gl_Position" || field->name() == "gl_PointSize" ||
                 field->name() == "gl_ClipDistance" || field->name() == "gl_CullDistance")
             {
-                // These builtins can be redifined only when used within a redefiend gl_PerVertex
+                // These builtins can be redefined only when used within a redefined gl_PerVertex
                 // block
                 if (interfaceBlock->name() != "gl_PerVertex")
                 {
diff --git a/src/compiler/translator/ParseContext.h b/src/compiler/translator/ParseContext.h
index 2397df9..b418216 100644
--- a/src/compiler/translator/ParseContext.h
+++ b/src/compiler/translator/ParseContext.h
@@ -612,10 +612,6 @@
                                                             TType type,
                                                             const TSourceLoc &line);
 
-    void checkCombinedClipCullDistanceIsValid(const TSourceLoc &line,
-                                              const ImmutableString &identifier,
-                                              const int arraySize);
-
     // Check texture offset is within range.
     void checkSingleTextureOffset(const TSourceLoc &line,
                                   const TConstantUnion *values,
diff --git a/src/compiler/translator/TranslatorESSL.cpp b/src/compiler/translator/TranslatorESSL.cpp
index a9b4ed4..58cda4e 100644
--- a/src/compiler/translator/TranslatorESSL.cpp
+++ b/src/compiler/translator/TranslatorESSL.cpp
@@ -10,6 +10,7 @@
 #include "common/utilities.h"
 #include "compiler/translator/BuiltInFunctionEmulatorGLSL.h"
 #include "compiler/translator/OutputESSL.h"
+#include "compiler/translator/tree_ops/DeclarePerVertexBlocks.h"
 #include "compiler/translator/tree_ops/RecordConstantPrecision.h"
 
 namespace sh
@@ -35,10 +36,12 @@
     TInfoSinkBase &sink = getInfoSink().obj;
 
     int shaderVer = getShaderVersion();  // Frontend shader version.
-    if (hasPixelLocalStorageUniforms() &&
-        compileOptions.pls.type == ShPixelLocalStorageType::ImageLoadStore)
+    if ((shaderVer > 100 && getResources().EXT_clip_cull_distance) ||
+        (hasPixelLocalStorageUniforms() &&
+         compileOptions.pls.type == ShPixelLocalStorageType::ImageLoadStore))
     {
-        // The backend translator emits shader image code. Use a minimum version of 310.
+        // The backend translator emits interface blocks or shader image code.
+        // Use a minimum version of 310.
         shaderVer = std::max(shaderVer, 310);
     }
     if (shaderVer > 100)
@@ -79,6 +82,17 @@
         sink << "// END: Generated code for built-in function emulation\n\n";
     }
 
+    if (getShaderType() == GL_VERTEX_SHADER)
+    {
+        // Move gl_ClipDistance and/or gl_CullDistance redeclarations to gl_PerVertex.
+        if (IsExtensionEnabled(getExtensionBehavior(), TExtension::EXT_clip_cull_distance) &&
+            areClipDistanceOrCullDistanceRedeclared() &&
+            !DeclarePerVertexBlocks(this, root, &getSymbolTable()))
+        {
+            return false;
+        }
+    }
+
     if (getShaderType() == GL_FRAGMENT_SHADER)
     {
         EmitEarlyFragmentTestsGLSL(*this, sink);
@@ -178,6 +192,14 @@
                 ASSERT(compileOptions.emulateGLBaseVertexBaseInstance);
                 continue;
             }
+            else if (iter->first == TExtension::EXT_clip_cull_distance &&
+                     areClipDistanceOrCullDistanceRedeclared())
+            {
+                sink << "#extension GL_EXT_clip_cull_distance : " << GetBehaviorString(iter->second)
+                     << "\n"
+                     << "#extension GL_EXT_shader_io_blocks : " << GetBehaviorString(iter->second)
+                     << "\n";
+            }
             else if (iter->first == TExtension::ANGLE_shader_pixel_local_storage)
             {
                 if (compileOptions.pls.type == ShPixelLocalStorageType::PixelLocalStorageEXT)
diff --git a/src/compiler/translator/TranslatorVulkan.cpp b/src/compiler/translator/TranslatorVulkan.cpp
index e415b9c..3af080d 100644
--- a/src/compiler/translator/TranslatorVulkan.cpp
+++ b/src/compiler/translator/TranslatorVulkan.cpp
@@ -19,6 +19,7 @@
 #include "compiler/translator/IntermNode.h"
 #include "compiler/translator/OutputSPIRV.h"
 #include "compiler/translator/StaticType.h"
+#include "compiler/translator/tree_ops/DeclarePerVertexBlocks.h"
 #include "compiler/translator/tree_ops/MonomorphizeUnsupportedFunctions.h"
 #include "compiler/translator/tree_ops/RecordConstantPrecision.h"
 #include "compiler/translator/tree_ops/RemoveAtomicCounterBuiltins.h"
@@ -29,7 +30,6 @@
 #include "compiler/translator/tree_ops/RewriteDfdy.h"
 #include "compiler/translator/tree_ops/RewriteStructSamplers.h"
 #include "compiler/translator/tree_ops/SeparateStructFromUniformDeclarations.h"
-#include "compiler/translator/tree_ops/vulkan/DeclarePerVertexBlocks.h"
 #include "compiler/translator/tree_ops/vulkan/EmulateAdvancedBlendEquations.h"
 #include "compiler/translator/tree_ops/vulkan/EmulateDithering.h"
 #include "compiler/translator/tree_ops/vulkan/EmulateFragColorData.h"
diff --git a/src/compiler/translator/ValidateClipCullDistance.cpp b/src/compiler/translator/ValidateClipCullDistance.cpp
index 18fca2b..9b364b9 100644
--- a/src/compiler/translator/ValidateClipCullDistance.cpp
+++ b/src/compiler/translator/ValidateClipCullDistance.cpp
@@ -31,17 +31,21 @@
     ValidateClipCullDistanceTraverser();
     void validate(TDiagnostics *diagnostics,
                   const unsigned int maxCombinedClipAndCullDistances,
-                  const bool limitSimultaneousClipAndCullDistanceUsage);
+                  const bool limitSimultaneousClipAndCullDistanceUsage,
+                  uint8_t *clipDistanceSizeOut,
+                  uint8_t *cullDistanceSizeOut,
+                  int8_t *clipDistanceMaxIndexOut,
+                  int8_t *cullDistanceMaxIndexOut);
 
   private:
     bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
     bool visitBinary(Visit visit, TIntermBinary *node) override;
 
-    unsigned int mClipDistanceSize;
-    unsigned int mCullDistanceSize;
+    uint8_t mClipDistanceSize;
+    uint8_t mCullDistanceSize;
 
-    unsigned int mMaxClipDistanceIndex;
-    unsigned int mMaxCullDistanceIndex;
+    int8_t mMaxClipDistanceIndex;
+    int8_t mMaxCullDistanceIndex;
 
     const TIntermSymbol *mClipDistance;
     const TIntermSymbol *mCullDistance;
@@ -51,8 +55,8 @@
     : TIntermTraverser(true, false, false),
       mClipDistanceSize(0),
       mCullDistanceSize(0),
-      mMaxClipDistanceIndex(0),
-      mMaxCullDistanceIndex(0),
+      mMaxClipDistanceIndex(-1),
+      mMaxCullDistanceIndex(-1),
       mClipDistance(nullptr),
       mCullDistance(nullptr)
 {}
@@ -74,12 +78,12 @@
 
     if (symbol->getName() == "gl_ClipDistance")
     {
-        mClipDistanceSize = symbol->getOutermostArraySize();
+        mClipDistanceSize = static_cast<uint8_t>(symbol->getOutermostArraySize());
         mClipDistance     = symbol;
     }
     else if (symbol->getName() == "gl_CullDistance")
     {
-        mCullDistanceSize = symbol->getOutermostArraySize();
+        mCullDistanceSize = static_cast<uint8_t>(symbol->getOutermostArraySize());
         mCullDistance     = symbol;
     }
 
@@ -109,7 +113,7 @@
     const TConstantUnion *constIdx = node->getRight()->getConstantValue();
     if (constIdx)
     {
-        unsigned int idx = 0;
+        int idx = 0;
         switch (constIdx->getType())
         {
             case EbtInt:
@@ -119,7 +123,7 @@
                 idx = constIdx->getUConst();
                 break;
             case EbtFloat:
-                idx = static_cast<unsigned int>(constIdx->getFConst());
+                idx = static_cast<int>(constIdx->getFConst());
                 break;
             case EbtBool:
                 idx = constIdx->getBConst() ? 1 : 0;
@@ -133,7 +137,7 @@
         {
             if (idx > mMaxClipDistanceIndex)
             {
-                mMaxClipDistanceIndex = idx;
+                mMaxClipDistanceIndex = static_cast<int8_t>(idx);
                 if (!mClipDistance)
                 {
                     mClipDistance = left;
@@ -145,7 +149,7 @@
             ASSERT(varName == "gl_CullDistance");
             if (idx > mMaxCullDistanceIndex)
             {
-                mMaxCullDistanceIndex = idx;
+                mMaxCullDistanceIndex = static_cast<int8_t>(idx);
                 if (!mCullDistance)
                 {
                     mCullDistance = left;
@@ -160,7 +164,11 @@
 void ValidateClipCullDistanceTraverser::validate(
     TDiagnostics *diagnostics,
     const unsigned int maxCombinedClipAndCullDistances,
-    const bool limitSimultaneousClipAndCullDistanceUsage)
+    const bool limitSimultaneousClipAndCullDistanceUsage,
+    uint8_t *clipDistanceSizeOut,
+    uint8_t *cullDistanceSizeOut,
+    int8_t *clipDistanceMaxIndexOut,
+    int8_t *cullDistanceMaxIndexOut)
 {
     ASSERT(diagnostics);
 
@@ -196,6 +204,12 @@
               "not be greater than 4.",
               diagnostics);
     }
+
+    // Update the compiler state
+    *clipDistanceSizeOut     = mClipDistanceSize;
+    *cullDistanceSizeOut     = mCullDistanceSize;
+    *clipDistanceMaxIndexOut = mMaxClipDistanceIndex;
+    *cullDistanceMaxIndexOut = mMaxCullDistanceIndex;
 }
 
 }  // anonymous namespace
@@ -203,13 +217,18 @@
 bool ValidateClipCullDistance(TIntermBlock *root,
                               TDiagnostics *diagnostics,
                               const unsigned int maxCombinedClipAndCullDistances,
-                              const bool limitSimultaneousClipAndCullDistanceUsage)
+                              const bool limitSimultaneousClipAndCullDistanceUsage,
+                              uint8_t *clipDistanceSizeOut,
+                              uint8_t *cullDistanceSizeOut,
+                              int8_t *clipDistanceMaxIndexOut,
+                              int8_t *cullDistanceMaxIndexOut)
 {
     ValidateClipCullDistanceTraverser varyingValidator;
     root->traverse(&varyingValidator);
     int numErrorsBefore = diagnostics->numErrors();
-    varyingValidator.validate(diagnostics, maxCombinedClipAndCullDistances,
-                              limitSimultaneousClipAndCullDistanceUsage);
+    varyingValidator.validate(
+        diagnostics, maxCombinedClipAndCullDistances, limitSimultaneousClipAndCullDistanceUsage,
+        clipDistanceSizeOut, cullDistanceSizeOut, clipDistanceMaxIndexOut, cullDistanceMaxIndexOut);
     return (diagnostics->numErrors() == numErrorsBefore);
 }
 
diff --git a/src/compiler/translator/ValidateClipCullDistance.h b/src/compiler/translator/ValidateClipCullDistance.h
index 2e63e6c..2be74fa 100644
--- a/src/compiler/translator/ValidateClipCullDistance.h
+++ b/src/compiler/translator/ValidateClipCullDistance.h
@@ -21,7 +21,11 @@
 bool ValidateClipCullDistance(TIntermBlock *root,
                               TDiagnostics *diagnostics,
                               const unsigned int maxCombinedClipAndCullDistances,
-                              const bool limitSimultaneousClipAndCullDistanceUsage);
+                              const bool limitSimultaneousClipAndCullDistanceUsage,
+                              uint8_t *clipDistanceSizeOut,
+                              uint8_t *cullDistanceSizeOut,
+                              int8_t *clipDistanceMaxIndexOut,
+                              int8_t *cullDistanceMaxIndexOut);
 
 }  // namespace sh
 
diff --git a/src/compiler/translator/tree_ops/vulkan/DeclarePerVertexBlocks.cpp b/src/compiler/translator/tree_ops/DeclarePerVertexBlocks.cpp
similarity index 84%
rename from src/compiler/translator/tree_ops/vulkan/DeclarePerVertexBlocks.cpp
rename to src/compiler/translator/tree_ops/DeclarePerVertexBlocks.cpp
index 45fd094..90f2fe0 100644
--- a/src/compiler/translator/tree_ops/vulkan/DeclarePerVertexBlocks.cpp
+++ b/src/compiler/translator/tree_ops/DeclarePerVertexBlocks.cpp
@@ -6,7 +6,7 @@
 // DeclarePerVertexBlocks: Declare gl_PerVertex blocks if not already.
 //
 
-#include "compiler/translator/tree_ops/vulkan/DeclarePerVertexBlocks.h"
+#include "compiler/translator/tree_ops/DeclarePerVertexBlocks.h"
 
 #include "compiler/translator/Compiler.h"
 #include "compiler/translator/ImmutableStringBuilder.h"
@@ -45,23 +45,18 @@
 
 // Traverser that:
 //
-// 1. Inspects global qualifier declarations and extracts whether any of the gl_PerVertex built-ins
-//    are invariant or precise.  These declarations are then dropped.
-// 2. Finds the array size of gl_ClipDistance and gl_CullDistance built-in, if any.
+// Inspects global qualifier declarations and extracts whether any of the gl_PerVertex built-ins
+// are invariant or precise. These declarations are then dropped.
 class InspectPerVertexBuiltInsTraverser : public TIntermTraverser
 {
   public:
     InspectPerVertexBuiltInsTraverser(TCompiler *compiler,
                                       TSymbolTable *symbolTable,
                                       PerVertexMemberFlags *invariantFlagsOut,
-                                      PerVertexMemberFlags *preciseFlagsOut,
-                                      uint32_t *clipDistanceArraySizeOut,
-                                      uint32_t *cullDistanceArraySizeOut)
+                                      PerVertexMemberFlags *preciseFlagsOut)
         : TIntermTraverser(true, false, false, symbolTable),
           mInvariantFlagsOut(invariantFlagsOut),
-          mPreciseFlagsOut(preciseFlagsOut),
-          mClipDistanceArraySizeOut(clipDistanceArraySizeOut),
-          mCullDistanceArraySizeOut(cullDistanceArraySizeOut)
+          mPreciseFlagsOut(preciseFlagsOut)
     {}
 
     bool visitGlobalQualifierDeclaration(Visit visit,
@@ -90,27 +85,35 @@
         return false;
     }
 
-    void visitSymbol(TIntermSymbol *symbol) override
+    bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
     {
+        const TIntermSequence &sequence = *(node->getSequence());
+
+        ASSERT(sequence.size() == 1);
+
+        const TIntermSymbol *symbol = sequence.front()->getAsSymbolNode();
+        if (symbol == nullptr)
+        {
+            return true;
+        }
+
         const TType &type = symbol->getType();
         switch (type.getQualifier())
         {
             case EvqClipDistance:
-                *mClipDistanceArraySizeOut = type.getOutermostArraySize();
-                break;
             case EvqCullDistance:
-                *mCullDistanceArraySizeOut = type.getOutermostArraySize();
                 break;
             default:
-                break;
+                return true;
         }
+
+        mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node, TIntermSequence());
+        return true;
     }
 
   private:
     PerVertexMemberFlags *mInvariantFlagsOut;
     PerVertexMemberFlags *mPreciseFlagsOut;
-    uint32_t *mClipDistanceArraySizeOut;
-    uint32_t *mCullDistanceArraySizeOut;
 };
 
 // Traverser that:
@@ -125,8 +128,8 @@
                                     TSymbolTable *symbolTable,
                                     const PerVertexMemberFlags &invariantFlags,
                                     const PerVertexMemberFlags &preciseFlags,
-                                    uint32_t clipDistanceArraySize,
-                                    uint32_t cullDistanceArraySize)
+                                    uint8_t clipDistanceArraySize,
+                                    uint8_t cullDistanceArraySize)
         : TIntermTraverser(true, false, false, symbolTable),
           mShaderType(compiler->getShaderType()),
           mShaderVersion(compiler->getShaderVersion()),
@@ -242,7 +245,7 @@
             return;
         }
 
-        const int fieldIndex = GetPerVertexFieldIndex(type->getQualifier(), variable->name());
+        int fieldIndex = GetPerVertexFieldIndex(type->getQualifier(), variable->name());
 
         // Not the built-in we are looking for.
         if (fieldIndex < 0)
@@ -250,6 +253,12 @@
             return;
         }
 
+        // If gl_ClipDistance is not used, it will be skipped and gl_CullDistance will have index 2.
+        if (fieldIndex == 3 && mClipDistanceArraySize == 0)
+        {
+            fieldIndex = 2;
+        }
+
         // Declare the output gl_PerVertex if not already.
         if (mPerVertexOutVar == nullptr)
         {
@@ -326,13 +335,15 @@
 
         TType *positionType     = new TType(*vec4Type);
         TType *pointSizeType    = new TType(*floatType);
-        TType *clipDistanceType = new TType(*floatType);
-        TType *cullDistanceType = new TType(*floatType);
+        TType *clipDistanceType = mClipDistanceArraySize ? new TType(*floatType) : nullptr;
+        TType *cullDistanceType = mCullDistanceArraySize ? new TType(*floatType) : nullptr;
 
         positionType->setQualifier(EvqPosition);
         pointSizeType->setQualifier(EvqPointSize);
-        clipDistanceType->setQualifier(EvqClipDistance);
-        cullDistanceType->setQualifier(EvqCullDistance);
+        if (clipDistanceType)
+            clipDistanceType->setQualifier(EvqClipDistance);
+        if (cullDistanceType)
+            cullDistanceType->setQualifier(EvqCullDistance);
 
         TPrecision pointSizePrecision = EbpHigh;
         if (mShaderType == GL_VERTEX_SHADER)
@@ -349,30 +360,38 @@
         // TODO: handle interaction with GS and T*S where the two can have different sizes.  These
         // values are valid for EvqPerVertexOut only.  For EvqPerVertexIn, the size should come from
         // the declaration of gl_in.  http://anglebug.com/5466.
-        clipDistanceType->makeArray(std::max(mClipDistanceArraySize, 1u));
-        cullDistanceType->makeArray(std::max(mCullDistanceArraySize, 1u));
+        if (clipDistanceType)
+            clipDistanceType->makeArray(mClipDistanceArraySize);
+        if (cullDistanceType)
+            cullDistanceType->makeArray(mCullDistanceArraySize);
 
         if (qualifier == EvqPerVertexOut)
         {
             positionType->setInvariant(mPerVertexOutInvariantFlags[0]);
             pointSizeType->setInvariant(mPerVertexOutInvariantFlags[1]);
-            clipDistanceType->setInvariant(mPerVertexOutInvariantFlags[2]);
-            cullDistanceType->setInvariant(mPerVertexOutInvariantFlags[3]);
+            if (clipDistanceType)
+                clipDistanceType->setInvariant(mPerVertexOutInvariantFlags[2]);
+            if (cullDistanceType)
+                cullDistanceType->setInvariant(mPerVertexOutInvariantFlags[3]);
 
             positionType->setPrecise(mPerVertexOutPreciseFlags[0]);
             pointSizeType->setPrecise(mPerVertexOutPreciseFlags[1]);
-            clipDistanceType->setPrecise(mPerVertexOutPreciseFlags[2]);
-            cullDistanceType->setPrecise(mPerVertexOutPreciseFlags[3]);
+            if (clipDistanceType)
+                clipDistanceType->setPrecise(mPerVertexOutPreciseFlags[2]);
+            if (cullDistanceType)
+                cullDistanceType->setPrecise(mPerVertexOutPreciseFlags[3]);
         }
 
         fields->push_back(new TField(positionType, ImmutableString("gl_Position"), TSourceLoc(),
                                      SymbolType::AngleInternal));
         fields->push_back(new TField(pointSizeType, ImmutableString("gl_PointSize"), TSourceLoc(),
                                      SymbolType::AngleInternal));
-        fields->push_back(new TField(clipDistanceType, ImmutableString("gl_ClipDistance"),
-                                     TSourceLoc(), SymbolType::AngleInternal));
-        fields->push_back(new TField(cullDistanceType, ImmutableString("gl_CullDistance"),
-                                     TSourceLoc(), SymbolType::AngleInternal));
+        if (clipDistanceType)
+            fields->push_back(new TField(clipDistanceType, ImmutableString("gl_ClipDistance"),
+                                         TSourceLoc(), SymbolType::AngleInternal));
+        if (cullDistanceType)
+            fields->push_back(new TField(cullDistanceType, ImmutableString("gl_CullDistance"),
+                                         TSourceLoc(), SymbolType::AngleInternal));
 
         TInterfaceBlock *interfaceBlock =
             new TInterfaceBlock(mSymbolTable, ImmutableString("gl_PerVertex"), fields,
@@ -433,8 +452,8 @@
     GLenum mShaderType;
     int mShaderVersion;
     const ShBuiltInResources &mResources;
-    uint32_t mClipDistanceArraySize;
-    uint32_t mCullDistanceArraySize;
+    uint8_t mClipDistanceArraySize;
+    uint8_t mCullDistanceArraySize;
 
     const TVariable *mPerVertexInVar;
     const TVariable *mPerVertexOutVar;
@@ -479,30 +498,18 @@
     }
 
     // First, visit all global qualifier declarations and find which built-ins are invariant or
-    // precise.  At the same time, find out the size of gl_ClipDistance and gl_CullDistance arrays.
+    // precise. At the same time, remove gl_ClipDistance and gl_CullDistance array redeclarations.
     PerVertexMemberFlags invariantFlags = {};
     PerVertexMemberFlags preciseFlags   = {};
-    uint32_t clipDistanceArraySize = 0, cullDistanceArraySize = 0;
 
     InspectPerVertexBuiltInsTraverser infoTraverser(compiler, symbolTable, &invariantFlags,
-                                                    &preciseFlags, &clipDistanceArraySize,
-                                                    &cullDistanceArraySize);
+                                                    &preciseFlags);
     root->traverse(&infoTraverser);
     if (!infoTraverser.updateTree(compiler, root))
     {
         return false;
     }
 
-    // If not specified, take the clip/cull distance size from the resources.
-    if (clipDistanceArraySize == 0)
-    {
-        clipDistanceArraySize = compiler->getResources().MaxClipDistances;
-    }
-    if (cullDistanceArraySize == 0)
-    {
-        cullDistanceArraySize = compiler->getResources().MaxCullDistances;
-    }
-
     // If #pragma STDGL invariant(all) is specified, make all outputs invariant.
     if (compiler->getPragma().stdgl.invariantAll)
     {
@@ -511,7 +518,8 @@
 
     // Then declare the in and out gl_PerVertex I/O blocks.
     DeclarePerVertexBlocksTraverser traverser(compiler, symbolTable, invariantFlags, preciseFlags,
-                                              clipDistanceArraySize, cullDistanceArraySize);
+                                              compiler->getClipDistanceArraySize(),
+                                              compiler->getCullDistanceArraySize());
     root->traverse(&traverser);
     if (!traverser.updateTree(compiler, root))
     {
diff --git a/src/compiler/translator/tree_ops/vulkan/DeclarePerVertexBlocks.h b/src/compiler/translator/tree_ops/DeclarePerVertexBlocks.h
similarity index 71%
rename from src/compiler/translator/tree_ops/vulkan/DeclarePerVertexBlocks.h
rename to src/compiler/translator/tree_ops/DeclarePerVertexBlocks.h
index 2ec5e58..4c90deb 100644
--- a/src/compiler/translator/tree_ops/vulkan/DeclarePerVertexBlocks.h
+++ b/src/compiler/translator/tree_ops/DeclarePerVertexBlocks.h
@@ -3,12 +3,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// DeclarePerVertexBlocks: If gl_PerVertex is not already declared, it is declared and builts are
+// DeclarePerVertexBlocks: If gl_PerVertex is not already declared, it is declared and builtins are
 // turned into references into that I/O block.
 //
 
-#ifndef COMPILER_TRANSLATOR_TREEOPS_VULKAN_DECLAREPERVERTEXBLOCKS_H_
-#define COMPILER_TRANSLATOR_TREEOPS_VULKAN_DECLAREPERVERTEXBLOCKS_H_
+#ifndef COMPILER_TRANSLATOR_TREEOPS_DECLAREPERVERTEXBLOCKS_H_
+#define COMPILER_TRANSLATOR_TREEOPS_DECLAREPERVERTEXBLOCKS_H_
 
 #include "common/angleutils.h"
 
@@ -23,4 +23,4 @@
                                           TSymbolTable *symbolTable);
 }  // namespace sh
 
-#endif  // COMPILER_TRANSLATOR_TREEOPS_VULKAN_DECLAREPERVERTEXBLOCKS_H_
+#endif  // COMPILER_TRANSLATOR_TREEOPS_DECLAREPERVERTEXBLOCKS_H_
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp
index d6ad40e..6911247 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.cpp
+++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp
@@ -1941,12 +1941,14 @@
         caps->maxClipDistances = QuerySingleGLInt(functions, GL_MAX_CLIP_DISTANCES_APPLE);
     }
 
-    // GL_EXT_clip_cull_distance
+    // GL_EXT_clip_cull_distance spec requires shader interface blocks to support
+    // built-in array redeclarations on OpenGL ES.
     extensions->clipCullDistanceEXT = !features.disableClipCullDistance.enabled &&
-                                      ((functions->isAtLeastGL(gl::Version(3, 0)) &&
+                                      (functions->isAtLeastGL(gl::Version(4, 5)) ||
+                                       (functions->isAtLeastGL(gl::Version(3, 0)) &&
                                         functions->hasGLExtension("GL_ARB_cull_distance")) ||
-                                       functions->isAtLeastGL(gl::Version(4, 5)) ||
-                                       functions->hasGLESExtension("GL_EXT_clip_cull_distance"));
+                                       (extensions->shaderIoBlocksEXT &&
+                                        functions->hasGLESExtension("GL_EXT_clip_cull_distance")));
     if (extensions->clipCullDistanceEXT)
     {
         caps->maxClipDistances = QuerySingleGLInt(functions, GL_MAX_CLIP_DISTANCES_EXT);