Optimize Vertex Shader Attribute Type Validition

Improves ValidateVertexShaderAttributeTypeMatch by storing vertex
attributes types into masks for quick comparisons when needed. This shows
2% improvement to glDrawElements for the aquarium workload.

BUG=angleproject:2202

Change-Id: I87fa3d30c3d8cdba6dfd936cd1a41fd27b1c6b77
Reviewed-on: https://chromium-review.googlesource.com/814795
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/ErrorStrings.h b/src/libANGLE/ErrorStrings.h
index 78023e5..a072171 100644
--- a/src/libANGLE/ErrorStrings.h
+++ b/src/libANGLE/ErrorStrings.h
@@ -174,6 +174,8 @@
 ERRMSG(UnknownParameter, "Unknown parameter value.");
 ERRMSG(VertexArrayNoBuffer, "An enabled vertex array has no buffer.");
 ERRMSG(VertexArrayNoBufferPointer, "An enabled vertex array has no buffer and no pointer.");
+ERRMSG(VertexShaderTypeMismatch,
+       "Vertex shader input type does not match the type of the bound vertex attribute.")
 ERRMSG(ViewportNegativeSize, "Viewport size cannot be negative.");
 ERRMSG(Webgl2NameLengthLimitExceeded, "Location lengths must not be greater than 1024 characters.");
 ERRMSG(WebglBindAttribLocationReservedPrefix,
diff --git a/src/libANGLE/Framebuffer.cpp b/src/libANGLE/Framebuffer.cpp
index f4320f6..0ae41e6 100644
--- a/src/libANGLE/Framebuffer.cpp
+++ b/src/libANGLE/Framebuffer.cpp
@@ -915,7 +915,7 @@
     }
 }
 
-DrawBufferTypeMask Framebuffer::getDrawBufferTypeMask() const
+ComponentTypeMask Framebuffer::getDrawBufferTypeMask() const
 {
     return mState.mDrawBufferTypeMask;
 }
diff --git a/src/libANGLE/Framebuffer.h b/src/libANGLE/Framebuffer.h
index 81dcfde..0843871 100644
--- a/src/libANGLE/Framebuffer.h
+++ b/src/libANGLE/Framebuffer.h
@@ -115,7 +115,7 @@
     std::vector<GLenum> mDrawBufferStates;
     GLenum mReadBufferState;
     DrawBufferMask mEnabledDrawBuffers;
-    DrawBufferTypeMask mDrawBufferTypeMask;
+    ComponentTypeMask mDrawBufferTypeMask;
 
     GLint mDefaultWidth;
     GLint mDefaultHeight;
@@ -201,7 +201,7 @@
     void setDrawBuffers(size_t count, const GLenum *buffers);
     const FramebufferAttachment *getDrawBuffer(size_t drawBuffer) const;
     GLenum getDrawbufferWriteType(size_t drawBuffer) const;
-    DrawBufferTypeMask getDrawBufferTypeMask() const;
+    ComponentTypeMask getDrawBufferTypeMask() const;
     DrawBufferMask getDrawBufferMask() const;
     bool hasEnabledDrawBuffer() const;
 
diff --git a/src/libANGLE/MemoryProgramCache.cpp b/src/libANGLE/MemoryProgramCache.cpp
index 5aa5480..4f01c62 100644
--- a/src/libANGLE/MemoryProgramCache.cpp
+++ b/src/libANGLE/MemoryProgramCache.cpp
@@ -232,6 +232,11 @@
 
     state->mNumViews = stream.readInt<int>();
 
+    static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
+                  "All bits of mAttributesTypeMask types and mask fit into 32 bits each");
+    state->mAttributesTypeMask.from_ulong(stream.readInt<uint32_t>());
+    state->mAttributesMask = stream.readInt<uint32_t>();
+
     static_assert(MAX_VERTEX_ATTRIBS <= sizeof(unsigned long) * 8,
                   "Too many vertex attribs for mask");
     state->mActiveAttribLocationsMask = stream.readInt<unsigned long>();
@@ -372,12 +377,10 @@
         state->mOutputVariableTypes.push_back(stream.readInt<GLenum>());
     }
 
-    static_assert(IMPLEMENTATION_MAX_DRAW_BUFFER_TYPE_MASK == 8 * sizeof(uint16_t),
-                  "All bits of DrawBufferTypeMask can be contained in an uint16_t");
-    state->mDrawBufferTypeMask.from_ulong(stream.readInt<uint16_t>());
-
-    static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
-                  "All bits of DrawBufferMask can be contained in an uint32_t");
+    static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t),
+                  "All bits of mDrawBufferTypeMask and mActiveOutputVariables types and mask fit "
+                  "into 32 bits each");
+    state->mDrawBufferTypeMask.from_ulong(stream.readInt<uint32_t>());
     state->mActiveOutputVariables = stream.readInt<uint32_t>();
 
     unsigned int samplerRangeLow  = stream.readInt<unsigned int>();
@@ -450,6 +453,11 @@
 
     stream.writeInt(state.mNumViews);
 
+    static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
+                  "All bits of mAttributesTypeMask types and mask fit into 32 bits each");
+    stream.writeInt(static_cast<int>(state.mAttributesTypeMask.to_ulong()));
+    stream.writeInt(static_cast<int>(state.mAttributesMask.to_ulong()));
+
     stream.writeInt(state.getActiveAttribLocationsMask().to_ulong());
 
     stream.writeInt(state.getAttributes().size());
@@ -546,13 +554,11 @@
         stream.writeInt(outputVariableType);
     }
 
-    static_assert(IMPLEMENTATION_MAX_DRAW_BUFFER_TYPE_MASK == 8 * sizeof(uint16_t),
-                  "All bits of DrawBufferTypeMask can be contained in an uint16_t");
-    stream.writeInt(static_cast<uint32_t>(state.mDrawBufferTypeMask.to_ulong()));
-
-    static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
-                  "All bits of DrawBufferMask can be contained in an uint32_t");
-    stream.writeInt(static_cast<uint32_t>(state.mActiveOutputVariables.to_ulong()));
+    static_assert(
+        IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t),
+        "All bits of mDrawBufferTypeMask and mActiveOutputVariables can be contained in 32 bits");
+    stream.writeInt(static_cast<int>(state.mDrawBufferTypeMask.to_ulong()));
+    stream.writeInt(static_cast<int>(state.mActiveOutputVariables.to_ulong()));
 
     stream.writeInt(state.getSamplerUniformRange().low());
     stream.writeInt(state.getSamplerUniformRange().high());
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 7ccd470..0d223b3 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -991,6 +991,8 @@
 void Program::unlink()
 {
     mState.mAttributes.clear();
+    mState.mAttributesTypeMask.reset();
+    mState.mAttributesMask.reset();
     mState.mActiveAttribLocationsMask.reset();
     mState.mMaxActiveAttribLocation = 0;
     mState.mLinkedTransformFeedbackVaryings.clear();
@@ -2365,6 +2367,9 @@
         }
     }
 
+    ASSERT(mState.mAttributesTypeMask.none());
+    ASSERT(mState.mAttributesMask.none());
+
     for (const sh::Attribute &attribute : mState.mAttributes)
     {
         ASSERT(attribute.location != -1);
@@ -2376,6 +2381,14 @@
             mState.mActiveAttribLocationsMask.set(location);
             mState.mMaxActiveAttribLocation =
                 std::max(mState.mMaxActiveAttribLocation, location + 1);
+
+            // gl_VertexID and gl_InstanceID are active attributes but don't have a bound attribute.
+            if (!attribute.isBuiltIn())
+            {
+                mState.mAttributesTypeMask.setIndex(VariableComponentType(attribute.type),
+                                                    location);
+                mState.mAttributesMask.set(location);
+            }
         }
     }
 
diff --git a/src/libANGLE/Program.h b/src/libANGLE/Program.h
index a2e53cf..5e10b57 100644
--- a/src/libANGLE/Program.h
+++ b/src/libANGLE/Program.h
@@ -345,6 +345,9 @@
     std::vector<sh::Attribute> mAttributes;
     angle::BitSet<MAX_VERTEX_ATTRIBS> mActiveAttribLocationsMask;
     unsigned int mMaxActiveAttribLocation;
+    ComponentTypeMask mAttributesTypeMask;
+    // mAttributesMask is identical to mActiveAttribLocationsMask with built-in attributes removed.
+    AttributesMask mAttributesMask;
 
     // Uniforms are sorted in order:
     //  1. Non-opaque uniforms
@@ -382,7 +385,7 @@
 
     // Fragment output variable base types: FLOAT, INT, or UINT.  Ordered by location.
     std::vector<GLenum> mOutputVariableTypes;
-    DrawBufferTypeMask mDrawBufferTypeMask;
+    ComponentTypeMask mDrawBufferTypeMask;
 
     bool mBinaryRetrieveableHint;
     bool mSeparable;
@@ -643,7 +646,9 @@
     int getNumViews() const { return mState.getNumViews(); }
     bool usesMultiview() const { return mState.usesMultiview(); }
 
-    DrawBufferTypeMask getDrawBufferTypeMask() const { return mState.mDrawBufferTypeMask; }
+    ComponentTypeMask getDrawBufferTypeMask() const { return mState.mDrawBufferTypeMask; }
+    ComponentTypeMask getAttributesTypeMask() const { return mState.mAttributesTypeMask; }
+    AttributesMask getAttributesMask() const { return mState.mAttributesMask; }
 
   private:
     ~Program() override;
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index 93953c7..d4085c4 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -142,6 +142,12 @@
 
     mVertexAttribCurrentValues.resize(caps.maxVertexAttributes);
 
+    // Set all indexes in state attributes type mask to float (default)
+    for (int i = 0; i < MAX_VERTEX_ATTRIBS; i++)
+    {
+        mCurrentValuesTypeMask.setIndex(GL_FLOAT, i);
+    }
+
     mUniformBuffers.resize(caps.maxUniformBufferBindings);
 
     mSamplerTextures[GL_TEXTURE_2D].resize(caps.maxCombinedTextureImageUnits);
@@ -1371,6 +1377,7 @@
     mVertexAttribCurrentValues[index].setFloatValues(values);
     mDirtyBits.set(DIRTY_BIT_CURRENT_VALUES);
     mDirtyCurrentValues.set(index);
+    mCurrentValuesTypeMask.setIndex(GL_FLOAT, index);
 }
 
 void State::setVertexAttribu(GLuint index, const GLuint values[4])
@@ -1379,6 +1386,7 @@
     mVertexAttribCurrentValues[index].setUnsignedIntValues(values);
     mDirtyBits.set(DIRTY_BIT_CURRENT_VALUES);
     mDirtyCurrentValues.set(index);
+    mCurrentValuesTypeMask.setIndex(GL_UNSIGNED_INT, index);
 }
 
 void State::setVertexAttribi(GLuint index, const GLint values[4])
@@ -1387,6 +1395,7 @@
     mVertexAttribCurrentValues[index].setIntValues(values);
     mDirtyBits.set(DIRTY_BIT_CURRENT_VALUES);
     mDirtyCurrentValues.set(index);
+    mCurrentValuesTypeMask.setIndex(GL_INT, index);
 }
 
 void State::setVertexAttribPointer(const Context *context,
diff --git a/src/libANGLE/State.h b/src/libANGLE/State.h
index d2bc701..d160c03 100644
--- a/src/libANGLE/State.h
+++ b/src/libANGLE/State.h
@@ -460,6 +460,7 @@
 
     const ImageUnit &getImageUnit(GLuint unit) const;
     const std::vector<Texture *> &getCompleteTextureCache() const { return mCompleteTextureCache; }
+    ComponentTypeMask getCurrentValuesTypeMask() const { return mCurrentValuesTypeMask; }
 
     // Handle a dirty texture event.
     void signal(size_t textureIndex, InitState initState) override;
@@ -515,6 +516,7 @@
     typedef std::vector<VertexAttribCurrentValueData> VertexAttribVector;
     VertexAttribVector mVertexAttribCurrentValues;  // From glVertexAttrib
     VertexArray *mVertexArray;
+    ComponentTypeMask mCurrentValuesTypeMask;
 
     // Texture and sampler bindings
     size_t mActiveSampler;  // Active texture unit selector - GL_TEXTURE0
diff --git a/src/libANGLE/VertexArray.cpp b/src/libANGLE/VertexArray.cpp
index 7151b59..55fb1b2 100644
--- a/src/libANGLE/VertexArray.cpp
+++ b/src/libANGLE/VertexArray.cpp
@@ -175,6 +175,8 @@
     attrib->normalized     = normalized;
     attrib->pureInteger    = pureInteger;
     attrib->relativeOffset = relativeOffset;
+    mState.mVertexAttributesTypeMask.setIndex(GetVertexAttributeBaseType(*attrib), attribIndex);
+    mState.mEnabledAttributesMask.set(attribIndex);
 }
 
 void VertexArray::setVertexAttribFormat(size_t attribIndex,
@@ -202,6 +204,8 @@
     ASSERT(attribIndex < getMaxAttribs());
 
     mState.mVertexAttributes[attribIndex].enabled = enabledState;
+    mState.mVertexAttributesTypeMask.setIndex(
+        GetVertexAttributeBaseType(mState.mVertexAttributes[attribIndex]), attribIndex);
 
     mDirtyBits.set(DIRTY_BIT_ATTRIB_0_ENABLED + attribIndex);
 
diff --git a/src/libANGLE/VertexArray.h b/src/libANGLE/VertexArray.h
index 3764afd..99dabf7 100644
--- a/src/libANGLE/VertexArray.h
+++ b/src/libANGLE/VertexArray.h
@@ -69,6 +69,7 @@
     BindingPointer<Buffer> mElementArrayBuffer;
     std::vector<VertexBinding> mVertexBindings;
     AttributesMask mEnabledAttributesMask;
+    ComponentTypeMask mVertexAttributesTypeMask;
 };
 
 class VertexArray final : public LabeledObject
