Commit c405ae71 by Brandon Jones Committed by Commit Bot

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: 's avatarGeoff Lang <geofflang@chromium.org>
parent 91586494
......@@ -174,6 +174,8 @@ ERRMSG(UniformSizeMismatch, "Uniform size does not match uniform method.");
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,
......
......@@ -915,7 +915,7 @@ GLenum Framebuffer::getDrawbufferWriteType(size_t drawBuffer) const
}
}
DrawBufferTypeMask Framebuffer::getDrawBufferTypeMask() const
ComponentTypeMask Framebuffer::getDrawBufferTypeMask() const
{
return mState.mDrawBufferTypeMask;
}
......
......@@ -115,7 +115,7 @@ class FramebufferState final : angle::NonCopyable
std::vector<GLenum> mDrawBufferStates;
GLenum mReadBufferState;
DrawBufferMask mEnabledDrawBuffers;
DrawBufferTypeMask mDrawBufferTypeMask;
ComponentTypeMask mDrawBufferTypeMask;
GLint mDefaultWidth;
GLint mDefaultHeight;
......@@ -201,7 +201,7 @@ class Framebuffer final : public LabeledObject, public OnAttachmentDirtyReceiver
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;
......
......@@ -232,6 +232,11 @@ LinkResult MemoryProgramCache::Deserialize(const Context *context,
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 @@ LinkResult MemoryProgramCache::Deserialize(const Context *context,
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 @@ void MemoryProgramCache::Serialize(const Context *context,
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 @@ void MemoryProgramCache::Serialize(const Context *context,
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());
......
......@@ -991,6 +991,8 @@ void Program::updateLinkedShaderStages()
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 @@ bool Program::linkAttributes(const Context *context, InfoLog &infoLog)
}
}
ASSERT(mState.mAttributesTypeMask.none());
ASSERT(mState.mAttributesMask.none());
for (const sh::Attribute &attribute : mState.mAttributes)
{
ASSERT(attribute.location != -1);
......@@ -2376,6 +2381,14 @@ bool Program::linkAttributes(const Context *context, InfoLog &infoLog)
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);
}
}
}
......
......@@ -345,6 +345,9 @@ class ProgramState final : angle::NonCopyable
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 @@ class ProgramState final : angle::NonCopyable
// 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 @@ class Program final : angle::NonCopyable, public LabeledObject
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;
......
......@@ -142,6 +142,12 @@ void State::initialize(const Context *context,
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 @@ void State::setVertexAttribf(GLuint index, const GLfloat values[4])
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 @@ void State::setVertexAttribu(GLuint index, const GLuint values[4])
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 @@ void State::setVertexAttribi(GLuint index, const GLint values[4])
mVertexAttribCurrentValues[index].setIntValues(values);
mDirtyBits.set(DIRTY_BIT_CURRENT_VALUES);
mDirtyCurrentValues.set(index);
mCurrentValuesTypeMask.setIndex(GL_INT, index);
}
void State::setVertexAttribPointer(const Context *context,
......
......@@ -460,6 +460,7 @@ class State : public OnAttachmentDirtyReceiver, angle::NonCopyable
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 @@ class State : public OnAttachmentDirtyReceiver, angle::NonCopyable
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
......
......@@ -175,6 +175,8 @@ void VertexArray::setVertexAttribFormatImpl(size_t attribIndex,
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 @@ void VertexArray::enableAttribute(size_t attribIndex, bool enabledState)
ASSERT(attribIndex < getMaxAttribs());
mState.mVertexAttributes[attribIndex].enabled = enabledState;
mState.mVertexAttributesTypeMask.setIndex(
GetVertexAttributeBaseType(mState.mVertexAttributes[attribIndex]), attribIndex);
mDirtyBits.set(DIRTY_BIT_ATTRIB_0_ENABLED + attribIndex);
......
......@@ -69,6 +69,7 @@ class VertexArrayState final : angle::NonCopyable
BindingPointer<Buffer> mElementArrayBuffer;
std::vector<VertexBinding> mVertexBindings;
AttributesMask mEnabledAttributesMask;
ComponentTypeMask mVertexAttributesTypeMask;
};
class VertexArray final : public LabeledObject
......@@ -192,6 +193,9 @@ class VertexArray final : public LabeledObject
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;
......
......@@ -257,50 +257,45 @@ bool operator!=(const Extents &lhs, const Extents &rhs)
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 @@ void DrawBufferTypeMask::setIndex(GLenum type, size_t index)
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");
// 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();
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);
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 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.
// 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
......@@ -290,24 +290,26 @@ using UniformBlockBindingMask = angle::BitSet<IMPLEMENTATION_MAX_COMBINED_SHADER
// 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;
......
......@@ -396,9 +396,10 @@ bool ValidateFragmentShaderColorBufferTypeMatch(ValidationContext *context)
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 @@ bool ValidateVertexShaderAttributeTypeMatch(ValidationContext *context)
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())
{
// gl_VertexID and gl_InstanceID are active attributes but don't have a bound attribute.
if (shaderAttribute.isBuiltIn())
{
continue;
}
GLenum shaderInputType = VariableComponentType(shaderAttribute.type);
unsigned long stateCurrentValuesTypeBits = glState.getCurrentValuesTypeMask().to_ulong();
unsigned long vaoAttribTypeBits = vao->getAttributesTypeMask().to_ulong();
unsigned long vaoAttribEnabledMask = vao->getAttributesMask().to_ulong();
const auto &attrib = vertexAttribs[shaderAttribute.location];
GLenum vertexType = attrib.enabled ? GetVertexAttributeBaseType(attrib)
: currentValues[shaderAttribute.location].Type;
vaoAttribEnabledMask |= vaoAttribEnabledMask << MAX_COMPONENT_TYPE_MASK_INDEX;
vaoAttribTypeBits = (vaoAttribEnabledMask & vaoAttribTypeBits);
vaoAttribTypeBits |= (~vaoAttribEnabledMask & stateCurrentValuesTypeBits);
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;
}
if (!ComponentTypeMask::Validate(program->getAttributesTypeMask().to_ulong(), vaoAttribTypeBits,
program->getAttributesMask().to_ulong(), 0xFFFF))
{
ANGLE_VALIDATION_ERR(context, InvalidOperation(), VertexShaderTypeMismatch);
return false;
}
return true;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment