Commit 02c9c04f by Jamie Madill Committed by Commit Bot

Optimize ValidateDrawAttribs: Part 2.

This moves much of the math into cached values in the VertexAttribute and VertexBinding. Bug: angleproject:1391 Change-Id: I1b6c0553bf57fef864c27c5193c7dd7ca9b56f53 Reviewed-on: https://chromium-review.googlesource.com/1008274 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent fe22e23a
...@@ -88,8 +88,8 @@ Error Buffer::bufferData(const Context *context, ...@@ -88,8 +88,8 @@ Error Buffer::bufferData(const Context *context,
mState.mUsage = usage; mState.mUsage = usage;
mState.mSize = size; mState.mSize = size;
// Notify when data changes. // Notify when storage changes.
mImpl->onStateChange(context, angle::SubjectMessage::CONTENTS_CHANGED); mImpl->onStateChange(context, angle::SubjectMessage::STORAGE_CHANGED);
return NoError(); return NoError();
} }
......
...@@ -159,6 +159,7 @@ void VertexArray::bindVertexBufferImpl(const Context *context, ...@@ -159,6 +159,7 @@ void VertexArray::bindVertexBufferImpl(const Context *context,
binding->setStride(stride); binding->setStride(stride);
updateObserverBinding(bindingIndex); updateObserverBinding(bindingIndex);
updateCachedBufferBindingSize(bindingIndex);
} }
void VertexArray::bindVertexBuffer(const Context *context, void VertexArray::bindVertexBuffer(const Context *context,
...@@ -213,6 +214,7 @@ void VertexArray::setVertexAttribFormatImpl(size_t attribIndex, ...@@ -213,6 +214,7 @@ void VertexArray::setVertexAttribFormatImpl(size_t attribIndex,
attrib->pureInteger = pureInteger; attrib->pureInteger = pureInteger;
attrib->relativeOffset = relativeOffset; attrib->relativeOffset = relativeOffset;
mState.mVertexAttributesTypeMask.setIndex(GetVertexAttributeBaseType(*attrib), attribIndex); mState.mVertexAttributesTypeMask.setIndex(GetVertexAttributeBaseType(*attrib), attribIndex);
attrib->updateCachedSizePlusRelativeOffset();
} }
void VertexArray::setVertexAttribFormat(size_t attribIndex, void VertexArray::setVertexAttribFormat(size_t attribIndex,
...@@ -349,6 +351,12 @@ void VertexArray::onSubjectStateChange(const gl::Context *context, ...@@ -349,6 +351,12 @@ void VertexArray::onSubjectStateChange(const gl::Context *context,
ASSERT(!mDirtyBitsGuard.valid() || mDirtyBitsGuard.value().test(dirtyBit)); ASSERT(!mDirtyBitsGuard.valid() || mDirtyBitsGuard.value().test(dirtyBit));
mDirtyBits.set(dirtyBit); mDirtyBits.set(dirtyBit);
context->getGLState().setVertexArrayDirty(this); context->getGLState().setVertexArrayDirty(this);
if (message == angle::SubjectMessage::STORAGE_CHANGED &&
index < mArrayBufferObserverBindings.size())
{
updateCachedBufferBindingSize(index);
}
} }
void VertexArray::updateObserverBinding(size_t bindingIndex) void VertexArray::updateObserverBinding(size_t bindingIndex)
...@@ -358,4 +366,14 @@ void VertexArray::updateObserverBinding(size_t bindingIndex) ...@@ -358,4 +366,14 @@ void VertexArray::updateObserverBinding(size_t bindingIndex)
: nullptr); : nullptr);
} }
void VertexArray::updateCachedVertexAttributeSize(size_t attribIndex)
{
mState.mVertexAttributes[attribIndex].updateCachedSizePlusRelativeOffset();
}
void VertexArray::updateCachedBufferBindingSize(size_t bindingIndex)
{
mState.mVertexBindings[bindingIndex].updateCachedBufferSizeMinusOffset();
}
} // namespace gl } // namespace gl
...@@ -255,6 +255,10 @@ class VertexArray final : public angle::ObserverInterface, public LabeledObject ...@@ -255,6 +255,10 @@ class VertexArray final : public angle::ObserverInterface, public LabeledObject
void updateObserverBinding(size_t bindingIndex); void updateObserverBinding(size_t bindingIndex);
DirtyBitType getDirtyBitFromIndex(bool contentsChanged, angle::SubjectIndex index) const; DirtyBitType getDirtyBitFromIndex(bool contentsChanged, angle::SubjectIndex index) const;
// These are used to optimize draw call validation.
void updateCachedVertexAttributeSize(size_t attribIndex);
void updateCachedBufferBindingSize(size_t bindingIndex);
GLuint mId; GLuint mId;
VertexArrayState mState; VertexArrayState mState;
......
...@@ -13,7 +13,8 @@ namespace gl ...@@ -13,7 +13,8 @@ namespace gl
// [OpenGL ES 3.1] (November 3, 2016) Section 20 Page 361 // [OpenGL ES 3.1] (November 3, 2016) Section 20 Page 361
// Table 20.2: Vertex Array Object State // Table 20.2: Vertex Array Object State
VertexBinding::VertexBinding() : mStride(16u), mDivisor(0), mOffset(0) VertexBinding::VertexBinding()
: mStride(16u), mDivisor(0), mOffset(0), mCachedBufferSizeMinusOffset(0)
{ {
} }
...@@ -34,6 +35,7 @@ VertexBinding &VertexBinding::operator=(VertexBinding &&binding) ...@@ -34,6 +35,7 @@ VertexBinding &VertexBinding::operator=(VertexBinding &&binding)
mDivisor = binding.mDivisor; mDivisor = binding.mDivisor;
mOffset = binding.mOffset; mOffset = binding.mOffset;
std::swap(binding.mBuffer, mBuffer); std::swap(binding.mBuffer, mBuffer);
mCachedBufferSizeMinusOffset = binding.mCachedBufferSizeMinusOffset;
} }
return *this; return *this;
} }
...@@ -53,6 +55,27 @@ void VertexBinding::onContainerBindingChanged(bool bound) ...@@ -53,6 +55,27 @@ void VertexBinding::onContainerBindingChanged(bool bound)
mBuffer->onBindingChanged(bound, BufferBinding::Array); mBuffer->onBindingChanged(bound, BufferBinding::Array);
} }
void VertexBinding::updateCachedBufferSizeMinusOffset()
{
if (mBuffer.get())
{
angle::CheckedNumeric<GLuint64> checkedSize(mBuffer->getSize());
angle::CheckedNumeric<GLuint64> checkedOffset(mOffset);
// Use a default value of zero so checks will fail on overflow.
mCachedBufferSizeMinusOffset = (checkedSize - checkedOffset).ValueOrDefault(0);
}
else
{
mCachedBufferSizeMinusOffset = 0;
}
}
GLuint64 VertexBinding::getCachedBufferSizeMinusOffset() const
{
return mCachedBufferSizeMinusOffset;
}
VertexAttribute::VertexAttribute(GLuint bindingIndex) VertexAttribute::VertexAttribute(GLuint bindingIndex)
: enabled(false), : enabled(false),
type(GL_FLOAT), type(GL_FLOAT),
...@@ -62,7 +85,8 @@ VertexAttribute::VertexAttribute(GLuint bindingIndex) ...@@ -62,7 +85,8 @@ VertexAttribute::VertexAttribute(GLuint bindingIndex)
pointer(nullptr), pointer(nullptr),
relativeOffset(0), relativeOffset(0),
vertexAttribArrayStride(0), vertexAttribArrayStride(0),
bindingIndex(bindingIndex) bindingIndex(bindingIndex),
cachedSizePlusRelativeOffset(0)
{ {
} }
...@@ -75,7 +99,8 @@ VertexAttribute::VertexAttribute(VertexAttribute &&attrib) ...@@ -75,7 +99,8 @@ VertexAttribute::VertexAttribute(VertexAttribute &&attrib)
pointer(attrib.pointer), pointer(attrib.pointer),
relativeOffset(attrib.relativeOffset), relativeOffset(attrib.relativeOffset),
vertexAttribArrayStride(attrib.vertexAttribArrayStride), vertexAttribArrayStride(attrib.vertexAttribArrayStride),
bindingIndex(attrib.bindingIndex) bindingIndex(attrib.bindingIndex),
cachedSizePlusRelativeOffset(attrib.cachedSizePlusRelativeOffset)
{ {
} }
...@@ -92,10 +117,19 @@ VertexAttribute &VertexAttribute::operator=(VertexAttribute &&attrib) ...@@ -92,10 +117,19 @@ VertexAttribute &VertexAttribute::operator=(VertexAttribute &&attrib)
relativeOffset = attrib.relativeOffset; relativeOffset = attrib.relativeOffset;
vertexAttribArrayStride = attrib.vertexAttribArrayStride; vertexAttribArrayStride = attrib.vertexAttribArrayStride;
bindingIndex = attrib.bindingIndex; bindingIndex = attrib.bindingIndex;
cachedSizePlusRelativeOffset = attrib.cachedSizePlusRelativeOffset;
} }
return *this; return *this;
} }
void VertexAttribute::updateCachedSizePlusRelativeOffset()
{
ASSERT(relativeOffset <=
std::numeric_limits<GLuint64>::max() - ComputeVertexAttributeTypeSize(*this));
cachedSizePlusRelativeOffset =
relativeOffset + static_cast<GLuint64>(ComputeVertexAttributeTypeSize(*this));
}
size_t ComputeVertexAttributeTypeSize(const VertexAttribute& attrib) size_t ComputeVertexAttributeTypeSize(const VertexAttribute& attrib)
{ {
GLuint size = attrib.size; GLuint size = attrib.size;
......
...@@ -40,12 +40,19 @@ class VertexBinding final : angle::NonCopyable ...@@ -40,12 +40,19 @@ class VertexBinding final : angle::NonCopyable
void setBuffer(const gl::Context *context, Buffer *bufferIn, bool containerIsBound); void setBuffer(const gl::Context *context, Buffer *bufferIn, bool containerIsBound);
void onContainerBindingChanged(bool bound); void onContainerBindingChanged(bool bound);
// Called from VertexArray.
void updateCachedBufferSizeMinusOffset();
GLuint64 getCachedBufferSizeMinusOffset() const;
private: private:
GLuint mStride; GLuint mStride;
GLuint mDivisor; GLuint mDivisor;
GLintptr mOffset; GLintptr mOffset;
BindingPointer<Buffer> mBuffer; BindingPointer<Buffer> mBuffer;
// This is kept in sync by the VertexArray. It is used to optimize draw call validation.
GLuint64 mCachedBufferSizeMinusOffset;
}; };
// //
...@@ -57,6 +64,9 @@ struct VertexAttribute final : private angle::NonCopyable ...@@ -57,6 +64,9 @@ struct VertexAttribute final : private angle::NonCopyable
VertexAttribute(VertexAttribute &&attrib); VertexAttribute(VertexAttribute &&attrib);
VertexAttribute &operator=(VertexAttribute &&attrib); VertexAttribute &operator=(VertexAttribute &&attrib);
// Called from VertexArray.
void updateCachedSizePlusRelativeOffset();
bool enabled; // For glEnable/DisableVertexAttribArray bool enabled; // For glEnable/DisableVertexAttribArray
GLenum type; GLenum type;
GLuint size; GLuint size;
...@@ -68,6 +78,9 @@ struct VertexAttribute final : private angle::NonCopyable ...@@ -68,6 +78,9 @@ struct VertexAttribute final : private angle::NonCopyable
GLuint vertexAttribArrayStride; // ONLY for queries of VERTEX_ATTRIB_ARRAY_STRIDE GLuint vertexAttribArrayStride; // ONLY for queries of VERTEX_ATTRIB_ARRAY_STRIDE
GLuint bindingIndex; GLuint bindingIndex;
// This is kept in sync by the VertexArray. It is used to optimize draw call validation.
GLuint64 cachedSizePlusRelativeOffset;
}; };
size_t ComputeVertexAttributeTypeSize(const VertexAttribute &attrib); size_t ComputeVertexAttributeTypeSize(const VertexAttribute &attrib);
......
...@@ -650,7 +650,7 @@ void VertexArrayGL::syncDirtyBinding(const gl::Context *context, ...@@ -650,7 +650,7 @@ void VertexArrayGL::syncDirtyBinding(const gl::Context *context,
size_t bindingIndex, size_t bindingIndex,
const gl::VertexArray::DirtyBindingBits &dirtyBindingBits) const gl::VertexArray::DirtyBindingBits &dirtyBindingBits)
{ {
ASSERT(dirtyBindingBits.any()); // Dependent state changes in buffers can trigger updates with no dirty bits set.
for (size_t dirtyBit : dirtyBindingBits) for (size_t dirtyBit : dirtyBindingBits)
{ {
......
...@@ -107,8 +107,8 @@ bool ValidateDrawAttribs(Context *context, GLint primcount, GLint maxVertex, GLi ...@@ -107,8 +107,8 @@ bool ValidateDrawAttribs(Context *context, GLint primcount, GLint maxVertex, GLi
const auto &vertexAttribs = vao->getVertexAttributes(); const auto &vertexAttribs = vao->getVertexAttributes();
const auto &vertexBindings = vao->getVertexBindings(); const auto &vertexBindings = vao->getVertexBindings();
const AttributesMask &activeAttribs = const AttributesMask &activeAttribs = (program->getActiveAttribLocationsMask() &
(program->getActiveAttribLocationsMask() & vao->getEnabledAttributesMask()); vao->getEnabledAttributesMask() & ~clientAttribs);
for (size_t attributeIndex : activeAttribs) for (size_t attributeIndex : activeAttribs)
{ {
...@@ -116,21 +116,11 @@ bool ValidateDrawAttribs(Context *context, GLint primcount, GLint maxVertex, GLi ...@@ -116,21 +116,11 @@ bool ValidateDrawAttribs(Context *context, GLint primcount, GLint maxVertex, GLi
ASSERT(attrib.enabled); ASSERT(attrib.enabled);
const VertexBinding &binding = vertexBindings[attrib.bindingIndex]; const VertexBinding &binding = vertexBindings[attrib.bindingIndex];
gl::Buffer *buffer = binding.getBuffer().get();
if (!buffer)
{
continue;
}
ASSERT(program->isAttribLocationActive(attributeIndex)); ASSERT(program->isAttribLocationActive(attributeIndex));
GLint maxVertexElement = 0; GLint maxVertexElement = maxVertex;
GLuint divisor = binding.getDivisor(); GLuint divisor = binding.getDivisor();
if (divisor == 0) if (divisor != 0)
{
maxVertexElement = maxVertex;
}
else
{ {
maxVertexElement = (primcount - 1) / divisor; maxVertexElement = (primcount - 1) / divisor;
} }
...@@ -146,39 +136,49 @@ bool ValidateDrawAttribs(Context *context, GLint primcount, GLint maxVertex, GLi ...@@ -146,39 +136,49 @@ bool ValidateDrawAttribs(Context *context, GLint primcount, GLint maxVertex, GLi
// We know attribStride is given as a GLsizei which is typedefed to int. // We know attribStride is given as a GLsizei which is typedefed to int.
// We also know an upper bound for attribSize. // We also know an upper bound for attribSize.
static_assert(std::is_same<int, GLsizei>::value, ""); static_assert(std::is_same<int, GLsizei>::value, "Unexpected type");
uint64_t attribStride = ComputeVertexAttributeStride(attrib, binding); ASSERT(ComputeVertexAttributeStride(attrib, binding) == binding.getStride());
uint64_t attribSize = ComputeVertexAttributeTypeSize(attrib); uint64_t attribStride = binding.getStride();
ASSERT(attribStride <= kIntMax && attribSize <= kMaxAttribSize); ASSERT(attribStride <= kIntMax && ComputeVertexAttributeTypeSize(attrib) <= kMaxAttribSize);
// Computing the max offset using uint64_t without attrib.offset is overflow // Computing the product of two 32-bit ints will fit in 64 bits without overflow.
// safe. Note: Last vertex element does not take the full stride! static_assert(kIntMax * kIntMax < kUint64Max, "Unexpected overflow");
static_assert(kIntMax * kIntMax < kUint64Max - kMaxAttribSize, ""); uint64_t attribDataSizeMinusAttribSize = maxVertexElement * attribStride;
uint64_t attribDataSizeNoOffset = maxVertexElement * attribStride + attribSize;
// An overflow can happen when adding the offset, check for it. // An overflow can happen when adding the offset, check for it.
uint64_t attribOffset = ComputeVertexAttributeOffset(attrib, binding); if (attribDataSizeMinusAttribSize > kUint64Max - attrib.cachedSizePlusRelativeOffset)
if (attribDataSizeNoOffset > kUint64Max - attribOffset)
{ {
ANGLE_VALIDATION_ERR(context, InvalidOperation(), IntegerOverflow); ANGLE_VALIDATION_ERR(context, InvalidOperation(), IntegerOverflow);
return false; return false;
} }
uint64_t attribDataSizeWithOffset = attribDataSizeNoOffset + attribOffset;
// [OpenGL ES 3.0.2] section 2.9.4 page 40: // [OpenGL ES 3.0.2] section 2.9.4 page 40:
// We can return INVALID_OPERATION if our vertex attribute does not have // We can return INVALID_OPERATION if our array buffer does not have enough backing data.
// enough backing data. if (attribDataSizeMinusAttribSize + attrib.cachedSizePlusRelativeOffset >
if (attribDataSizeWithOffset > static_cast<uint64_t>(buffer->getSize())) binding.getCachedBufferSizeMinusOffset())
{ {
ANGLE_VALIDATION_ERR(context, InvalidOperation(), InsufficientVertexBufferSize); ANGLE_VALIDATION_ERR(context, InvalidOperation(), InsufficientVertexBufferSize);
return false; return false;
} }
}
if (webglCompatibility && buffer->isBoundForTransformFeedbackAndOtherUse()) // TODO(jmadill): Cache this. http://anglebug.com/1391
if (webglCompatibility)
{
for (size_t attributeIndex : activeAttribs)
{ {
ANGLE_VALIDATION_ERR(context, InvalidOperation(), const VertexAttribute &attrib = vertexAttribs[attributeIndex];
VertexBufferBoundForTransformFeedback); ASSERT(attrib.enabled);
return false;
const VertexBinding &binding = vertexBindings[attrib.bindingIndex];
gl::Buffer *buffer = binding.getBuffer().get();
if (buffer->isBoundForTransformFeedbackAndOtherUse())
{
ANGLE_VALIDATION_ERR(context, InvalidOperation(),
VertexBufferBoundForTransformFeedback);
return false;
}
} }
} }
......
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