@@ -192,6 +193,9 @@
     void syncState(const Context *context);
     bool hasAnyDirtyBit() const { return mDirtyBits.any(); }
 
+    ComponentTypeMask getAttributesTypeMask() const { return mState.mVertexAttributesTypeMask; }
+    AttributesMask getAttributesMask() const { return mState.mEnabledAttributesMask; }
+
   private:
     ~VertexArray() override;
 
diff --git a/src/libANGLE/angletypes.cpp b/src/libANGLE/angletypes.cpp
index 467c91c..b6f61c2 100644
--- a/src/libANGLE/angletypes.cpp
+++ b/src/libANGLE/angletypes.cpp
@@ -257,50 +257,45 @@
     return !(lhs == rhs);
 }
 
-DrawBufferTypeMask::DrawBufferTypeMask()
+ComponentTypeMask::ComponentTypeMask()
 {
     mTypeMask.reset();
 }
 
-DrawBufferTypeMask::DrawBufferTypeMask(const DrawBufferTypeMask &other) = default;
+ComponentTypeMask::ComponentTypeMask(const ComponentTypeMask &other) = default;
 
-DrawBufferTypeMask::~DrawBufferTypeMask() = default;
+ComponentTypeMask::~ComponentTypeMask() = default;
 
-void DrawBufferTypeMask::reset()
+void ComponentTypeMask::reset()
 {
     mTypeMask.reset();
 }
 
-bool DrawBufferTypeMask::none()
+bool ComponentTypeMask::none()
 {
-    if (mTypeMask.none())
-    {
-        return true;
-    }
-
-    return false;
+    return mTypeMask.none();
 }
 
-void DrawBufferTypeMask::setIndex(GLenum type, size_t index)
+void ComponentTypeMask::setIndex(GLenum type, size_t index)
 {
-    ASSERT(index <= IMPLEMENTATION_MAX_DRAW_BUFFERS);
+    ASSERT(index <= MAX_COMPONENT_TYPE_MASK_INDEX);
 
-    mTypeMask &= ~(0x101 << index);
+    mTypeMask &= ~(0x10001 << index);
 
-    uint16_t m = 0;
+    uint32_t m = 0;
     switch (type)
     {
         case GL_INT:
-            m = 0x001;
+            m = 0x00001;
             break;
         case GL_UNSIGNED_INT:
-            m = 0x100;
+            m = 0x10000;
             break;
         case GL_FLOAT:
-            m = 0x101;
+            m = 0x10001;
             break;
         case GL_NONE:
-            m = 0x000;
+            m = 0x00000;
             break;
         default:
             UNREACHABLE();
@@ -309,45 +304,42 @@
     mTypeMask |= m << index;
 }
 
-unsigned long DrawBufferTypeMask::to_ulong() const
+unsigned long ComponentTypeMask::to_ulong() const
 {
     return mTypeMask.to_ulong();
 }
 
-void DrawBufferTypeMask::from_ulong(unsigned long mask)
+void ComponentTypeMask::from_ulong(unsigned long mask)
 {
     mTypeMask = mask;
 }
 
-bool DrawBufferTypeMask::ProgramOutputsMatchFramebuffer(DrawBufferTypeMask outputTypes,
-                                                        DrawBufferTypeMask inputTypes,
-                                                        DrawBufferMask outputMask,
-                                                        DrawBufferMask inputMask)
+bool ComponentTypeMask::Validate(unsigned long outputTypes,
+                                 unsigned long inputTypes,
+                                 unsigned long outputMask,
+                                 unsigned long inputMask)
 {
-    static_assert(IMPLEMENTATION_MAX_DRAW_BUFFER_TYPE_MASK == 16,
-                  "Draw buffer type masks should fit into 16 bits. 2 bits per draw buffer.");
-    static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS == 8,
-                  "Output/Input masks should fit into 8 bits. 1 bit per draw buffer");
+    static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS <= MAX_COMPONENT_TYPE_MASK_INDEX,
+                  "Output/input masks should fit into 16 bits - 1 bit per draw buffer. The "
+                  "corresponding type masks should fit into 32 bits - 2 bits per draw buffer.");
+    static_assert(MAX_VERTEX_ATTRIBS <= MAX_COMPONENT_TYPE_MASK_INDEX,
+                  "Output/input masks should fit into 16 bits - 1 bit per attrib. The "
+                  "corresponding type masks should fit into 32 bits - 2 bits per attrib.");
 
-    // For performance reasons, draw buffer type validation is done using bit masks. We store two
-    // bits representing the type split, with the low bit in the lower 8 bits of the variable,
-    // and the high bit in the upper 8 bits of the variable. This is done so we can AND with the
-    // elswewhere used DrawBufferMask.
-    const unsigned long outputTypeBits = outputTypes.to_ulong();
-    const unsigned long inputTypeBits  = inputTypes.to_ulong();
+    // For performance reasons, draw buffer and attribute type validation is done using bit masks.
+    // We store two bits representing the type split, with the low bit in the lower 16 bits of the
+    // variable, and the high bit in the upper 16 bits of the variable. This is done so we can AND
+    // with the elswewhere used DrawBufferMask or AttributeMask.
 
-    unsigned long outputMaskBits = outputMask.to_ulong();
-    unsigned long inputMaskBits  = inputMask.to_ulong();
-
-    // OR the masks with themselves, shifted 8 bits. This is to match our split type bits.
-    outputMaskBits |= (outputMaskBits << 8);
-    inputMaskBits |= (inputMaskBits << 8);
+    // OR the masks with themselves, shifted 16 bits. This is to match our split type bits.
+    outputMask |= (outputMask << MAX_COMPONENT_TYPE_MASK_INDEX);
+    inputMask |= (inputMask << MAX_COMPONENT_TYPE_MASK_INDEX);
 
     // To validate:
-    // 1. Remove any indexes that are not enabled in the framebuffer (& inputMask)
-    // 2. Remove any indexes that exist in program, but not in framebuffer (& outputMask)
-    // 3. Use XOR to check for a match
-    return (outputTypeBits & inputMaskBits) == ((inputTypeBits & outputMaskBits) & inputMaskBits);
+    // 1. Remove any indexes that are not enabled in the input (& inputMask)
+    // 2. Remove any indexes that exist in output, but not in input (& outputMask)
+    // 3. Use == to verify equality
+    return (outputTypes & inputMask) == ((inputTypes & outputMask) & inputMask);
 }
 
 }  // namespace gl
diff --git a/src/libANGLE/angletypes.h b/src/libANGLE/angletypes.h
index 6d84868..035c8fa 100644
--- a/src/libANGLE/angletypes.h
+++ b/src/libANGLE/angletypes.h
@@ -290,24 +290,26 @@
 
 // Used in Framebuffer / Program
 using DrawBufferMask = angle::BitSet<IMPLEMENTATION_MAX_DRAW_BUFFERS>;
