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
mState.mTransformFeedbackGenericBindingCount += bound ? 1 : -1;
}
}
} // namespace gl
......@@ -103,25 +103,67 @@ class StateCache final : angle::NonCopyable
// Places that can trigger updateVertexElementLimits:
// 1. onVertexArrayBindingChange.
// 2. onProgramExecutableChange.
// 3. onVertexArraySizeChange.
// 4. onVertexArrayStateChange.
// 3. onVertexArrayFormatChange.
// 4. onVertexArrayBufferChange.
// 5. onVertexArrayStateChange.
GLint64 getNonInstancedVertexElementLimit() const
{
return mCachedNonInstancedVertexElementLimit;
}
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.
void onVertexArrayBindingChange(Context *context);
void onProgramExecutableChange(Context *context);
void onVertexArraySizeChange(Context *context);
void onVertexArrayFormatChange(Context *context);
void onVertexArrayBufferContentsChange(Context *context);
void onVertexArrayStateChange(Context *context);
void onVertexArrayBufferStateChange(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:
// Cache update functions.
void updateActiveAttribsMask(Context *context);
void updateVertexElementLimits(Context *context);
void updateBasicDrawStatesError();
intptr_t getBasicDrawStatesErrorImpl(Context *context) const;
static constexpr intptr_t kInvalidPointer = 1;
AttributesMask mCachedActiveBufferedAttribsMask;
AttributesMask mCachedActiveClientAttribsMask;
......@@ -129,6 +171,7 @@ class StateCache final : angle::NonCopyable
bool mCachedHasAnyEnabledClientAttrib;
GLint64 mCachedNonInstancedVertexElementLimit;
GLint64 mCachedInstancedVertexElementLimit;
mutable intptr_t mCachedBasicDrawStatesError;
};
class Context final : public egl::LabeledObject, angle::NonCopyable, public angle::ObserverInterface
......
......@@ -146,7 +146,6 @@ inline Error NoError()
}
using LinkResult = ErrorOrResult<bool>;
} // namespace gl
namespace egl
......
......@@ -992,6 +992,7 @@ void Framebuffer::invalidateCompletenessCache(const Context *context)
if (mState.mId != 0)
{
mCachedStatus.reset();
onStateChange(context, angle::SubjectMessage::CONTENTS_CHANGED);
}
}
......@@ -1833,7 +1834,7 @@ void Framebuffer::onSubjectStateChange(const Context *context,
{
ASSERT(!mDirtyBitsGuard.valid() || mDirtyBitsGuard.value().test(index));
mDirtyBits.set(index);
onStateChange(context, angle::SubjectMessage::CONTENTS_CHANGED);
onStateChange(context, angle::SubjectMessage::STORAGE_CHANGED);
return;
}
......
......@@ -364,16 +364,16 @@ void VertexArray::setElementArrayBuffer(const Context *context, Buffer *buffer)
{
mState.mElementArrayBuffer->onNonTFBindingChanged(context, -1);
}
mState.mElementArrayBuffer.set(context, buffer);
if (buffer)
{
buffer->onNonTFBindingChanged(context, 1);
mElementArrayBufferObserverBinding.bind(buffer->getImplementation());
buffer->onNonTFBindingChanged(context, 1);
}
else
{
mElementArrayBufferObserverBinding.bind(nullptr);
}
mState.mElementArrayBuffer.set(context, buffer);
mDirtyBits.set(DIRTY_BIT_ELEMENT_ARRAY_BUFFER);
}
......@@ -454,6 +454,7 @@ void VertexArray::onSubjectStateChange(const gl::Context *context,
if (!IsElementArrayBufferSubjectIndex(index))
{
updateCachedMappedArrayBuffers(&mState.mVertexBindings[index]);
onStateChange(context, angle::SubjectMessage::RESOURCE_MAPPED);
}
break;
......@@ -463,6 +464,7 @@ void VertexArray::onSubjectStateChange(const gl::Context *context,
if (!IsElementArrayBufferSubjectIndex(index))
{
updateCachedMappedArrayBuffers(&mState.mVertexBindings[index]);
onStateChange(context, angle::SubjectMessage::RESOURCE_UNMAPPED);
}
break;
......
......@@ -2566,7 +2566,10 @@ const char *ValidateDrawStates(Context *context)
// WebGL buffers cannot be mapped/unmapped because the MapBufferRange, FlushMappedBufferRange,
// and UnmapBuffer entry points are removed from the WebGL 2.0 API.
// 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;
}
......@@ -2574,6 +2577,8 @@ const char *ValidateDrawStates(Context *context)
// Note: these separate values are not supported in WebGL, due to D3D's limitations. See
// Section 6.10 of the WebGL 1.0 spec.
Framebuffer *framebuffer = state.getDrawFramebuffer();
ASSERT(framebuffer);
if (context->getLimitations().noSeparateStencilRefsAndMasks || extensions.webglCompatibility)
{
ASSERT(framebuffer);
......@@ -2785,9 +2790,11 @@ bool ValidateDrawBase(Context *context, PrimitiveMode mode, GLsizei count)
const State &state = context->getGLState();
const char *errorMessage = ValidateDrawStates(context);
if (errorMessage)
intptr_t drawStatesError = context->getStateCache().getBasicDrawStatesError(context);
if (drawStatesError)
{
const char *errorMessage = reinterpret_cast<const char *>(drawStatesError);
// All errors from ValidateDrawStates should return INVALID_OPERATION except Framebuffer
// Incomplete.
GLenum errorCode =
......
......@@ -713,11 +713,7 @@ ANGLE_INLINE bool ValidateFramebufferComplete(Context *context, Framebuffer *fra
return true;
}
struct ErrorAndMessage
{
GLenum errorType;
const char *message;
};
const char *ValidateDrawStates(Context *context);
} // namespace gl
#endif // LIBANGLE_VALIDATION_ES_H_
......@@ -307,6 +307,8 @@ TEST_P(FramebufferFormatsTest, IncompleteCubeMap)
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
ASSERT_GL_NO_ERROR();
// Verify drawing with the incomplete framebuffer produces a GL error
mProgram = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
ASSERT_NE(0u, mProgram);
......
......@@ -2688,6 +2688,104 @@ void main()
glEndTransformFeedback();
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
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