Commit 220642a9 by Tim Van Patten Committed by Commit Bot

Allow Drawing with Immutable Persistent Mapped Buffers

From the EXT_buffer_storage overview: The GL_EXT_texture_storage extension added immutable storage for texture objects (and was subsequently incorporated into OpenGL ES 3.0). This extension further applies the concept of immutable storage to buffer objects. [T]his extension introduces the concept of persistent client mappings of buffer objects, which allow clients to retain pointers to a buffer's data store returned as the result of a mapping, and to issue drawing commands while those mappings are in place. The initial implementation of EXT_buffer_storage didn't enable this portion of the extension, so ANGLE is generating errors while attempting to draw with an immutable buffer mapped with the GL_MAP_PERSISTENT_BIT flag. This CL enables that functionality, since apps (e.g., FIFA Soccer) rely on it. Bug: angleproject:5473 Change-Id: Icf1c0597156044a342aac5e4d2abbc29b34f46b2 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2596957Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCody Northrop <cnorthrop@google.com> Commit-Queue: Tim Van Patten <timvp@google.com>
parent bc06d145
......@@ -90,7 +90,20 @@ void VertexArrayState::setAttribBinding(const Context *context,
bool isMapped = newBinding.getBuffer().get() && newBinding.getBuffer()->isMapped();
mCachedMappedArrayBuffers.set(attribIndex, isMapped);
mCachedEnabledMappedArrayBuffers.set(attribIndex, isMapped && attrib.enabled);
mEnabledAttributesMask.set(attribIndex, attrib.enabled);
updateCachedMutableOrNonPersistentArrayBuffers(attribIndex);
mCachedInvalidMappedArrayBuffer = mCachedMappedArrayBuffers & mEnabledAttributesMask &
mCachedMutableOrImpersistentArrayBuffers;
}
void VertexArrayState::updateCachedMutableOrNonPersistentArrayBuffers(size_t index)
{
const VertexBinding &vertexBinding = mVertexBindings[index];
const BindingPointer<Buffer> &buffer = vertexBinding.getBuffer();
bool isMutableOrImpersistentArrayBuffer =
buffer.get() &&
(!buffer->isImmutable() || (buffer->getAccessFlags() & GL_MAP_PERSISTENT_BIT_EXT) == 0);
mCachedMutableOrImpersistentArrayBuffers.set(index, isMutableOrImpersistentArrayBuffer);
}
// VertexArray implementation.
......@@ -237,8 +250,10 @@ ANGLE_INLINE void VertexArray::updateCachedBufferBindingSize(VertexBinding *bind
}
}
ANGLE_INLINE void VertexArray::updateCachedMappedArrayBuffers(
ANGLE_INLINE void VertexArray::updateCachedArrayBuffersMasks(
bool isMapped,
bool isImmutable,
bool isPersistent,
const AttributesMask &boundAttributesMask)
{
if (isMapped)
......@@ -250,15 +265,28 @@ ANGLE_INLINE void VertexArray::updateCachedMappedArrayBuffers(
mState.mCachedMappedArrayBuffers &= ~boundAttributesMask;
}
mState.mCachedEnabledMappedArrayBuffers =
mState.mCachedMappedArrayBuffers & mState.mEnabledAttributesMask;
if (!isImmutable || !isPersistent)
{
mState.mCachedMutableOrImpersistentArrayBuffers |= boundAttributesMask;
}
else
{
mState.mCachedMutableOrImpersistentArrayBuffers &= ~boundAttributesMask;
}
mState.mCachedInvalidMappedArrayBuffer = mState.mCachedMappedArrayBuffers &
mState.mEnabledAttributesMask &
mState.mCachedMutableOrImpersistentArrayBuffers;
}
ANGLE_INLINE void VertexArray::updateCachedMappedArrayBuffersBinding(const VertexBinding &binding)
{
const Buffer *buffer = binding.getBuffer().get();
return updateCachedMappedArrayBuffers(buffer && buffer->isMapped(),
binding.getBoundAttributesMask());
bool isMapped = buffer && buffer->isMapped();
bool isImmutable = buffer && buffer->isImmutable();
bool isPersistent = buffer && (buffer->getAccessFlags() & GL_MAP_PERSISTENT_BIT_EXT) != 0;
return updateCachedArrayBuffersMasks(isMapped, isImmutable, isPersistent,
binding.getBoundAttributesMask());
}
ANGLE_INLINE void VertexArray::updateCachedTransformFeedbackBindingValidation(size_t bindingIndex,
......@@ -315,14 +343,18 @@ bool VertexArray::bindVertexBufferImpl(const Context *context,
mCachedTransformFeedbackConflictedBindingsMask.set(
bindingIndex, boundBuffer->isBoundForTransformFeedbackAndOtherUse());
mState.mClientMemoryAttribsMask &= ~binding->getBoundAttributesMask();
updateCachedMappedArrayBuffers((boundBuffer->isMapped() == GL_TRUE),
binding->getBoundAttributesMask());
bool isMapped = boundBuffer->isMapped() == GL_TRUE;
bool isImmutable = boundBuffer->isImmutable() == GL_TRUE;
bool isPersistent = (boundBuffer->getAccessFlags() & GL_MAP_PERSISTENT_BIT_EXT) != 0;
updateCachedArrayBuffersMasks(isMapped, isImmutable, isPersistent,
binding->getBoundAttributesMask());
}
else
{
mCachedTransformFeedbackConflictedBindingsMask.set(bindingIndex, false);
mState.mClientMemoryAttribsMask |= binding->getBoundAttributesMask();
updateCachedMappedArrayBuffers(false, binding->getBoundAttributesMask());
updateCachedArrayBuffersMasks(false, false, false, binding->getBoundAttributesMask());
}
return true;
......@@ -441,8 +473,10 @@ void VertexArray::enableAttribute(size_t attribIndex, bool enabledState)
// Update state cache
mState.mEnabledAttributesMask.set(attribIndex, enabledState);
mState.mCachedEnabledMappedArrayBuffers =
mState.mCachedMappedArrayBuffers & mState.mEnabledAttributesMask;
mState.updateCachedMutableOrNonPersistentArrayBuffers(attribIndex);
mState.mCachedInvalidMappedArrayBuffer = mState.mCachedMappedArrayBuffers &
mState.mEnabledAttributesMask &
mState.mCachedMutableOrImpersistentArrayBuffers;
}
ANGLE_INLINE void VertexArray::setVertexAttribPointerImpl(const Context *context,
......
......@@ -81,6 +81,8 @@ class VertexArrayState final : angle::NonCopyable
}
private:
void updateCachedMutableOrNonPersistentArrayBuffers(size_t index);
friend class VertexArray;
std::string mLabel;
std::vector<VertexAttribute> mVertexAttributes;
......@@ -101,7 +103,8 @@ class VertexArrayState final : angle::NonCopyable
// Used for validation cache. Indexed by attribute.
AttributesMask mCachedMappedArrayBuffers;
AttributesMask mCachedEnabledMappedArrayBuffers;
AttributesMask mCachedMutableOrImpersistentArrayBuffers;
AttributesMask mCachedInvalidMappedArrayBuffer;
};
class VertexArray final : public angle::ObserverInterface,
......@@ -251,9 +254,9 @@ class VertexArray final : public angle::ObserverInterface,
return mState.hasEnabledNullPointerClientArray();
}
bool hasMappedEnabledArrayBuffer() const
bool hasInvalidMappedArrayBuffer() const
{
return mState.mCachedEnabledMappedArrayBuffers.any();
return mState.mCachedInvalidMappedArrayBuffer.any();
}
const VertexArrayState &getState() const { return mState; }
......@@ -309,7 +312,10 @@ class VertexArray final : public angle::ObserverInterface,
// These are used to optimize draw call validation.
void updateCachedBufferBindingSize(VertexBinding *binding);
void updateCachedTransformFeedbackBindingValidation(size_t bindingIndex, const Buffer *buffer);
void updateCachedMappedArrayBuffers(bool isMapped, const AttributesMask &boundAttributesMask);
void updateCachedArrayBuffersMasks(bool isMapped,
bool isImmutable,
bool isPersistent,
const AttributesMask &boundAttributesMask);
void updateCachedMappedArrayBuffersBinding(const VertexBinding &binding);
angle::Result getIndexRangeImpl(const Context *context,
......
......@@ -3625,7 +3625,7 @@ const char *ValidateDrawStates(const Context *context)
VertexArray *vertexArray = state.getVertexArray();
ASSERT(vertexArray);
if (!extensions.webglCompatibility && vertexArray->hasMappedEnabledArrayBuffer())
if (!extensions.webglCompatibility && vertexArray->hasInvalidMappedArrayBuffer())
{
return kBufferMapped;
}
......
......@@ -4244,6 +4244,146 @@ TEST_P(ValidationStateChangeTest, MapBufferAndDraw)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
}
// Tests that mapping an immutable and persistent buffer after calling glVertexAttribPointer()
// allows rendering to succeed.
TEST_P(ValidationStateChangeTest, MapImmutablePersistentBufferAndDraw)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_buffer_storage"));
// Initialize program and set up state.
ANGLE_GL_PROGRAM(program, kColorVS, kColorFS);
glUseProgram(program);
GLint positionLoc = glGetAttribLocation(program, "position");
ASSERT_NE(-1, positionLoc);
GLint colorLoc = glGetAttribLocation(program, "color");
ASSERT_NE(-1, colorLoc);
const std::array<Vector3, 6> &quadVertices = GetQuadVertices();
const size_t posBufferSize = quadVertices.size() * sizeof(Vector3);
GLBuffer posBuffer;
glBindBuffer(GL_ARRAY_BUFFER, posBuffer);
glBufferStorageEXT(GL_ARRAY_BUFFER, posBufferSize, quadVertices.data(),
GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT_EXT);
// Start with position enabled.
glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(positionLoc);
std::vector<GLColor> colorVertices(6, GLColor::blue);
const size_t colorBufferSize = sizeof(GLColor) * 6;
GLBuffer colorBuffer;
glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
glBufferStorageEXT(GL_ARRAY_BUFFER, colorBufferSize, colorVertices.data(),
GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT_EXT);
// Start with color disabled.
glVertexAttribPointer(colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, nullptr);
glDisableVertexAttribArray(colorLoc);
ASSERT_GL_NO_ERROR();
// Draw without a mapped buffer. Should succeed.
glVertexAttrib4f(colorLoc, 0, 1, 0, 1);
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
// Map position buffer and draw. Should succeed.
glBindBuffer(GL_ARRAY_BUFFER, posBuffer);
glMapBufferRange(GL_ARRAY_BUFFER, 0, posBufferSize,
GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT_EXT);
ASSERT_GL_NO_ERROR();
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
glUnmapBuffer(GL_ARRAY_BUFFER);
// Map then enable color buffer. Should succeed.
glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
glMapBufferRange(GL_ARRAY_BUFFER, 0, colorBufferSize,
GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT_EXT);
glEnableVertexAttribArray(colorLoc);
ASSERT_GL_NO_ERROR();
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
// Unmap then draw. Should succeed.
glUnmapBuffer(GL_ARRAY_BUFFER);
ASSERT_GL_NO_ERROR();
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
}
// Tests that mapping an immutable and persistent buffer before calling glVertexAttribPointer()
// allows rendering to succeed. This case is special in that the VertexArray is not observing the
// buffer yet, so it's various cached buffer states aren't updated when the buffer is mapped.
TEST_P(ValidationStateChangeTest, MapImmutablePersistentBufferThenVAPAndDraw)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_buffer_storage"));
// Initialize program and set up state.
ANGLE_GL_PROGRAM(program, kColorVS, kColorFS);
glUseProgram(program);
GLint positionLoc = glGetAttribLocation(program, "position");
ASSERT_NE(-1, positionLoc);
GLint colorLoc = glGetAttribLocation(program, "color");
ASSERT_NE(-1, colorLoc);
const std::array<Vector3, 6> &quadVertices = GetQuadVertices();
const size_t posBufferSize = quadVertices.size() * sizeof(Vector3);
GLBuffer posBuffer;
glBindBuffer(GL_ARRAY_BUFFER, posBuffer);
glBufferStorageEXT(GL_ARRAY_BUFFER, posBufferSize, quadVertices.data(),
GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT_EXT);
glMapBufferRange(GL_ARRAY_BUFFER, 0, posBufferSize,
GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT_EXT);
// Start with position enabled.
glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(positionLoc);
std::vector<GLColor> colorVertices(6, GLColor::blue);
const size_t colorBufferSize = sizeof(GLColor) * 6;
GLBuffer colorBuffer;
glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
glBufferStorageEXT(GL_ARRAY_BUFFER, colorBufferSize, colorVertices.data(),
GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT_EXT);
glMapBufferRange(GL_ARRAY_BUFFER, 0, colorBufferSize,
GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT_EXT);
ASSERT_GL_NO_ERROR();
// Start with color disabled.
glVertexAttribPointer(colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, nullptr);
glDisableVertexAttribArray(colorLoc);
ASSERT_GL_NO_ERROR();
// Draw without a mapped buffer. Should succeed.
glVertexAttrib4f(colorLoc, 0, 1, 0, 1);
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
// Unmap then draw. Should succeed.
glUnmapBuffer(GL_ARRAY_BUFFER);
glEnableVertexAttribArray(colorLoc);
ASSERT_GL_NO_ERROR();
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
}
// Tests that changing a vertex binding with glVertexAttribDivisor updates the mapped buffer check.
TEST_P(ValidationStateChangeTestES31, MapBufferAndDrawWithDivisor)
{
......
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