-constexpr int IMPLEMENTATION_MAX_DRAW_BUFFER_TYPE_MASK = 16;
-struct DrawBufferTypeMask final
+
+constexpr size_t MAX_COMPONENT_TYPE_MASK_INDEX = 16;
+struct ComponentTypeMask final
 {
-    DrawBufferTypeMask();
-    DrawBufferTypeMask(const DrawBufferTypeMask &other);
-    ~DrawBufferTypeMask();
+    ComponentTypeMask();
+    ComponentTypeMask(const ComponentTypeMask &other);
+    ~ComponentTypeMask();
     void reset();
     bool none();
     void setIndex(GLenum type, size_t index);
     unsigned long to_ulong() const;
     void from_ulong(unsigned long mask);
-    static bool ProgramOutputsMatchFramebuffer(DrawBufferTypeMask outputTypes,
-                                               DrawBufferTypeMask inputTypes,
-                                               DrawBufferMask outputMask,
-                                               DrawBufferMask inputMask);
+    static bool Validate(unsigned long outputTypes,
+                         unsigned long inputTypes,
+                         unsigned long outputMask,
+                         unsigned long inputMask);
 
   private:
-    angle::BitSet<IMPLEMENTATION_MAX_DRAW_BUFFER_TYPE_MASK> mTypeMask;
+    // Each index type is represented by 2 bits
+    angle::BitSet<MAX_COMPONENT_TYPE_MASK_INDEX * 2> mTypeMask;
 };
 
 using ContextID = uintptr_t;
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index 16ccd0a..ba7e73f 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -396,9 +396,10 @@
     const Program *program         = context->getGLState().getProgram();
     const Framebuffer *framebuffer = context->getGLState().getDrawFramebuffer();
 
-    if (!DrawBufferTypeMask::ProgramOutputsMatchFramebuffer(
-            program->getDrawBufferTypeMask(), framebuffer->getDrawBufferTypeMask(),
-            program->getActiveOutputVariables(), framebuffer->getDrawBufferMask()))
+    if (!ComponentTypeMask::Validate(program->getDrawBufferTypeMask().to_ulong(),
+                                     framebuffer->getDrawBufferTypeMask().to_ulong(),
+                                     program->getActiveOutputVariables().to_ulong(),
+                                     framebuffer->getDrawBufferMask().to_ulong()))
     {
         ANGLE_VALIDATION_ERR(context, InvalidOperation(), DrawBufferTypeMismatch);
         return false;
@@ -412,32 +413,21 @@
     const auto &glState       = context->getGLState();
     const Program *program = context->getGLState().getProgram();
     const VertexArray *vao = context->getGLState().getVertexArray();
-    const auto &vertexAttribs = vao->getVertexAttributes();
-    const auto &currentValues = glState.getVertexAttribCurrentValues();
 
-    for (const sh::Attribute &shaderAttribute : program->getAttributes())
+    unsigned long stateCurrentValuesTypeBits = glState.getCurrentValuesTypeMask().to_ulong();
+    unsigned long vaoAttribTypeBits          = vao->getAttributesTypeMask().to_ulong();
+    unsigned long vaoAttribEnabledMask       = vao->getAttributesMask().to_ulong();
+
+    vaoAttribEnabledMask |= vaoAttribEnabledMask << MAX_COMPONENT_TYPE_MASK_INDEX;
+    vaoAttribTypeBits = (vaoAttribEnabledMask & vaoAttribTypeBits);
+    vaoAttribTypeBits |= (~vaoAttribEnabledMask & stateCurrentValuesTypeBits);
+
+    if (!ComponentTypeMask::Validate(program->getAttributesTypeMask().to_ulong(), vaoAttribTypeBits,
+                                     program->getAttributesMask().to_ulong(), 0xFFFF))
     {
-        // gl_VertexID and gl_InstanceID are active attributes but don't have a bound attribute.
-        if (shaderAttribute.isBuiltIn())
-        {
-            continue;
-        }
-
-        GLenum shaderInputType = VariableComponentType(shaderAttribute.type);
-
-        const auto &attrib = vertexAttribs[shaderAttribute.location];
-        GLenum vertexType  = attrib.enabled ? GetVertexAttributeBaseType(attrib)
-                                           : currentValues[shaderAttribute.location].Type;
-
-        if (shaderInputType != GL_NONE && vertexType != GL_NONE && shaderInputType != vertexType)
-        {
-            context->handleError(InvalidOperation() << "Vertex shader input type does not "
-                                                       "match the type of the bound vertex "
-                                                       "attribute.");
-            return false;
-        }
+        ANGLE_VALIDATION_ERR(context, InvalidOperation(), VertexShaderTypeMismatch);
+        return false;
     }
-
     return true;
 }