Commit 70d79b76 by Jonah Ryan-Davis Committed by Commit Bot

GL: Add state validation functions to GL backend

These functions validate that the driver's reported state matches ANGLE's internal state. Can be useful for debugging Bug: angleproject:3900 Change-Id: I35d15e991986dcab114939c551a88549f09bd263 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1797254 Commit-Queue: Jonah Ryan-Davis <jonahr@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 33ffed01
...@@ -253,6 +253,11 @@ std::ostream &FmtHex(std::ostream &os, T value) ...@@ -253,6 +253,11 @@ std::ostream &FmtHex(std::ostream &os, T value)
# define EVENT(message, ...) (void(0)) # define EVENT(message, ...) (void(0))
#endif #endif
// The state tracked by ANGLE will be validated with the driver state before each call
#if defined(ANGLE_ENABLE_ASSERTS)
# define ANGLE_STATE_VALIDATION_ENABLED
#endif
#if defined(__GNUC__) #if defined(__GNUC__)
# define ANGLE_CRASH() __builtin_trap() # define ANGLE_CRASH() __builtin_trap()
#else #else
......
...@@ -221,6 +221,10 @@ ANGLE_INLINE angle::Result ContextGL::setDrawArraysState(const gl::Context *cont ...@@ -221,6 +221,10 @@ ANGLE_INLINE angle::Result ContextGL::setDrawArraysState(const gl::Context *cont
ANGLE_TRY(vaoGL->syncClientSideData(context, program->getActiveAttribLocationsMask(), first, ANGLE_TRY(vaoGL->syncClientSideData(context, program->getActiveAttribLocationsMask(), first,
count, instanceCount)); count, instanceCount));
#if defined(ANGLE_STATE_VALIDATION_ENABLED)
vaoGL->validateState();
#endif // ANGLE_STATE_VALIDATION_ENABLED
} }
return angle::Result::Continue; return angle::Result::Continue;
...@@ -252,6 +256,11 @@ ANGLE_INLINE angle::Result ContextGL::setDrawElementsState(const gl::Context *co ...@@ -252,6 +256,11 @@ ANGLE_INLINE angle::Result ContextGL::setDrawElementsState(const gl::Context *co
*outIndices = indices; *outIndices = indices;
} }
#if defined(ANGLE_STATE_VALIDATION_ENABLED)
const VertexArrayGL *vaoGL = GetImplAs<VertexArrayGL>(vao);
vaoGL->validateState();
#endif // ANGLE_STATE_VALIDATION_ENABLED
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -264,6 +273,10 @@ angle::Result ContextGL::drawArrays(const gl::Context *context, ...@@ -264,6 +273,10 @@ angle::Result ContextGL::drawArrays(const gl::Context *context,
const bool usesMultiview = program->usesMultiview(); const bool usesMultiview = program->usesMultiview();
const GLsizei instanceCount = usesMultiview ? program->getNumViews() : 0; const GLsizei instanceCount = usesMultiview ? program->getNumViews() : 0;
#if defined(ANGLE_STATE_VALIDATION_ENABLED)
validateState();
#endif
ANGLE_TRY(setDrawArraysState(context, first, count, instanceCount)); ANGLE_TRY(setDrawArraysState(context, first, count, instanceCount));
if (!usesMultiview) if (!usesMultiview)
{ {
...@@ -424,6 +437,10 @@ angle::Result ContextGL::drawElements(const gl::Context *context, ...@@ -424,6 +437,10 @@ angle::Result ContextGL::drawElements(const gl::Context *context,
const GLsizei instanceCount = usesMultiview ? program->getNumViews() : 0; const GLsizei instanceCount = usesMultiview ? program->getNumViews() : 0;
const void *drawIndexPtr = nullptr; const void *drawIndexPtr = nullptr;
#if defined(ANGLE_STATE_VALIDATION_ENABLED)
validateState();
#endif // ANGLE_STATE_VALIDATION_ENABLED
ANGLE_TRY(setDrawElementsState(context, count, type, indices, instanceCount, &drawIndexPtr)); ANGLE_TRY(setDrawElementsState(context, count, type, indices, instanceCount, &drawIndexPtr));
if (!usesMultiview) if (!usesMultiview)
{ {
...@@ -780,4 +797,10 @@ void ContextGL::invalidateTexture(gl::TextureType target) ...@@ -780,4 +797,10 @@ void ContextGL::invalidateTexture(gl::TextureType target)
mRenderer->getStateManager()->invalidateTexture(target); mRenderer->getStateManager()->invalidateTexture(target);
} }
void ContextGL::validateState() const
{
const StateManagerGL *stateManager = mRenderer->getStateManager();
stateManager->validateState();
}
} // namespace rx } // namespace rx
...@@ -242,6 +242,8 @@ class ContextGL : public ContextImpl ...@@ -242,6 +242,8 @@ class ContextGL : public ContextImpl
void invalidateTexture(gl::TextureType target) override; void invalidateTexture(gl::TextureType target) override;
void validateState() const;
private: private:
angle::Result setDrawArraysState(const gl::Context *context, angle::Result setDrawArraysState(const gl::Context *context,
GLint first, GLint first,
......
...@@ -34,6 +34,28 @@ ...@@ -34,6 +34,28 @@
namespace rx namespace rx
{ {
namespace
{
static void ValidateStateHelper(const FunctionsGL *functions,
const GLuint localValue,
const GLenum pname,
const char *localName,
const char *driverName)
{
GLint queryValue;
functions->getIntegerv(pname, &queryValue);
if (localValue != static_cast<GLuint>(queryValue))
{
WARN() << localName << " (" << localValue << ") != " << driverName << " (" << queryValue
<< ")";
// Re-add ASSERT: http://anglebug.com/3900
// ASSERT(false);
}
}
} // anonymous namespace
StateManagerGL::IndexedBufferBinding::IndexedBufferBinding() : offset(0), size(0), buffer(0) {} StateManagerGL::IndexedBufferBinding::IndexedBufferBinding() : offset(0), size(0), buffer(0) {}
StateManagerGL::StateManagerGL(const FunctionsGL *functions, StateManagerGL::StateManagerGL(const FunctionsGL *functions,
...@@ -2113,4 +2135,34 @@ void StateManagerGL::syncTransformFeedbackState(const gl::Context *context) ...@@ -2113,4 +2135,34 @@ void StateManagerGL::syncTransformFeedbackState(const gl::Context *context)
mCurrentTransformFeedback = nullptr; mCurrentTransformFeedback = nullptr;
} }
} }
void StateManagerGL::validateState() const
{
// Current program
ValidateStateHelper(mFunctions, mProgram, GL_CURRENT_PROGRAM, "mProgram", "GL_CURRENT_PROGRAM");
// Buffers
for (gl::BufferBinding bindingType : angle::AllEnums<gl::BufferBinding>())
{
// These binding types need compute support to be queried
if (bindingType == gl::BufferBinding::AtomicCounter ||
bindingType == gl::BufferBinding::DispatchIndirect ||
bindingType == gl::BufferBinding::ShaderStorage)
{
if (!nativegl::SupportsCompute(mFunctions))
{
continue;
}
}
GLenum bindingTypeGL = nativegl::GetBufferBindingQuery(bindingType);
std::string localName = "mBuffers[" + ToString(bindingType) + "]";
ValidateStateHelper(mFunctions, mBuffers[bindingType], bindingTypeGL, localName.c_str(),
nativegl::GetBufferBindingString(bindingType).c_str());
}
// Vertex array object
ValidateStateHelper(mFunctions, mVAO, GL_VERTEX_ARRAY_BINDING, "mVAO",
"GL_VERTEX_ARRAY_BINDING");
}
} // namespace rx } // namespace rx
...@@ -184,6 +184,8 @@ class StateManagerGL final : angle::NonCopyable ...@@ -184,6 +184,8 @@ class StateManagerGL final : angle::NonCopyable
} }
GLuint getBufferID(gl::BufferBinding binding) const { return mBuffers[binding]; } GLuint getBufferID(gl::BufferBinding binding) const { return mBuffers[binding]; }
void validateState() const;
private: private:
void setTextureCubemapSeamlessEnabled(bool enabled); void setTextureCubemapSeamlessEnabled(bool enabled);
......
...@@ -49,6 +49,41 @@ GLuint GetAdjustedDivisor(GLuint numViews, GLuint divisor) ...@@ -49,6 +49,41 @@ GLuint GetAdjustedDivisor(GLuint numViews, GLuint divisor)
return numViews * divisor; return numViews * divisor;
} }
static void ValidateStateHelperGetIntegerv(const FunctionsGL *functions,
const GLuint localValue,
const GLenum pname,
const char *localName,
const char *driverName)
{
GLint queryValue;
functions->getIntegerv(pname, &queryValue);
if (localValue != static_cast<GLuint>(queryValue))
{
WARN() << localName << " (" << localValue << ") != " << driverName << " (" << queryValue
<< ")";
// Re-add ASSERT: http://anglebug.com/3900
// ASSERT(false);
}
}
static void ValidateStateHelperGetVertexAttribiv(const FunctionsGL *functions,
const GLint index,
const GLuint localValue,
const GLenum pname,
const char *localName,
const char *driverName)
{
GLint queryValue;
functions->getVertexAttribiv(index, pname, &queryValue);
if (localValue != static_cast<GLuint>(queryValue))
{
WARN() << localName << "[" << index << "] (" << localValue << ") != " << driverName << "["
<< index << "] (" << queryValue << ")";
// Re-add ASSERT: http://anglebug.com/3900
// ASSERT(false);
}
}
} // anonymous namespace } // anonymous namespace
VertexArrayGL::VertexArrayGL(const VertexArrayState &state, VertexArrayGL::VertexArrayGL(const VertexArrayState &state,
...@@ -740,4 +775,100 @@ void VertexArrayGL::applyActiveAttribLocationsMask(const gl::AttributesMask &act ...@@ -740,4 +775,100 @@ void VertexArrayGL::applyActiveAttribLocationsMask(const gl::AttributesMask &act
} }
} }
void VertexArrayGL::validateState() const
{
// Ensure this vao is currently bound
ValidateStateHelperGetIntegerv(mFunctions, mVertexArrayID, GL_VERTEX_ARRAY_BINDING,
"mVertexArrayID", "GL_VERTEX_ARRAY_BINDING");
// Element array buffer
if (mAppliedElementArrayBuffer.get() == nullptr)
{
ValidateStateHelperGetIntegerv(
mFunctions, mStreamingElementArrayBuffer, GL_ELEMENT_ARRAY_BUFFER_BINDING,
"mAppliedElementArrayBuffer", "GL_ELEMENT_ARRAY_BUFFER_BINDING");
}
else
{
const BufferGL *bufferGL = GetImplAs<BufferGL>(mAppliedElementArrayBuffer.get());
ValidateStateHelperGetIntegerv(
mFunctions, bufferGL->getBufferID(), GL_ELEMENT_ARRAY_BUFFER_BINDING,
"mAppliedElementArrayBuffer", "GL_ELEMENT_ARRAY_BUFFER_BINDING");
}
// ValidateStateHelperGetIntegerv but with > comparison instead of !=
GLint queryValue;
mFunctions->getIntegerv(GL_MAX_VERTEX_ATTRIBS, &queryValue);
if (mAppliedAttributes.size() > static_cast<GLuint>(queryValue))
{
WARN() << "mAppliedAttributes.size() (" << mAppliedAttributes.size()
<< ") > GL_MAX_VERTEX_ATTRIBS (" << queryValue << ")";
// Re-add ASSERT: http://anglebug.com/3900
// ASSERT(false);
}
// Check each applied attribute/binding
for (GLuint index = 0; index < mAppliedAttributes.size(); index++)
{
VertexAttribute &attribute = mAppliedAttributes[index];
ASSERT(attribute.bindingIndex < mAppliedBindings.size());
VertexBinding &binding = mAppliedBindings[attribute.bindingIndex];
ValidateStateHelperGetVertexAttribiv(
mFunctions, index, attribute.enabled, GL_VERTEX_ATTRIB_ARRAY_ENABLED,
"mAppliedAttributes.enabled", "GL_VERTEX_ATTRIB_ARRAY_ENABLED");
if (attribute.enabled)
{
// Applied attributes
ASSERT(attribute.format);
ValidateStateHelperGetVertexAttribiv(
mFunctions, index, ToGLenum(attribute.format->vertexAttribType),
GL_VERTEX_ATTRIB_ARRAY_TYPE, "mAppliedAttributes.format->vertexAttribType",
"GL_VERTEX_ATTRIB_ARRAY_TYPE");
ValidateStateHelperGetVertexAttribiv(
mFunctions, index, attribute.format->channelCount, GL_VERTEX_ATTRIB_ARRAY_SIZE,
"attribute.format->channelCount", "GL_VERTEX_ATTRIB_ARRAY_SIZE");
ValidateStateHelperGetVertexAttribiv(
mFunctions, index, attribute.format->isNorm(), GL_VERTEX_ATTRIB_ARRAY_NORMALIZED,
"attribute.format->isNorm()", "GL_VERTEX_ATTRIB_ARRAY_NORMALIZED");
ValidateStateHelperGetVertexAttribiv(
mFunctions, index, attribute.format->isPureInt(), GL_VERTEX_ATTRIB_ARRAY_INTEGER,
"attribute.format->isPureInt()", "GL_VERTEX_ATTRIB_ARRAY_INTEGER");
if (supportVertexAttribBinding())
{
ValidateStateHelperGetVertexAttribiv(
mFunctions, index, attribute.relativeOffset, GL_VERTEX_ATTRIB_RELATIVE_OFFSET,
"attribute.relativeOffset", "GL_VERTEX_ATTRIB_RELATIVE_OFFSET");
ValidateStateHelperGetVertexAttribiv(
mFunctions, index, attribute.bindingIndex, GL_VERTEX_ATTRIB_BINDING,
"attribute.bindingIndex", "GL_VERTEX_ATTRIB_BINDING");
}
// Applied bindings
if (binding.getBuffer().get() == nullptr)
{
ValidateStateHelperGetVertexAttribiv(
mFunctions, index, mStreamingArrayBuffer, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING,
"mAppliedBindings.bufferID", "GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING");
}
else
{
const BufferGL *arrayBufferGL = GetImplAs<BufferGL>(binding.getBuffer().get());
ASSERT(arrayBufferGL);
ValidateStateHelperGetVertexAttribiv(
mFunctions, index, arrayBufferGL->getBufferID(),
GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, "mAppliedBindings.bufferID",
"GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING");
ValidateStateHelperGetVertexAttribiv(
mFunctions, index, binding.getStride(), GL_VERTEX_ATTRIB_ARRAY_STRIDE,
"binding.getStride()", "GL_VERTEX_ATTRIB_ARRAY_STRIDE");
ValidateStateHelperGetVertexAttribiv(
mFunctions, index, binding.getDivisor(), GL_VERTEX_ATTRIB_ARRAY_DIVISOR,
"binding.getDivisor()", "GL_VERTEX_ATTRIB_ARRAY_DIVISOR");
}
}
}
}
} // namespace rx } // namespace rx
...@@ -56,6 +56,8 @@ class VertexArrayGL : public VertexArrayImpl ...@@ -56,6 +56,8 @@ class VertexArrayGL : public VertexArrayImpl
void applyNumViewsToDivisor(int numViews); void applyNumViewsToDivisor(int numViews);
void applyActiveAttribLocationsMask(const gl::AttributesMask &activeMask); void applyActiveAttribLocationsMask(const gl::AttributesMask &activeMask);
void validateState() const;
private: private:
angle::Result syncDrawState(const gl::Context *context, angle::Result syncDrawState(const gl::Context *context,
const gl::AttributesMask &activeAttributesMask, const gl::AttributesMask &activeAttributesMask,
......
...@@ -963,13 +963,7 @@ void GenerateCaps(const FunctionsGL *functions, ...@@ -963,13 +963,7 @@ void GenerateCaps(const FunctionsGL *functions,
LimitVersion(maxSupportedESVersion, gl::Version(3, 0)); LimitVersion(maxSupportedESVersion, gl::Version(3, 0));
} }
// OpenGL 4.2 is required for GL_ARB_compute_shader, some platform drivers have the extension, if (nativegl::SupportsCompute(functions))
// but their maximum supported GL versions are less than 4.2. Explicitly limit the minimum
// GL version to 4.2.
if (functions->isAtLeastGL(gl::Version(4, 3)) || functions->isAtLeastGLES(gl::Version(3, 1)) ||
(functions->isAtLeastGL(gl::Version(4, 2)) &&
functions->hasGLExtension("GL_ARB_compute_shader") &&
functions->hasGLExtension("GL_ARB_shader_storage_buffer_object")))
{ {
for (GLuint index = 0u; index < 3u; ++index) for (GLuint index = 0u; index < 3u; ++index)
{ {
...@@ -1572,6 +1566,18 @@ void InitializeFrontendFeatures(const FunctionsGL *functions, angle::FrontendFea ...@@ -1572,6 +1566,18 @@ void InitializeFrontendFeatures(const FunctionsGL *functions, angle::FrontendFea
namespace nativegl namespace nativegl
{ {
bool SupportsCompute(const FunctionsGL *functions)
{
// OpenGL 4.2 is required for GL_ARB_compute_shader, some platform drivers have the extension,
// but their maximum supported GL versions are less than 4.2. Explicitly limit the minimum
// GL version to 4.2.
return (functions->isAtLeastGL(gl::Version(4, 3)) ||
functions->isAtLeastGLES(gl::Version(3, 1)) ||
(functions->isAtLeastGL(gl::Version(4, 2)) &&
functions->hasGLExtension("GL_ARB_compute_shader") &&
functions->hasGLExtension("GL_ARB_shader_storage_buffer_object")));
}
bool SupportsFenceSync(const FunctionsGL *functions) bool SupportsFenceSync(const FunctionsGL *functions)
{ {
return functions->isAtLeastGL(gl::Version(3, 2)) || functions->hasGLExtension("GL_ARB_sync") || return functions->isAtLeastGL(gl::Version(3, 2)) || functions->hasGLExtension("GL_ARB_sync") ||
...@@ -1669,6 +1675,47 @@ GLenum GetTextureBindingQuery(gl::TextureType textureType) ...@@ -1669,6 +1675,47 @@ GLenum GetTextureBindingQuery(gl::TextureType textureType)
} }
} }
GLenum GetBufferBindingQuery(gl::BufferBinding bufferBinding)
{
switch (bufferBinding)
{
case gl::BufferBinding::Array:
return GL_ARRAY_BUFFER_BINDING;
case gl::BufferBinding::AtomicCounter:
return GL_ATOMIC_COUNTER_BUFFER_BINDING;
case gl::BufferBinding::CopyRead:
return GL_COPY_READ_BUFFER_BINDING;
case gl::BufferBinding::CopyWrite:
return GL_COPY_WRITE_BUFFER_BINDING;
case gl::BufferBinding::DispatchIndirect:
return GL_DISPATCH_INDIRECT_BUFFER_BINDING;
case gl::BufferBinding::DrawIndirect:
return GL_DRAW_INDIRECT_BUFFER_BINDING;
case gl::BufferBinding::ElementArray:
return GL_ELEMENT_ARRAY_BUFFER_BINDING;
case gl::BufferBinding::PixelPack:
return GL_PIXEL_PACK_BUFFER_BINDING;
case gl::BufferBinding::PixelUnpack:
return GL_PIXEL_UNPACK_BUFFER_BINDING;
case gl::BufferBinding::ShaderStorage:
return GL_SHADER_STORAGE_BUFFER_BINDING;
case gl::BufferBinding::TransformFeedback:
return GL_TRANSFORM_FEEDBACK_BUFFER_BINDING;
case gl::BufferBinding::Uniform:
return GL_UNIFORM_BUFFER_BINDING;
default:
UNREACHABLE();
return 0;
}
}
std::string GetBufferBindingString(gl::BufferBinding bufferBinding)
{
std::ostringstream os;
os << bufferBinding << "_BINDING";
return os.str();
}
} // namespace nativegl } // namespace nativegl
const FunctionsGL *GetFunctionsGL(const gl::Context *context) const FunctionsGL *GetFunctionsGL(const gl::Context *context)
......
...@@ -96,6 +96,7 @@ void InitializeFrontendFeatures(const FunctionsGL *functions, angle::FrontendFea ...@@ -96,6 +96,7 @@ void InitializeFrontendFeatures(const FunctionsGL *functions, angle::FrontendFea
namespace nativegl namespace nativegl
{ {
bool SupportsCompute(const FunctionsGL *functions);
bool SupportsFenceSync(const FunctionsGL *functions); bool SupportsFenceSync(const FunctionsGL *functions);
bool SupportsOcclusionQueries(const FunctionsGL *functions); bool SupportsOcclusionQueries(const FunctionsGL *functions);
bool SupportsNativeRendering(const FunctionsGL *functions, bool SupportsNativeRendering(const FunctionsGL *functions,
...@@ -105,6 +106,8 @@ bool SupportsTexImage(gl::TextureType type); ...@@ -105,6 +106,8 @@ bool SupportsTexImage(gl::TextureType type);
bool UseTexImage2D(gl::TextureType textureType); bool UseTexImage2D(gl::TextureType textureType);
bool UseTexImage3D(gl::TextureType textureType); bool UseTexImage3D(gl::TextureType textureType);
GLenum GetTextureBindingQuery(gl::TextureType textureType); GLenum GetTextureBindingQuery(gl::TextureType textureType);
GLenum GetBufferBindingQuery(gl::BufferBinding bufferBinding);
std::string GetBufferBindingString(gl::BufferBinding bufferBinding);
} // namespace nativegl } // namespace nativegl
bool CanMapBufferForRead(const FunctionsGL *functions); bool CanMapBufferForRead(const FunctionsGL *functions);
......
...@@ -6552,7 +6552,11 @@ TEST_P(GLSLTest_ES3, InitSameNameArray) ...@@ -6552,7 +6552,11 @@ TEST_P(GLSLTest_ES3, InitSameNameArray)
TEST_P(GLSLTest, FragData) TEST_P(GLSLTest, FragData)
{ {
// Ensures that we don't regress and emit Vulkan layer warnings. // Ensures that we don't regress and emit Vulkan layer warnings.
treatPlatformWarningsAsErrors(); // TODO(jonahr): http://anglebug.com/3900 - Remove check once warnings are cleaned up
if (IsVulkan())
{
treatPlatformWarningsAsErrors();
}
constexpr char kFS[] = R"(void main() { gl_FragData[0] = vec4(1, 0, 0, 1); })"; constexpr char kFS[] = R"(void main() { gl_FragData[0] = vec4(1, 0, 0, 1); })";
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS);
......
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