Commit e858cb1d by Jamie Madill Committed by Commit Bot

Split VAO dirty bits to speed iteration.

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/556799Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarYuly Novikov <ynovikov@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent a0ccea1e
......@@ -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,
......@@ -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,
......@@ -153,8 +164,9 @@ 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)
......@@ -162,8 +174,7 @@ 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,
......@@ -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)
......@@ -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);
......@@ -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)
......@@ -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));
}
}
......
......@@ -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);
......@@ -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;
};
......
......@@ -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++)
{
......@@ -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);
......
......@@ -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)
{
}
......@@ -32,6 +35,6 @@ class VertexArrayImpl : angle::NonCopyable
const gl::VertexArrayState &mState;
};
}
} // namespace rx
#endif // LIBANGLE_RENDERER_VERTEXARRAYIMPL_H_
......@@ -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());
......
......@@ -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,
......
......@@ -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 {}
......@@ -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_
......@@ -640,56 +640,99 @@ void VertexArrayGL::updateBindingDivisor(size_t bindingIndex)
mAppliedBindings[bindingIndex].setDivisor(adjustedDivisor);
}
void VertexArrayGL::syncState(const gl::Context *context, const VertexArray::DirtyBits &dirtyBits)
void VertexArrayGL::syncDirtyAttrib(const gl::Context *context,
size_t attribIndex,
const gl::VertexArray::DirtyAttribBits &dirtyAttribBits)
{
mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
ASSERT(dirtyAttribBits.any());
for (size_t dirtyBit : dirtyBits)
for (size_t dirtyBit : dirtyAttribBits)
{
if (dirtyBit == VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER)
switch (dirtyBit)
{
updateElementArrayBufferBinding(context);
continue;
case VertexArray::DIRTY_ATTRIB_ENABLED:
updateAttribEnabled(attribIndex);
break;
case VertexArray::DIRTY_ATTRIB_POINTER:
updateAttribPointer(context, attribIndex);
break;
case VertexArray::DIRTY_ATTRIB_FORMAT:
ASSERT(supportVertexAttribBinding());
updateAttribFormat(attribIndex);
break;
case VertexArray::DIRTY_ATTRIB_BINDING:
ASSERT(supportVertexAttribBinding());
updateAttribBinding(attribIndex);
break;
default:
UNREACHABLE();
break;
}
}
}
size_t index = VertexArray::GetVertexIndexFromDirtyBit(dirtyBit);
if (dirtyBit >= VertexArray::DIRTY_BIT_ATTRIB_0_ENABLED &&
dirtyBit < VertexArray::DIRTY_BIT_ATTRIB_MAX_ENABLED)
{
updateAttribEnabled(index);
}
else if (dirtyBit >= VertexArray::DIRTY_BIT_ATTRIB_0_POINTER &&
dirtyBit < VertexArray::DIRTY_BIT_ATTRIB_MAX_POINTER)
{
updateAttribPointer(context, index);
}
void VertexArrayGL::syncDirtyBinding(const gl::Context *context,
size_t bindingIndex,
const gl::VertexArray::DirtyBindingBits &dirtyBindingBits)
{
ASSERT(dirtyBindingBits.any());
else if (dirtyBit >= VertexArray::DIRTY_BIT_ATTRIB_0_FORMAT &&
dirtyBit < VertexArray::DIRTY_BIT_ATTRIB_MAX_FORMAT)
{
ASSERT(supportVertexAttribBinding());
updateAttribFormat(index);
}
else if (dirtyBit >= VertexArray::DIRTY_BIT_ATTRIB_0_BINDING &&
dirtyBit < VertexArray::DIRTY_BIT_ATTRIB_MAX_BINDING)
{
ASSERT(supportVertexAttribBinding());
updateAttribBinding(index);
}
else if (dirtyBit >= VertexArray::DIRTY_BIT_BINDING_0_BUFFER &&
dirtyBit < VertexArray::DIRTY_BIT_BINDING_MAX_BUFFER)
for (size_t dirtyBit : dirtyBindingBits)
{
switch (dirtyBit)
{
ASSERT(supportVertexAttribBinding());
updateBindingBuffer(context, index);
case VertexArray::DIRTY_BINDING_BUFFER:
ASSERT(supportVertexAttribBinding());
updateBindingBuffer(context, bindingIndex);
break;
case VertexArray::DIRTY_BINDING_DIVISOR:
updateBindingDivisor(bindingIndex);
break;
default:
UNREACHABLE();
break;
}
}
}
void VertexArrayGL::syncState(const gl::Context *context,
const VertexArray::DirtyBits &dirtyBits,
const gl::VertexArray::DirtyAttribBitsArray &attribBits,
const gl::VertexArray::DirtyBindingBitsArray &bindingBits)
{
mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
else if (dirtyBit >= VertexArray::DIRTY_BIT_BINDING_0_DIVISOR &&
dirtyBit < VertexArray::DIRTY_BIT_BINDING_MAX_DIVISOR)
for (size_t dirtyBit : dirtyBits)
{
switch (dirtyBit)
{
updateBindingDivisor(index);
case VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER:
updateElementArrayBufferBinding(context);
break;
default:
{
ASSERT(dirtyBit >= VertexArray::DIRTY_BIT_ATTRIB_0);
size_t index = VertexArray::GetVertexIndexFromDirtyBit(dirtyBit);
if (dirtyBit < VertexArray::DIRTY_BIT_ATTRIB_MAX)
{
syncDirtyAttrib(context, index, attribBits[index]);
}
else
{
ASSERT(dirtyBit >= VertexArray::DIRTY_BIT_BINDING_0 &&
dirtyBit < VertexArray::DIRTY_BIT_BINDING_MAX);
syncDirtyBinding(context, index, bindingBits[index]);
}
break;
}
}
else
UNREACHABLE();
}
}
......
......@@ -45,7 +45,10 @@ class VertexArrayGL : public VertexArrayImpl
GLuint getAppliedElementArrayBufferID() const;
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;
void applyNumViewsToDivisor(int numViews);
private:
......@@ -81,6 +84,12 @@ class VertexArrayGL : public VertexArrayImpl
gl::Error streamAttributes(const gl::AttributesMask &activeAttributesMask,
GLsizei instanceCount,
const gl::IndexRange &indexRange) const;
void syncDirtyAttrib(const gl::Context *context,
size_t attribIndex,
const gl::VertexArray::DirtyAttribBits &dirtyAttribBits);
void syncDirtyBinding(const gl::Context *context,
size_t bindingIndex,
const gl::VertexArray::DirtyBindingBits &dirtyBindingBits);
void updateNeedsStreaming(size_t attribIndex);
void updateAttribEnabled(size_t attribIndex);
......
......@@ -94,7 +94,9 @@ gl::Error VertexArrayVk::streamVertexData(ContextVk *context,
}
void VertexArrayVk::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());
......
......@@ -32,7 +32,9 @@ class VertexArrayVk : public VertexArrayImpl
size_t firstVertex,
size_t lastVertex);
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;
const gl::AttribArray<VkBuffer> &getCurrentArrayBufferHandles() const;
const gl::AttribArray<VkDeviceSize> &getCurrentArrayBufferOffsets() const;
......
......@@ -1603,6 +1603,9 @@
2272 WIN NVIDIA OPENGL : dEQP-GLES31.functional.ssbo.layout.random.nested_structs* = FAIL
2272 WIN NVIDIA OPENGL : dEQP-GLES31.functional.ssbo.layout.random.all* = FAIL
// An NVIDIA bug seems to occur when sending GL commands in a certain order to the driver
2432 NVIDIA OPENGL : dEQP-GLES31.functional.vertex_attribute_binding.usage.mixed_usage.mixed_api_change_binding_point = SKIP
// OpenGL and D3D11 Failing Tests
1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.opaque_type_indexing.* = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.shaders.helper_invocation.* = FAIL
......
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