Skip to content

Commit

Permalink
Split VAO dirty bits to speed iteration.
Browse files Browse the repository at this point in the history
Using > 64 bits (we had over 90) would use a much slower dirty bit
iteration. Speed this up by splitting the dirty bits into two levels.
The first top level only has a single dirty bit per attrib, per
binding, and one bit for the element array buffer. The next level has
separate dirty bits for attribs and bindings.

The D3D11 back-end doesn't actually care about individual dirty bits
of attribs or bindings, since it resets entire attributes at a time,
but the GL back-end only refreshes the necessary info.

Improves the score of a simple state change microbenchmark by 15% on
the D3D11 and GL back-ends with a no-op driver. Real-world impact will
be smaller.

Also includes a test suppression for an NVIDIA bug that surfaced when
we changed the order of that GL commands were sent to the driver.

BUG=angleproject:2389

Change-Id: If8d5e5eb0b27e2a77e20535e33626183d372d311
Reviewed-on: https://chromium-review.googlesource.com/556799
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Yuly Novikov <ynovikov@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
  • Loading branch information
null77 authored and Commit Bot committed Mar 27, 2018
1 parent a0ccea1 commit e858cb1
Show file tree
Hide file tree
Showing 12 changed files with 192 additions and 102 deletions.
39 changes: 28 additions & 11 deletions src/libANGLE/VertexArray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,19 @@ size_t VertexArray::GetVertexIndexFromDirtyBit(size_t dirtyBit)
static_assert(gl::MAX_VERTEX_ATTRIBS == gl::MAX_VERTEX_ATTRIB_BINDINGS,
"The stride of vertex attributes should equal to that of vertex bindings.");
ASSERT(dirtyBit > DIRTY_BIT_ELEMENT_ARRAY_BUFFER);
return (dirtyBit - DIRTY_BIT_ATTRIB_0_ENABLED) % gl::MAX_VERTEX_ATTRIBS;
return (dirtyBit - DIRTY_BIT_ATTRIB_0) % gl::MAX_VERTEX_ATTRIBS;
}

void VertexArray::setDirtyAttribBit(size_t attribIndex, DirtyAttribBitType dirtyAttribBit)
{
mDirtyBits.set(DIRTY_BIT_ATTRIB_0 + attribIndex);
mDirtyAttribBits[attribIndex].set(dirtyAttribBit);
}

void VertexArray::setDirtyBindingBit(size_t bindingIndex, DirtyBindingBitType dirtyBindingBit)
{
mDirtyBits.set(DIRTY_BIT_BINDING_0 + bindingIndex);
mDirtyBindingBits[bindingIndex].set(dirtyBindingBit);
}

void VertexArray::bindVertexBufferImpl(const Context *context,
Expand All @@ -137,8 +149,7 @@ void VertexArray::bindVertexBuffer(const Context *context,
GLsizei stride)
{
bindVertexBufferImpl(context, bindingIndex, boundBuffer, offset, stride);

mDirtyBits.set(DIRTY_BIT_BINDING_0_BUFFER + bindingIndex);
setDirtyBindingBit(bindingIndex, DIRTY_BINDING_BUFFER);
}

void VertexArray::setVertexAttribBinding(const Context *context,
Expand All @@ -153,17 +164,17 @@ void VertexArray::setVertexAttribBinding(const Context *context,
ASSERT(context->getClientVersion() >= ES_3_1);
mState.mVertexAttributes[attribIndex].bindingIndex = bindingIndex;

mDirtyBits.set(DIRTY_BIT_ATTRIB_0_BINDING + attribIndex);
setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_BINDING);
}
mState.mVertexAttributes[attribIndex].bindingIndex = static_cast<GLuint>(bindingIndex);
}

void VertexArray::setVertexBindingDivisor(size_t bindingIndex, GLuint divisor)
{
ASSERT(bindingIndex < getMaxBindings());

mState.mVertexBindings[bindingIndex].setDivisor(divisor);

mDirtyBits.set(DIRTY_BIT_BINDING_0_DIVISOR + bindingIndex);
setDirtyBindingBit(bindingIndex, DIRTY_BINDING_DIVISOR);
}

void VertexArray::setVertexAttribFormatImpl(size_t attribIndex,
Expand Down Expand Up @@ -194,8 +205,7 @@ void VertexArray::setVertexAttribFormat(size_t attribIndex,
GLuint relativeOffset)
{
setVertexAttribFormatImpl(attribIndex, size, type, normalized, pureInteger, relativeOffset);

mDirtyBits.set(DIRTY_BIT_ATTRIB_0_FORMAT + attribIndex);
setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_FORMAT);
}

