Commit d84b6737 by Jamie Madill Committed by Commit Bot

Cache ValidateDrawStates.

This improves performance of all draw call validation. The error that should be generated on the draw call is cached in the Context. The cache is updated in several places. Bug: angleproject:2747 Change-Id: I178617623731608e2e7166b53ab6489d8b742ff5 Reviewed-on: https://chromium-review.googlesource.com/1158612Reviewed-by: 's avatarYuly Novikov <ynovikov@chromium.org> Reviewed-by: 's avatarFrank Henigman <fjhenigman@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 80171923
...@@ -272,5 +272,4 @@ void Buffer::onTFBindingChanged(const Context *context, bool bound, bool indexed ...@@ -272,5 +272,4 @@ void Buffer::onTFBindingChanged(const Context *context, bool bound, bool indexed
mState.mTransformFeedbackGenericBindingCount += bound ? 1 : -1; mState.mTransformFeedbackGenericBindingCount += bound ? 1 : -1;
} }
} }
} // namespace gl } // namespace gl
...@@ -103,25 +103,67 @@ class StateCache final : angle::NonCopyable ...@@ -103,25 +103,67 @@ class StateCache final : angle::NonCopyable
// Places that can trigger updateVertexElementLimits: // Places that can trigger updateVertexElementLimits:
// 1. onVertexArrayBindingChange. // 1. onVertexArrayBindingChange.
// 2. onProgramExecutableChange. // 2. onProgramExecutableChange.
// 3. onVertexArraySizeChange. // 3. onVertexArrayFormatChange.
// 4. onVertexArrayStateChange. // 4. onVertexArrayBufferChange.
// 5. onVertexArrayStateChange.
GLint64 getNonInstancedVertexElementLimit() const GLint64 getNonInstancedVertexElementLimit() const
{ {
return mCachedNonInstancedVertexElementLimit; return mCachedNonInstancedVertexElementLimit;
} }
GLint64 getInstancedVertexElementLimit() const { return mCachedInstancedVertexElementLimit; } GLint64 getInstancedVertexElementLimit() const { return mCachedInstancedVertexElementLimit; }
// Places that can trigger updateBasicDrawStatesError:
// 1. onVertexArrayBindingChange.
// 2. onProgramExecutableChange.
// 3. onVertexArrayBufferContentsChange.
// 4. onVertexArrayStateChange.
// 5. onVertexArrayBufferStateChange.
// 6. onDrawFramebufferChange.
// 7. onContextCapChange.
// 8. onStencilStateChange.
// 9. onDefaultVertexAttributeChange.
// 10. onActiveTextureChange.
// 11. onQueryChange.
// 12. onTransformFeedbackChange.
// 13. onUniformBufferStateChange.
// 14. onBufferBindingChange.
intptr_t getBasicDrawStatesError(Context *context) const
{
if (mCachedBasicDrawStatesError != kInvalidPointer)
{
return mCachedBasicDrawStatesError;
}
return getBasicDrawStatesErrorImpl(context);
}
// State change notifications. // State change notifications.
void onVertexArrayBindingChange(Context *context); void onVertexArrayBindingChange(Context *context);
void onProgramExecutableChange(Context *context); void onProgramExecutableChange(Context *context);
void onVertexArraySizeChange(Context *context); void onVertexArrayFormatChange(Context *context);
void onVertexArrayBufferContentsChange(Context *context);
void onVertexArrayStateChange(Context *context); void onVertexArrayStateChange(Context *context);
void onVertexArrayBufferStateChange(Context *context);
void onGLES1ClientStateChange(Context *context); void onGLES1ClientStateChange(Context *context);
void onDrawFramebufferChange(Context *context);
void onContextCapChange(Context *context);
void onStencilStateChange(Context *context);
void onDefaultVertexAttributeChange(Context *context);
void onActiveTextureChange(Context *context);
void onQueryChange(Context *context);
void onTransformFeedbackChange(Context *context);
void onUniformBufferStateChange(Context *context);
void onBufferBindingChange(Context *context);
private: private:
// Cache update functions. // Cache update functions.
void updateActiveAttribsMask(Context *context); void updateActiveAttribsMask(Context *context);
void updateVertexElementLimits(Context *context); void updateVertexElementLimits(Context *context);
void updateBasicDrawStatesError();
intptr_t getBasicDrawStatesErrorImpl(Context *context) const;
static constexpr intptr_t kInvalidPointer = 1;
AttributesMask mCachedActiveBufferedAttribsMask; AttributesMask mCachedActiveBufferedAttribsMask;
AttributesMask mCachedActiveClientAttribsMask; AttributesMask mCachedActiveClientAttribsMask;
...@@ -129,6 +171,7 @@ class StateCache final : angle::NonCopyable ...@@ -129,6 +171,7 @@ class StateCache final : angle::NonCopyable
bool mCachedHasAnyEnabledClientAttrib; bool mCachedHasAnyEnabledClientAttrib;
GLint64 mCachedNonInstancedVertexElementLimit; GLint64 mCachedNonInstancedVertexElementLimit;
GLint64 mCachedInstancedVertexElementLimit; GLint64 mCachedInstancedVertexElementLimit;
mutable intptr_t mCachedBasicDrawStatesError;
}; };
class Context final : public egl::LabeledObject, angle::NonCopyable, public angle::ObserverInterface class Context final : public egl::LabeledObject, angle::NonCopyable, public angle::ObserverInterface
......
...@@ -146,7 +146,6 @@ inline Error NoError() ...@@ -146,7 +146,6 @@ inline Error NoError()
} }
using LinkResult = ErrorOrResult<bool>; using LinkResult = ErrorOrResult<bool>;
} // namespace gl } // namespace gl
namespace egl namespace egl
......
...@@ -992,6 +992,7 @@ void Framebuffer::invalidateCompletenessCache(const Context *context) ...@@ -992,6 +992,7 @@ void Framebuffer::invalidateCompletenessCache(const Context *context)
if (mState.mId != 0) if (mState.mId != 0)
{ {
mCachedStatus.reset(); mCachedStatus.reset();
onStateChange(context, angle::SubjectMessage::CONTENTS_CHANGED);
} }
} }
...@@ -1833,7 +1834,7 @@ void Framebuffer::onSubjectStateChange(const Context *context, ...@@ -1833,7 +1834,7 @@ void Framebuffer::onSubjectStateChange(const Context *context,
{ {
ASSERT(!mDirtyBitsGuard.valid() || mDirtyBitsGuard.value().test(index)); ASSERT(!mDirtyBitsGuard.valid() || mDirtyBitsGuard.value().test(index));
mDirtyBits.set(index); mDirtyBits.set(index);
onStateChange(context, angle::SubjectMessage::CONTENTS_CHANGED); onStateChange(context, angle::SubjectMessage::STORAGE_CHANGED);
return; return;
} }
......
...@@ -364,16 +364,16 @@ void VertexArray::setElementArrayBuffer(const Context *context, Buffer *buffer) ...@@ -364,16 +364,16 @@ void VertexArray::setElementArrayBuffer(const Context *context, Buffer *buffer)
{ {
mState.mElementArrayBuffer->onNonTFBindingChanged(context, -1); mState.mElementArrayBuffer->onNonTFBindingChanged(context, -1);
} }
mState.mElementArrayBuffer.set(context, buffer);
if (buffer) if (buffer)
{ {
buffer->onNonTFBindingChanged(context, 1);
mElementArrayBufferObserverBinding.bind(buffer->getImplementation()); mElementArrayBufferObserverBinding.bind(buffer->getImplementation());
buffer->onNonTFBindingChanged(context, 1);
} }
else else
{ {
mElementArrayBufferObserverBinding.bind(nullptr); mElementArrayBufferObserverBinding.bind(nullptr);
} }
mState.mElementArrayBuffer.set(context, buffer);
mDirtyBits.set(DIRTY_BIT_ELEMENT_ARRAY_BUFFER); mDirtyBits.set(DIRTY_BIT_ELEMENT_ARRAY_BUFFER);
} }
...@@ -454,6 +454,7 @@ void VertexArray::onSubjectStateChange(const gl::Context *context, ...@@ -454,6 +454,7 @@ void VertexArray::onSubjectStateChange(const gl::Context *context,
if (!IsElementArrayBufferSubjectIndex(index)) if (!IsElementArrayBufferSubjectIndex(index))
{ {
updateCachedMappedArrayBuffers(&mState.mVertexBindings[index]); updateCachedMappedArrayBuffers(&mState.mVertexBindings[index]);
onStateChange(context, angle::SubjectMessage::RESOURCE_MAPPED);
} }
break; break;
...@@ -463,6 +464,7 @@ void VertexArray::onSubjectStateChange(const gl::Context *context, ...@@ -463,6 +464,7 @@ void VertexArray::onSubjectStateChange(const gl::Context *context,
if (!IsElementArrayBufferSubjectIndex(index)) if (!IsElementArrayBufferSubjectIndex(index))
{ {
updateCachedMappedArrayBuffers(&mState.mVertexBindings[index]); updateCachedMappedArrayBuffers(&mState.mVertexBindings[index]);
onStateChange(context, angle::SubjectMessage::RESOURCE_UNMAPPED);
} }
break; break;
......
...@@ -2566,7 +2566,10 @@ const char *ValidateDrawStates(Context *context) ...@@ -2566,7 +2566,10 @@ const char *ValidateDrawStates(Context *context)
// WebGL buffers cannot be mapped/unmapped because the MapBufferRange, FlushMappedBufferRange, // WebGL buffers cannot be mapped/unmapped because the MapBufferRange, FlushMappedBufferRange,
// and UnmapBuffer entry points are removed from the WebGL 2.0 API. // and UnmapBuffer entry points are removed from the WebGL 2.0 API.
// https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.14 // https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.14
if (!extensions.webglCompatibility && state.getVertexArray()->hasMappedEnabledArrayBuffer()) VertexArray *vertexArray = state.getVertexArray();
ASSERT(vertexArray);
if (!extensions.webglCompatibility && vertexArray->hasMappedEnabledArrayBuffer())
{ {
return kErrorBufferMapped; return kErrorBufferMapped;
} }
...@@ -2574,6 +2577,8 @@ const char *ValidateDrawStates(Context *context) ...@@ -2574,6 +2577,8 @@ const char *ValidateDrawStates(Context *context)
// Note: these separate values are not supported in WebGL, due to D3D's limitations. See // Note: these separate values are not supported in WebGL, due to D3D's limitations. See
// Section 6.10 of the WebGL 1.0 spec. // Section 6.10 of the WebGL 1.0 spec.
Framebuffer *framebuffer = state.getDrawFramebuffer(); Framebuffer *framebuffer = state.getDrawFramebuffer();
ASSERT(framebuffer);
if (context->getLimitations().noSeparateStencilRefsAndMasks || extensions.webglCompatibility) if (context->getLimitations().noSeparateStencilRefsAndMasks || extensions.webglCompatibility)
{ {
ASSERT(framebuffer); ASSERT(framebuffer);
...@@ -2785,9 +2790,11 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count) ...@@ -2785,9 +2790,11 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count)
const State &state = context->getGLState(); const State &state = context->getGLState();
const char *errorMessage = ValidateDrawStates(context); intptr_t drawStatesError = context->getStateCache().getBasicDrawStatesError(context);
if (errorMessage) if (drawStatesError)
{ {
const char *errorMessage = reinterpret_cast<const char *>(drawStatesError);
// All errors from ValidateDrawStates should return INVALID_OPERATION except Framebuffer // All errors from ValidateDrawStates should return INVALID_OPERATION except Framebuffer
// Incomplete. // Incomplete.
GLenum errorCode = GLenum errorCode =
......
...@@ -713,11 +713,7 @@ ANGLE_INLINE bool ValidateFramebufferComplete(Context *context, Framebuffer *fra ...@@ -713,11 +713,7 @@ ANGLE_INLINE bool ValidateFramebufferComplete(Context *context, Framebuffer *fra
return true; return true;
} }
struct ErrorAndMessage const char *ValidateDrawStates(Context *context);
{
GLenum errorType;
const char *message;
};
} // namespace gl } // namespace gl
#endif // LIBANGLE_VALIDATION_ES_H_ #endif // LIBANGLE_VALIDATION_ES_H_
...@@ -307,6 +307,8 @@ TEST_P(FramebufferFormatsTest, IncompleteCubeMap) ...@@ -307,6 +307,8 @@ TEST_P(FramebufferFormatsTest, IncompleteCubeMap)
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER)); glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
// Verify drawing with the incomplete framebuffer produces a GL error // Verify drawing with the incomplete framebuffer produces a GL error
mProgram = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); mProgram = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
ASSERT_NE(0u, mProgram); ASSERT_NE(0u, mProgram);
......
...@@ -2688,6 +2688,104 @@ void main() ...@@ -2688,6 +2688,104 @@ void main()
glEndTransformFeedback(); glEndTransformFeedback();
EXPECT_GL_ERROR(GL_INVALID_OPERATION) << "Simultaneous pack buffer binding should fail"; EXPECT_GL_ERROR(GL_INVALID_OPERATION) << "Simultaneous pack buffer binding should fail";
} }
// Tests a valid rendering setup with two textures. Followed by a draw with conflicting samplers.
TEST_P(ValidationStateChangeTest, TextureConflict)
{
ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_texture_storage"));
GLint maxTextures = 0;
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextures);
ANGLE_SKIP_TEST_IF(maxTextures < 2);
// Set up state.
constexpr GLint kSize = 2;
std::vector<GLColor> greenData(4, GLColor::green);
GLTexture textureA;
glBindTexture(GL_TEXTURE_2D, textureA);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
greenData.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glActiveTexture(GL_TEXTURE1);
GLTexture textureB;
glBindTexture(GL_TEXTURE_CUBE_MAP, textureB);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA8, kSize, kSize, 0, GL_RGBA,
GL_UNSIGNED_BYTE, greenData.data());
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA8, kSize, kSize, 0, GL_RGBA,
GL_UNSIGNED_BYTE, greenData.data());
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA8, kSize, kSize, 0, GL_RGBA,
GL_UNSIGNED_BYTE, greenData.data());
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA8, kSize, kSize, 0, GL_RGBA,
GL_UNSIGNED_BYTE, greenData.data());
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA8, kSize, kSize, 0, GL_RGBA,
GL_UNSIGNED_BYTE, greenData.data());
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA8, kSize, kSize, 0, GL_RGBA,
GL_UNSIGNED_BYTE, greenData.data());
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
constexpr char kVS[] = R"(attribute vec2 position;
varying mediump vec2 texCoord;
void main()
{
gl_Position = vec4(position, 0, 1);
texCoord = position * 0.5 + vec2(0.5);
})";
constexpr char kFS[] = R"(varying mediump vec2 texCoord;
uniform sampler2D texA;
uniform samplerCube texB;
void main()
{
gl_FragColor = texture2D(texA, texCoord) + textureCube(texB, vec3(1, 0, 0));
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glUseProgram(program);
const auto &quadVertices = GetQuadVertices();
GLBuffer arrayBuffer;
glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer);
glBufferData(GL_ARRAY_BUFFER, quadVertices.size() * sizeof(Vector3), quadVertices.data(),
GL_STATIC_DRAW);
GLint positionLoc = glGetAttribLocation(program, "position");
ASSERT_NE(-1, positionLoc);
GLint texALoc = glGetUniformLocation(program, "texA");
ASSERT_NE(-1, texALoc);
GLint texBLoc = glGetUniformLocation(program, "texB");
ASSERT_NE(-1, texBLoc);
glUniform1i(texALoc, 0);
glUniform1i(texBLoc, 1);
glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(positionLoc);
ASSERT_GL_NO_ERROR();
// First draw. Should succeed.
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
// Second draw to ensure all state changes are flushed.
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
// Make the uniform use an invalid texture binding.
glUniform1i(texBLoc, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
} // anonymous namespace } // anonymous namespace
ANGLE_INSTANTIATE_TEST(StateChangeTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL(), ES2_VULKAN()); ANGLE_INSTANTIATE_TEST(StateChangeTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL(), ES2_VULKAN());
......
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