Add an extension to share textures at the display level.
BUG=angleproject:1639
Change-Id: If9140142ebce89f33921c13d9d212c17d1894162
Reviewed-on: https://chromium-review.googlesource.com/437618
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Caps.cpp b/src/libANGLE/Caps.cpp
index b16f27e..47b45f3 100644
--- a/src/libANGLE/Caps.cpp
+++ b/src/libANGLE/Caps.cpp
@@ -1032,7 +1032,8 @@
createContextBindGeneratesResource(false),
swapBuffersWithDamage(false),
pixelFormatFloat(false),
- surfacelessContext(false)
+ surfacelessContext(false),
+ displayTextureShareGroup(false)
{
}
@@ -1072,6 +1073,7 @@
InsertExtensionString("EGL_EXT_swap_buffers_with_damage", swapBuffersWithDamage, &extensionStrings);
InsertExtensionString("EGL_EXT_pixel_format_float", pixelFormatFloat, &extensionStrings);
InsertExtensionString("EGL_KHR_surfaceless_context", surfacelessContext, &extensionStrings);
+ InsertExtensionString("EGL_ANGLE_display_texture_share_group", displayTextureShareGroup, &extensionStrings);
// TODO(jmadill): Enable this when complete.
//InsertExtensionString("KHR_create_context_no_error", createContextNoError, &extensionStrings);
// clang-format on
diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h
index 1b64f1a..31fb003 100644
--- a/src/libANGLE/Caps.h
+++ b/src/libANGLE/Caps.h
@@ -646,6 +646,9 @@
// EGL_KHR_surfaceless_context
bool surfacelessContext;
+
+ // EGL_ANGLE_display_texture_share_group
+ bool displayTextureShareGroup;
};
struct DeviceExtensions
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 96c7c8a..6f04a4c 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -236,10 +236,12 @@
Context::Context(rx::EGLImplFactory *implFactory,
const egl::Config *config,
const Context *shareContext,
+ TextureManager *shareTextures,
const egl::AttributeMap &attribs,
const egl::DisplayExtensions &displayExtensions)
: ValidationContext(shareContext,
+ shareTextures,
GetClientVersion(attribs),
&mGLState,
mCaps,
diff --git a/src/libANGLE/Context.h b/src/libANGLE/Context.h
index 7037c9d..c1dd093 100644
--- a/src/libANGLE/Context.h
+++ b/src/libANGLE/Context.h
@@ -61,6 +61,7 @@
Context(rx::EGLImplFactory *implFactory,
const egl::Config *config,
const Context *shareContext,
+ TextureManager *shareTextures,
const egl::AttributeMap &attribs,
const egl::DisplayExtensions &displayExtensions);
diff --git a/src/libANGLE/ContextState.cpp b/src/libANGLE/ContextState.cpp
index 6a24816..c5fb61c 100644
--- a/src/libANGLE/ContextState.cpp
+++ b/src/libANGLE/ContextState.cpp
@@ -36,10 +36,34 @@
}
}
+TextureManager *AllocateOrGetSharedTextureManager(const ContextState *shareContextState,
+ TextureManager *shareTextures,
+ ContextStateMember<TextureManager> member)
+{
+ if (shareContextState)
+ {
+ TextureManager *textureManager = (*shareContextState).*member;
+ ASSERT(shareTextures == nullptr || textureManager == shareTextures);
+ textureManager->addRef();
+ return textureManager;
+ }
+ else if (shareTextures)
+ {
+ TextureManager *textureManager = shareTextures;
+ textureManager->addRef();
+ return textureManager;
+ }
+ else
+ {
+ return new TextureManager();
+ }
+}
+
} // anonymous namespace
ContextState::ContextState(uintptr_t contextIn,
const ContextState *shareContextState,
+ TextureManager *shareTextures,
const Version &clientVersion,
State *stateIn,
const Caps &capsIn,
@@ -56,7 +80,9 @@
mBuffers(AllocateOrGetSharedResourceManager(shareContextState, &ContextState::mBuffers)),
mShaderPrograms(
AllocateOrGetSharedResourceManager(shareContextState, &ContextState::mShaderPrograms)),
- mTextures(AllocateOrGetSharedResourceManager(shareContextState, &ContextState::mTextures)),
+ mTextures(AllocateOrGetSharedTextureManager(shareContextState,
+ shareTextures,
+ &ContextState::mTextures)),
mRenderbuffers(
AllocateOrGetSharedResourceManager(shareContextState, &ContextState::mRenderbuffers)),
mSamplers(AllocateOrGetSharedResourceManager(shareContextState, &ContextState::mSamplers)),
@@ -85,6 +111,7 @@
}
ValidationContext::ValidationContext(const ValidationContext *shareContext,
+ TextureManager *shareTextures,
const Version &clientVersion,
State *state,
const Caps &caps,
@@ -94,13 +121,15 @@
bool skipValidation)
: mState(reinterpret_cast<uintptr_t>(this),
shareContext ? &shareContext->mState : nullptr,
+ shareTextures,
clientVersion,
state,
caps,
textureCaps,
extensions,
limitations),
- mSkipValidation(skipValidation)
+ mSkipValidation(skipValidation),
+ mDisplayTextureShareGroup(shareTextures != nullptr)
{
}
@@ -673,4 +702,9 @@
return mState.mFramebuffers->isFramebufferGenerated(framebuffer);
}
+bool ValidationContext::usingDisplayTextureShareGroup() const
+{
+ return mDisplayTextureShareGroup;
+}
+
} // namespace gl
diff --git a/src/libANGLE/ContextState.h b/src/libANGLE/ContextState.h
index 5c384a2..7af0e9c 100644
--- a/src/libANGLE/ContextState.h
+++ b/src/libANGLE/ContextState.h
@@ -35,6 +35,7 @@
public:
ContextState(uintptr_t context,
const ContextState *shareContextState,
+ TextureManager *shareTextures,
const Version &clientVersion,
State *state,
const Caps &caps,
@@ -55,6 +56,8 @@
const TextureCaps &getTextureCap(GLenum internalFormat) const;
+ bool usingDisplayTextureShareGroup() const;
+
private:
friend class Context;
friend class ValidationContext;
@@ -81,6 +84,7 @@
{
public:
ValidationContext(const ValidationContext *shareContext,
+ TextureManager *shareTextures,
const Version &clientVersion,
State *state,
const Caps &caps,
@@ -115,9 +119,12 @@
bool isRenderbufferGenerated(GLuint renderbuffer) const;
bool isFramebufferGenerated(GLuint framebuffer) const;
+ bool usingDisplayTextureShareGroup() const;
+
protected:
ContextState mState;
bool mSkipValidation;
+ bool mDisplayTextureShareGroup;
};
} // namespace gl
diff --git a/src/libANGLE/Display.cpp b/src/libANGLE/Display.cpp
index 2a6b65b..0cb7621 100644
--- a/src/libANGLE/Display.cpp
+++ b/src/libANGLE/Display.cpp
@@ -29,6 +29,7 @@
#include "libANGLE/Image.h"
#include "libANGLE/Surface.h"
#include "libANGLE/Stream.h"
+#include "libANGLE/ResourceManager.h"
#include "libANGLE/renderer/DisplayImpl.h"
#include "libANGLE/renderer/ImageImpl.h"
#include "third_party/trace_event/trace_event.h"
@@ -358,7 +359,8 @@
mDisplayExtensionString(),
mVendorString(),
mDevice(eglDevice),
- mPlatform(platform)
+ mPlatform(platform),
+ mTextureManager(nullptr)
{
}
@@ -462,6 +464,9 @@
ASSERT(mDevice != nullptr);
}
+ ASSERT(mTextureManager == nullptr);
+ mTextureManager = new gl::TextureManager();
+
mInitialized = true;
return egl::Error(EGL_SUCCESS);
@@ -469,6 +474,12 @@
void Display::terminate()
{
+ if (mTextureManager)
+ {
+ mTextureManager->release();
+ mTextureManager = nullptr;
+ }
+
makeCurrent(nullptr, nullptr, nullptr);
while (!mContextSet.empty())
@@ -673,8 +684,12 @@
ANGLE_TRY(restoreLostDevice());
}
- gl::Context *context =
- new gl::Context(mImplementation, configuration, shareContext, attribs, mDisplayExtensions);
+ bool usingDisplayTextureShareGroup =
+ attribs.get(EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_FALSE) == EGL_TRUE;
+ gl::TextureManager *shareTextures = usingDisplayTextureShareGroup ? mTextureManager : nullptr;
+
+ gl::Context *context = new gl::Context(mImplementation, configuration, shareContext,
+ shareTextures, attribs, mDisplayExtensions);
ASSERT(context != nullptr);
mContextSet.insert(context);
diff --git a/src/libANGLE/Display.h b/src/libANGLE/Display.h
index 5b61ce7..767e322 100644
--- a/src/libANGLE/Display.h
+++ b/src/libANGLE/Display.h
@@ -24,6 +24,7 @@
namespace gl
{
class Context;
+class TextureManager;
}
namespace rx
@@ -172,6 +173,8 @@
Device *mDevice;
EGLenum mPlatform;
angle::LoggingAnnotator mAnnotator;
+
+ gl::TextureManager *mTextureManager;
};
}
diff --git a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
index 357b2bd..a50ce00 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
@@ -1099,6 +1099,9 @@
outExtensions->flexibleSurfaceCompatibility = true;
outExtensions->directComposition = !!mDCompModule;
+
+ // Contexts are virtualized so textures can be shared globally
+ outExtensions->displayTextureShareGroup = true;
}
gl::Error Renderer11::flush()
diff --git a/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp b/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
index 4ec64fc..4d1b67f 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
+++ b/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
@@ -540,6 +540,9 @@
outExtensions->glRenderbufferImage = true;
outExtensions->flexibleSurfaceCompatibility = true;
+
+ // Contexts are virtualized so textures can be shared globally
+ outExtensions->displayTextureShareGroup = true;
}
void Renderer9::startScene()
diff --git a/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm b/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm
index 41df548..e1e344e 100644
--- a/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm
+++ b/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm
@@ -246,6 +246,9 @@
void DisplayCGL::generateExtensions(egl::DisplayExtensions *outExtensions) const
{
outExtensions->surfacelessContext = true;
+
+ // Contexts are virtualized so textures can be shared globally
+ outExtensions->displayTextureShareGroup = true;
}
void DisplayCGL::generateCaps(egl::Caps *outCaps) const
diff --git a/src/libANGLE/renderer/gl/egl/DisplayEGL.cpp b/src/libANGLE/renderer/gl/egl/DisplayEGL.cpp
index 34ca22d..d7215d0 100644
--- a/src/libANGLE/renderer/gl/egl/DisplayEGL.cpp
+++ b/src/libANGLE/renderer/gl/egl/DisplayEGL.cpp
@@ -102,6 +102,9 @@
mEGL->hasExtension("EGL_EXT_create_context_robustness");
outExtensions->postSubBuffer = false; // Since SurfaceEGL::postSubBuffer is not implemented
+
+ // Contexts are virtualized so textures can be shared globally
+ outExtensions->displayTextureShareGroup = true;
}
void DisplayEGL::generateCaps(egl::Caps *outCaps) const
diff --git a/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp b/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp
index 3654e09..858481b 100644
--- a/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp
+++ b/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp
@@ -891,6 +891,9 @@
void DisplayGLX::generateExtensions(egl::DisplayExtensions *outExtensions) const
{
outExtensions->createContextRobustness = mHasARBCreateContextRobustness;
+
+ // Contexts are virtualized so textures can be shared globally
+ outExtensions->displayTextureShareGroup = true;
}
void DisplayGLX::generateCaps(egl::Caps *outCaps) const
diff --git a/src/libANGLE/renderer/gl/wgl/DisplayWGL.cpp b/src/libANGLE/renderer/gl/wgl/DisplayWGL.cpp
index f135cc7..8eb51b8 100644
--- a/src/libANGLE/renderer/gl/wgl/DisplayWGL.cpp
+++ b/src/libANGLE/renderer/gl/wgl/DisplayWGL.cpp
@@ -660,6 +660,9 @@
outExtensions->createContextRobustness = mHasRobustness;
outExtensions->d3dTextureClientBuffer = mFunctionsWGL->hasExtension("WGL_NV_DX_interop2");
+
+ // Contexts are virtualized so textures can be shared globally
+ outExtensions->displayTextureShareGroup = true;
}
void DisplayWGL::generateCaps(egl::Caps *outCaps) const
diff --git a/src/libANGLE/validationEGL.cpp b/src/libANGLE/validationEGL.cpp
index 1e6bfe3..83e41a0 100644
--- a/src/libANGLE/validationEGL.cpp
+++ b/src/libANGLE/validationEGL.cpp
@@ -427,8 +427,30 @@
}
break;
+ case EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE:
+ if (!display->getExtensions().displayTextureShareGroup)
+ {
+ return Error(EGL_BAD_ATTRIBUTE,
+ "Attribute EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE requires "
+ "EGL_ANGLE_display_texture_share_group.");
+ }
+ if (value != EGL_TRUE && value != EGL_FALSE)
+ {
+ return Error(
+ EGL_BAD_ATTRIBUTE,
+ "EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE must be EGL_TRUE or EGL_FALSE.");
+ }
+ if (shareContext &&
+ (shareContext->usingDisplayTextureShareGroup() != (value == EGL_TRUE)))
+ {
+ return Error(EGL_BAD_ATTRIBUTE,
+ "All contexts within a share group must be created with the same "
+ "value of EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE.");
+ }
+ break;
+
default:
- return Error(EGL_BAD_ATTRIBUTE);
+ return Error(EGL_BAD_ATTRIBUTE, "Unknown attribute.");
}
}
diff --git a/src/libANGLE/validationES_unittest.cpp b/src/libANGLE/validationES_unittest.cpp
index 7a23026..586bd71 100644
--- a/src/libANGLE/validationES_unittest.cpp
+++ b/src/libANGLE/validationES_unittest.cpp
@@ -31,6 +31,7 @@
{
public:
MockValidationContext(const ValidationContext *shareContext,
+ TextureManager *shareTextures,
const Version &version,
State *state,
const Caps &caps,
@@ -39,6 +40,7 @@
const Limitations &limitations,
bool skipValidation)
: ValidationContext(shareContext,
+ shareTextures,
version,
state,
caps,
@@ -97,8 +99,8 @@
state.setDrawFramebufferBinding(framebuffer);
state.setProgram(program);
- NiceMock<MockValidationContext> testContext(nullptr, Version(3, 0), &state, caps, textureCaps,
- extensions, limitations, false);
+ NiceMock<MockValidationContext> testContext(nullptr, nullptr, Version(3, 0), &state, caps,
+ textureCaps, extensions, limitations, false);
// Set the expectation for the validation error here.
Error expectedError(GL_INVALID_OPERATION, g_ExceedsMaxElementErrorMessage);
diff --git a/src/tests/egl_tests/EGLContextSharingTest.cpp b/src/tests/egl_tests/EGLContextSharingTest.cpp
index fc66b79..ce1bd83 100644
--- a/src/tests/egl_tests/EGLContextSharingTest.cpp
+++ b/src/tests/egl_tests/EGLContextSharingTest.cpp
@@ -82,6 +82,128 @@
ASSERT_GL_NO_ERROR();
}
+// Tests the creation of contexts using EGL_ANGLE_display_texture_share_group
+TEST_P(EGLContextSharingTest, DisplayShareGroupContextCreation)
+{
+ EGLDisplay display = getEGLWindow()->getDisplay();
+ EGLConfig config = getEGLWindow()->getConfig();
+
+ const EGLint inShareGroupContextAttribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_TRUE, EGL_NONE};
+
+ // Test creating two contexts in the global share group
+ mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
+ mContexts[1] = eglCreateContext(display, config, mContexts[1], inShareGroupContextAttribs);
+
+ if (!ANGLETest::eglDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group"))
+ {
+ // Make sure an error is generated and early-exit
+ ASSERT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
+ ASSERT_EQ(EGL_NO_CONTEXT, mContexts[0]);
+ return;
+ }
+
+ ASSERT_EGL_SUCCESS();
+
+ ASSERT_NE(EGL_NO_CONTEXT, mContexts[0]);
+ ASSERT_NE(EGL_NO_CONTEXT, mContexts[1]);
+ eglDestroyContext(display, mContexts[0]);
+
+ // Try creating a context that is not in the global share group but tries to share with a
+ // context that is
+ const EGLint notInShareGroupContextAttribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_FALSE, EGL_NONE};
+ mContexts[1] = eglCreateContext(display, config, mContexts[1], notInShareGroupContextAttribs);
+ ASSERT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
+ ASSERT_TRUE(mContexts[1] == EGL_NO_CONTEXT);
+}
+
+// Tests the sharing of textures using EGL_ANGLE_display_texture_share_group
+TEST_P(EGLContextSharingTest, DisplayShareGroupObjectSharing)
+{
+ EGLDisplay display = getEGLWindow()->getDisplay();
+ if (!ANGLETest::eglDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group"))
+ {
+ std::cout << "Test skipped because EGL_ANGLE_display_texture_share_group is not present."
+ << std::endl;
+ return;
+ }
+
+ EGLConfig config = getEGLWindow()->getConfig();
+ EGLSurface surface = getEGLWindow()->getSurface();
+
+ const EGLint inShareGroupContextAttribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_TRUE, EGL_NONE};
+
+ // Create two contexts in the global share group but not in the same context share group
+ mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
+ mContexts[1] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
+
+ ASSERT_EGL_SUCCESS();
+
+ ASSERT_NE(EGL_NO_CONTEXT, mContexts[0]);
+ ASSERT_NE(EGL_NO_CONTEXT, mContexts[1]);
+
+ ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
+ ASSERT_EGL_SUCCESS();
+
+ // Create a texture and buffer in ctx 0
+ GLuint textureFromCtx0 = 0;
+ glGenTextures(1, &textureFromCtx0);
+ glBindTexture(GL_TEXTURE_2D, textureFromCtx0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
+
+ GLuint bufferFromCtx0 = 0;
+ glGenBuffers(1, &bufferFromCtx0);
+ glBindBuffer(GL_ARRAY_BUFFER, bufferFromCtx0);
+ glBufferData(GL_ARRAY_BUFFER, 1, nullptr, GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ ASSERT_GL_TRUE(glIsBuffer(bufferFromCtx0));
+
+ ASSERT_GL_NO_ERROR();
+
+ // Switch to context 1 and verify that the texture is accessible but the buffer is not
+ ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[1]));
+ ASSERT_EGL_SUCCESS();
+
+ ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
+
+ ASSERT_GL_FALSE(glIsBuffer(bufferFromCtx0));
+ glDeleteBuffers(1, &bufferFromCtx0);
+ ASSERT_GL_NO_ERROR();
+
+ // Call readpixels on the texture to verify that the backend has proper support
+ GLuint fbo = 0;
+ glGenFramebuffers(1, &fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureFromCtx0, 0);
+
+ GLubyte pixel[4];
+ glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+ ASSERT_GL_NO_ERROR();
+
+ glDeleteFramebuffers(1, &fbo);
+
+ glDeleteTextures(1, &textureFromCtx0);
+ ASSERT_GL_NO_ERROR();
+ ASSERT_GL_FALSE(glIsTexture(textureFromCtx0));
+
+ // Switch back to context 0 and delete the buffer
+ ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
+ ASSERT_EGL_SUCCESS();
+
+ ASSERT_GL_TRUE(glIsBuffer(bufferFromCtx0));
+ glDeleteBuffers(1, &bufferFromCtx0);
+ ASSERT_GL_NO_ERROR();
+}
+
} // anonymous namespace
-ANGLE_INSTANTIATE_TEST(EGLContextSharingTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL());
+ANGLE_INSTANTIATE_TEST(EGLContextSharingTest,
+ ES2_D3D9(),
+ ES2_D3D11(),
+ ES3_D3D11(),
+ ES2_OPENGL(),
+ ES3_OPENGL());
diff --git a/src/tests/test_utils/ANGLETest.h b/src/tests/test_utils/ANGLETest.h
index 07b2d2f..fbe4dbc 100644
--- a/src/tests/test_utils/ANGLETest.h
+++ b/src/tests/test_utils/ANGLETest.h
@@ -21,6 +21,11 @@
#include "shader_utils.h"
#include "system_utils.h"
+#define ASSERT_GL_TRUE(a) ASSERT_EQ(static_cast<GLboolean>(GL_TRUE), (a))
+#define ASSERT_GL_FALSE(a) ASSERT_EQ(static_cast<GLboolean>(GL_FALSE), (a))
+#define EXPECT_GL_TRUE(a) EXPECT_EQ(static_cast<GLboolean>(GL_TRUE), (a))
+#define EXPECT_GL_FALSE(a) EXPECT_EQ(static_cast<GLboolean>(GL_FALSE), (a))
+
#define EXPECT_GL_ERROR(err) EXPECT_EQ(static_cast<GLenum>(err), glGetError())
#define EXPECT_GL_NO_ERROR() EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError())