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, ...@@ -90,7 +90,20 @@ void VertexArrayState::setAttribBinding(const Context *context,
bool isMapped = newBinding.getBuffer().get() && newBinding.getBuffer()->isMapped(); bool isMapped = newBinding.getBuffer().get() && newBinding.getBuffer()->isMapped();
mCachedMappedArrayBuffers.set(attribIndex, 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. // VertexArray implementation.
...@@ -237,8 +250,10 @@ ANGLE_INLINE void VertexArray::updateCachedBufferBindingSize(VertexBinding *bind ...@@ -237,8 +250,10 @@ ANGLE_INLINE void VertexArray::updateCachedBufferBindingSize(VertexBinding *bind
} }
} }
ANGLE_INLINE void VertexArray::updateCachedMappedArrayBuffers( ANGLE_INLINE void VertexArray::updateCachedArrayBuffersMasks(
bool isMapped, bool isMapped,
bool isImmutable,
bool isPersistent,
const AttributesMask &boundAttributesMask) const AttributesMask &boundAttributesMask)
{ {
if (isMapped) if (isMapped)
...@@ -250,15 +265,28 @@ ANGLE_INLINE void VertexArray::updateCachedMappedArrayBuffers( ...@@ -250,15 +265,28 @@ ANGLE_INLINE void VertexArray::updateCachedMappedArrayBuffers(
mState.mCachedMappedArrayBuffers &= ~boundAttributesMask; mState.mCachedMappedArrayBuffers &= ~boundAttributesMask;
} }
mState.mCachedEnabledMappedArrayBuffers = if (!isImmutable || !isPersistent)
mState.mCachedMappedArrayBuffers & mState.mEnabledAttributesMask; {
mState.mCachedMutableOrImpersistentArrayBuffers |= boundAttributesMask;
}
else
{
mState.mCachedMutableOrImpersistentArrayBuffers &= ~boundAttributesMask;
}
mState.mCachedInvalidMappedArrayBuffer = mState.mCachedMappedArrayBuffers &
mState.mEnabledAttributesMask &
mState.mCachedMutableOrImpersistentArrayBuffers;
} }
ANGLE_INLINE void VertexArray::updateCachedMappedArrayBuffersBinding(const VertexBinding &binding) ANGLE_INLINE void VertexArray::updateCachedMappedArrayBuffersBinding(const VertexBinding &binding)
{ {
const Buffer *buffer = binding.getBuffer().get(); const Buffer *buffer = binding.getBuffer().get();
return updateCachedMappedArrayBuffers(buffer && buffer->isMapped(), bool isMapped = buffer && buffer->isMapped();
binding.getBoundAttributesMask()); 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, ANGLE_INLINE void VertexArray::updateCachedTransformFeedbackBindingValidation(size_t bindingIndex,
...@@ -315,14 +343,18 @@ bool VertexArray::bindVertexBufferImpl(const Context *context, ...@@ -315,14 +343,18 @@ bool VertexArray::bindVertexBufferImpl(const Context *context,
mCachedTransformFeedbackConflictedBindingsMask.set( mCachedTransformFeedbackConflictedBindingsMask.set(
bindingIndex, boundBuffer->isBoundForTransformFeedbackAndOtherUse()); bindingIndex, boundBuffer->isBoundForTransformFeedbackAndOtherUse());
mState.mClientMemoryAttribsMask &= ~binding->getBoundAttributesMask(); 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 else
{ {
mCachedTransformFeedbackConflictedBindingsMask.set(bindingIndex, false); mCachedTransformFeedbackConflictedBindingsMask.set(bindingIndex, false);
mState.mClientMemoryAttribsMask |= binding->getBoundAttributesMask(); mState.mClientMemoryAttribsMask |= binding->getBoundAttributesMask();
updateCachedMappedArrayBuffers(false, binding->getBoundAttributesMask()); updateCachedArrayBuffersMasks(false, false, false, binding->getBoundAttributesMask());
} }
return true; return true;
...@@ -441,8 +473,10 @@ void VertexArray::enableAttribute(size_t attribIndex, bool enabledState) ...@@ -441,8 +473,10 @@ void VertexArray::enableAttribute(size_t attribIndex, bool enabledState)
// Update state cache // Update state cache
mState.mEnabledAttributesMask.set(attribIndex, enabledState); mState.mEnabledAttributesMask.set(attribIndex, enabledState);
mState.mCachedEnabledMappedArrayBuffers = mState.updateCachedMutableOrNonPersistentArrayBuffers(attribIndex);
mState.mCachedMappedArrayBuffers & mState.mEnabledAttributesMask; mState.mCachedInvalidMappedArrayBuffer = mState.mCachedMappedArrayBuffers &
mState.mEnabledAttributesMask &
mState.mCachedMutableOrImpersistentArrayBuffers;
} }
ANGLE_INLINE void VertexArray::setVertexAttribPointerImpl(const Context *context, ANGLE_INLINE void VertexArray::setVertexAttribPointerImpl(const Context *context,
......
...@@ -81,6 +81,8 @@ class VertexArrayState final : angle::NonCopyable ...@@ -81,6 +81,8 @@ class VertexArrayState final : angle::NonCopyable
} }
private: private:
void updateCachedMutableOrNonPersistentArrayBuffers(size_t index);
friend class VertexArray; friend class VertexArray;
std::string mLabel; std::string mLabel;
std::vector<VertexAttribute> mVertexAttributes; std::vector<VertexAttribute> mVertexAttributes;
...@@ -101,7 +103,8 @@ class VertexArrayState final : angle::NonCopyable ...@@ -101,7 +103,8 @@ class VertexArrayState final : angle::NonCopyable
// Used for validation cache. Indexed by attribute. // Used for validation cache. Indexed by attribute.
AttributesMask mCachedMappedArrayBuffers; AttributesMask mCachedMappedArrayBuffers;
AttributesMask mCachedEnabledMappedArrayBuffers; AttributesMask mCachedMutableOrImpersistentArrayBuffers;
AttributesMask mCachedInvalidMappedArrayBuffer;
}; };
class VertexArray final : public angle::ObserverInterface, class VertexArray final : public angle::ObserverInterface,
...@@ -251,9 +254,9 @@ class VertexArray final : public angle::ObserverInterface, ...@@ -251,9 +254,9 @@ class VertexArray final : public angle::ObserverInterface,
return mState.hasEnabledNullPointerClientArray(); return mState.hasEnabledNullPointerClientArray();
} }
bool hasMappedEnabledArrayBuffer() const bool hasInvalidMappedArrayBuffer() const
{ {
return mState.mCachedEnabledMappedArrayBuffers.any(); return mState.mCachedInvalidMappedArrayBuffer.any();
} }
const VertexArrayState &getState() const { return mState; } const VertexArrayState &getState() const { return mState; }
...@@ -309,7 +312,10 @@ class VertexArray final : public angle::ObserverInterface, ...@@ -309,7 +312,10 @@ class VertexArray final : public angle::ObserverInterface,
// These are used to optimize draw call validation. // These are used to optimize draw call validation.
void updateCachedBufferBindingSize(VertexBinding *binding); void updateCachedBufferBindingSize(VertexBinding *binding);
void updateCachedTransformFeedbackBindingValidation(size_t bindingIndex, const Buffer *buffer); 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); void updateCachedMappedArrayBuffersBinding(const VertexBinding &binding);
angle::Result getIndexRangeImpl(const Context *context, angle::Result getIndexRangeImpl(const Context *context,
......
...@@ -3625,7 +3625,7 @@ const char *ValidateDrawStates(const Context *context) ...@@ -3625,7 +3625,7 @@ const char *ValidateDrawStates(const Context *context)
VertexArray *vertexArray = state.getVertexArray(); VertexArray *vertexArray = state.getVertexArray();
ASSERT(vertexArray); ASSERT(vertexArray);
if (!extensions.webglCompatibility && vertexArray->hasMappedEnabledArrayBuffer()) if (!extensions.webglCompatibility && vertexArray->hasInvalidMappedArrayBuffer())
{ {
return kBufferMapped; return kBufferMapped;
} }
......
...@@ -4244,6 +4244,146 @@ TEST_P(ValidationStateChangeTest, MapBufferAndDraw) ...@@ -4244,6 +4244,146 @@ TEST_P(ValidationStateChangeTest, MapBufferAndDraw)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); 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. // Tests that changing a vertex binding with glVertexAttribDivisor updates the mapped buffer check.
TEST_P(ValidationStateChangeTestES31, MapBufferAndDrawWithDivisor) 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