void VertexArray::setVertexAttribDivisor(const Context *context, size_t attribIndex, GLuint divisor)
Expand All @@ -214,7 +224,7 @@ void VertexArray::enableAttribute(size_t attribIndex, bool enabledState)
mState.mVertexAttributesTypeMask.setIndex(
GetVertexAttributeBaseType(mState.mVertexAttributes[attribIndex]), attribIndex);

mDirtyBits.set(DIRTY_BIT_ATTRIB_0_ENABLED + attribIndex);
setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_ENABLED);

// Update state cache
mState.mEnabledAttributesMask.set(attribIndex, enabledState);
Expand Down Expand Up @@ -246,7 +256,7 @@ void VertexArray::setVertexAttribPointer(const Context *context,

bindVertexBufferImpl(context, attribIndex, boundBuffer, offset, effectiveStride);

mDirtyBits.set(DIRTY_BIT_ATTRIB_0_POINTER + attribIndex);
setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_POINTER);
}

void VertexArray::setElementArrayBuffer(const Context *context, Buffer *buffer)
Expand All @@ -264,8 +274,15 @@ void VertexArray::syncState(const Context *context)
{
if (mDirtyBits.any())
{
mVertexArray->syncState(context, mDirtyBits);
mVertexArray->syncState(context, mDirtyBits, mDirtyAttribBits, mDirtyBindingBits);
mDirtyBits.reset();

// This is a bit of an implementation hack - but since we know the implementation
// details of the dirty bit class it should always have the same effect as iterating
// individual attribs. We could also look into schemes where iterating the dirty
// bit set also resets it as you pass through it.
memset(&mDirtyAttribBits, 0, sizeof(mDirtyAttribBits));
memset(&mDirtyBindingBits, 0, sizeof(mDirtyBindingBits));
}
}

Expand Down
63 changes: 41 additions & 22 deletions src/libANGLE/VertexArray.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,40 +153,54 @@ class VertexArray final : public LabeledObject
return mState.getEnabledAttributesMask();
}

// Dirty bits for VertexArrays use a heirarchical design. At the top level, each attribute
// has a single dirty bit. Then an array of MAX_ATTRIBS dirty bits each has a dirty bit for
// enabled/pointer/format/binding. Bindings are handled similarly. Note that because the
// total number of dirty bits is 33, it will not be as fast on a 32-bit machine, which
// can't support the advanced 64-bit scanning intrinsics. We could consider packing the
// binding and attribute bits together if this becomes a problem.
enum DirtyBitType
{
DIRTY_BIT_ELEMENT_ARRAY_BUFFER,

// Reserve bits for enabled flags
DIRTY_BIT_ATTRIB_0_ENABLED,
DIRTY_BIT_ATTRIB_MAX_ENABLED = DIRTY_BIT_ATTRIB_0_ENABLED + gl::MAX_VERTEX_ATTRIBS,
// Dirty bits for attributes.
DIRTY_BIT_ATTRIB_0,
DIRTY_BIT_ATTRIB_MAX = DIRTY_BIT_ATTRIB_0 + gl::MAX_VERTEX_ATTRIBS,

// Reserve bits for attrib pointers
DIRTY_BIT_ATTRIB_0_POINTER = DIRTY_BIT_ATTRIB_MAX_ENABLED,
DIRTY_BIT_ATTRIB_MAX_POINTER = DIRTY_BIT_ATTRIB_0_POINTER + gl::MAX_VERTEX_ATTRIBS,
// Dirty bits for bindings.
DIRTY_BIT_BINDING_0 = DIRTY_BIT_ATTRIB_MAX,
DIRTY_BIT_BINDING_MAX = DIRTY_BIT_BINDING_0 + gl::MAX_VERTEX_ATTRIB_BINDINGS,

// Reserve bits for changes to VertexAttribFormat
DIRTY_BIT_ATTRIB_0_FORMAT = DIRTY_BIT_ATTRIB_MAX_POINTER,
DIRTY_BIT_ATTRIB_MAX_FORMAT = DIRTY_BIT_ATTRIB_0_FORMAT + gl::MAX_VERTEX_ATTRIBS,

// Reserve bits for changes to VertexAttribBinding
DIRTY_BIT_ATTRIB_0_BINDING = DIRTY_BIT_ATTRIB_MAX_FORMAT,
DIRTY_BIT_ATTRIB_MAX_BINDING = DIRTY_BIT_ATTRIB_0_BINDING + gl::MAX_VERTEX_ATTRIBS,
DIRTY_BIT_UNKNOWN = DIRTY_BIT_BINDING_MAX,
DIRTY_BIT_MAX = DIRTY_BIT_UNKNOWN,
};

