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 ¤tValues = 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;
}