// Reserve bits for changes to BindVertexBuffer
DIRTY_BIT_BINDING_0_BUFFER = DIRTY_BIT_ATTRIB_MAX_BINDING,
DIRTY_BIT_BINDING_MAX_BUFFER = DIRTY_BIT_BINDING_0_BUFFER + gl::MAX_VERTEX_ATTRIB_BINDINGS,
// We want to keep the number of dirty bits within 64 to keep iteration times fast.
static_assert(DIRTY_BIT_MAX <= 64, "Too many vertex array dirty bits.");

// Reserve bits for binding divisors
DIRTY_BIT_BINDING_0_DIVISOR = DIRTY_BIT_BINDING_MAX_BUFFER,
DIRTY_BIT_BINDING_MAX_DIVISOR =
DIRTY_BIT_BINDING_0_DIVISOR + gl::MAX_VERTEX_ATTRIB_BINDINGS,
enum DirtyAttribBitType
{
DIRTY_ATTRIB_ENABLED,
DIRTY_ATTRIB_POINTER,
DIRTY_ATTRIB_FORMAT,
DIRTY_ATTRIB_BINDING,
DIRTY_ATTRIB_UNKNOWN,
DIRTY_ATTRIB_MAX = DIRTY_ATTRIB_UNKNOWN,
};

DIRTY_BIT_UNKNOWN = DIRTY_BIT_BINDING_MAX_DIVISOR,
DIRTY_BIT_MAX = DIRTY_BIT_UNKNOWN,
enum DirtyBindingBitType
{
DIRTY_BINDING_BUFFER,
DIRTY_BINDING_DIVISOR,
DIRTY_BINDING_UNKNOWN,
DIRTY_BINDING_MAX = DIRTY_BINDING_UNKNOWN,
};

using DirtyBits = angle::BitSet<DIRTY_BIT_MAX>;
using DirtyAttribBits = angle::BitSet<DIRTY_ATTRIB_MAX>;
using DirtyBindingBits = angle::BitSet<DIRTY_BINDING_MAX>;
using DirtyAttribBitsArray = std::array<DirtyAttribBits, gl::MAX_VERTEX_ATTRIBS>;
using DirtyBindingBitsArray = std::array<DirtyBindingBits, gl::MAX_VERTEX_ATTRIB_BINDINGS>;

static size_t GetVertexIndexFromDirtyBit(size_t dirtyBit);

Expand All @@ -201,10 +215,15 @@ class VertexArray final : public LabeledObject
private:
~VertexArray() override;

void setDirtyAttribBit(size_t attribIndex, DirtyAttribBitType dirtyAttribBit);
void setDirtyBindingBit(size_t bindingIndex, DirtyBindingBitType dirtyBindingBit);

GLuint mId;

VertexArrayState mState;
DirtyBits mDirtyBits;
DirtyAttribBitsArray mDirtyAttribBits;
DirtyBindingBitsArray mDirtyBindingBits;

rx::VertexArrayImpl *mVertexArray;
};
Expand Down
26 changes: 5 additions & 21 deletions src/libANGLE/VertexArray_unittest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ using namespace gl;
TEST(VertexArrayTest, VerifyGetIndexFromDirtyBit)
{
VertexArray::DirtyBits dirtyBits;
constexpr size_t bits[] = {1, 4, 9, 16, 25, 36, 49, 64, 81, 92};
constexpr size_t bits[] = {1, 4, 9, 16, 25};
constexpr GLint count = sizeof(bits) / sizeof(size_t);
for (GLint i = 0; i < count; i++)
{
Expand All @@ -29,29 +29,13 @@ TEST(VertexArrayTest, VerifyGetIndexFromDirtyBit)
for (size_t dirtyBit : dirtyBits)
{
const size_t index = VertexArray::GetVertexIndexFromDirtyBit(dirtyBit);
if (dirtyBit < VertexArray::DIRTY_BIT_ATTRIB_MAX_ENABLED)
if (dirtyBit < VertexArray::DIRTY_BIT_ATTRIB_MAX)
{
EXPECT_EQ(dirtyBit - VertexArray::DIRTY_BIT_ATTRIB_0_ENABLED, index);
EXPECT_EQ(dirtyBit - VertexArray::DIRTY_BIT_ATTRIB_0, index);
}
else if (dirtyBit < VertexArray::DIRTY_BIT_ATTRIB_MAX_POINTER)
else if (dirtyBit < VertexArray::DIRTY_BIT_BINDING_MAX)
{
EXPECT_EQ(dirtyBit - VertexArray::DIRTY_BIT_ATTRIB_0_POINTER, index);
}
else if (dirtyBit < VertexArray::DIRTY_BIT_ATTRIB_MAX_FORMAT)
{
EXPECT_EQ(dirtyBit - VertexArray::DIRTY_BIT_ATTRIB_0_FORMAT, index);
}
else if (dirtyBit < VertexArray::DIRTY_BIT_ATTRIB_MAX_BINDING)
{
EXPECT_EQ(dirtyBit - VertexArray::DIRTY_BIT_ATTRIB_0_BINDING, index);
}
else if (dirtyBit < VertexArray::DIRTY_BIT_BINDING_MAX_BUFFER)
{
EXPECT_EQ(dirtyBit - VertexArray::DIRTY_BIT_BINDING_0_BUFFER, index);
}
else if (dirtyBit < VertexArray::DIRTY_BIT_BINDING_MAX_DIVISOR)
{
EXPECT_EQ(dirtyBit - VertexArray::DIRTY_BIT_BINDING_0_DIVISOR, index);
EXPECT_EQ(dirtyBit - VertexArray::DIRTY_BIT_BINDING_0, index);
}
else
ASSERT_TRUE(false);
Expand Down
7 changes: 5 additions & 2 deletions src/libANGLE/renderer/VertexArrayImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ class VertexArrayImpl : angle::NonCopyable
{
public:
VertexArrayImpl(const gl::VertexArrayState &state) : mState(state) {}
virtual void syncState(const gl::Context *context, const gl::VertexArray::DirtyBits &dirtyBits)
virtual void syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits,
const gl::VertexArray::DirtyAttribBitsArray &attribBits,
const gl::VertexArray::DirtyBindingBitsArray &bindingBits)
{
}

Expand All @@ -32,6 +35,6 @@ class VertexArrayImpl : angle::NonCopyable
const gl::VertexArrayState &mState;
};

}
} // namespace rx

#endif // LIBANGLE_RENDERER_VERTEXARRAYIMPL_H_
4 changes: 3 additions & 1 deletion src/libANGLE/renderer/d3d/d3d11/VertexArray11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ void VertexArray11::destroy(const gl::Context *context)
}

void VertexArray11::syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits)
const gl::VertexArray::DirtyBits &dirtyBits,
const gl::VertexArray::DirtyAttribBitsArray &attribBits,
const gl::VertexArray::DirtyBindingBitsArray &bindingBits)
{
ASSERT(dirtyBits.any());

Expand Down
4 changes: 3 additions & 1 deletion src/libANGLE/renderer/d3d/d3d11/VertexArray11.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ class VertexArray11 : public angle::ObserverInterface, public VertexArrayImpl
void destroy(const gl::Context *context) override;

void syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits) override;
const gl::VertexArray::DirtyBits &dirtyBits,
const gl::VertexArray::DirtyAttribBitsArray &attribBits,
const gl::VertexArray::DirtyBindingBitsArray &bindingBits) override;
// This will flush any pending attrib updates and then check the dynamic attribs mask.
bool hasActiveDynamicAttrib(const gl::Context *context);
gl::Error updateDirtyAndDynamicAttribs(const gl::Context *context,
Expand Down
10 changes: 7 additions & 3 deletions src/libANGLE/renderer/d3d/d3d9/VertexArray9.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ class VertexArray9 : public VertexArrayImpl
VertexArray9(const gl::VertexArrayState &data) : VertexArrayImpl(data) {}

void syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits) override;
const gl::VertexArray::DirtyBits &dirtyBits,
const gl::VertexArray::DirtyAttribBitsArray &attribBits,
const gl::VertexArray::DirtyBindingBitsArray &bindingBits) override;

~VertexArray9() override {}

Expand All @@ -35,12 +37,14 @@ class VertexArray9 : public VertexArrayImpl
};

inline void VertexArray9::syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits)
const gl::VertexArray::DirtyBits &dirtyBits,
const gl::VertexArray::DirtyAttribBitsArray &attribBits,
const gl::VertexArray::DirtyBindingBitsArray &bindingBits)
{
ASSERT(dirtyBits.any());
Renderer9 *renderer = GetImplAs<Context9>(context)->getRenderer();
mCurrentStateSerial = renderer->generateSerial();
}
}
} // namespace rx

#endif // LIBANGLE_RENDERER_D3D_D3D9_VERTEXARRAY9_H_
Loading

0 comments on commit e858cb1

Please sign in to comment.