| // |
| // Copyright 2017 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| // EGLSurfacelessContextTest.cpp: |
| // Tests for the EGL_KHR_surfaceless_context and GL_OES_surfaceless_context |
| |
| #include <gtest/gtest.h> |
| |
| #include "test_utils/ANGLETest.h" |
| #include "test_utils/angle_test_configs.h" |
| #include "test_utils/gl_raii.h" |
| |
| using namespace angle; |
| |
| namespace |
| { |
| |
| class EGLSurfacelessContextTest : public ANGLETest |
| { |
| public: |
| EGLSurfacelessContextTest() : mDisplay(0) {} |
| |
| void testSetUp() override |
| { |
| EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE}; |
| mDisplay = eglGetPlatformDisplayEXT( |
| EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs); |
| ASSERT_TRUE(mDisplay != EGL_NO_DISPLAY); |
| |
| ASSERT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr)); |
| |
| int nConfigs = 0; |
| ASSERT_EGL_TRUE(eglGetConfigs(mDisplay, nullptr, 0, &nConfigs)); |
| ASSERT_TRUE(nConfigs != 0); |
| |
| int nReturnedConfigs = 0; |
| std::vector<EGLConfig> configs(nConfigs); |
| ASSERT_EGL_TRUE(eglGetConfigs(mDisplay, configs.data(), nConfigs, &nReturnedConfigs)); |
| ASSERT_TRUE(nConfigs == nReturnedConfigs); |
| |
| for (auto config : configs) |
| { |
| EGLint surfaceType; |
| eglGetConfigAttrib(mDisplay, config, EGL_SURFACE_TYPE, &surfaceType); |
| if (surfaceType & EGL_PBUFFER_BIT) |
| { |
| mConfig = config; |
| break; |
| } |
| } |
| ASSERT_NE(nullptr, mConfig); |
| } |
| |
| void testTearDown() override |
| { |
| eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| |
| if (mContext != EGL_NO_CONTEXT) |
| { |
| eglDestroyContext(mDisplay, mContext); |
| } |
| |
| if (mPbuffer != EGL_NO_SURFACE) |
| { |
| eglDestroySurface(mDisplay, mPbuffer); |
| } |
| |
| eglTerminate(mDisplay); |
| } |
| |
| protected: |
| EGLContext createContext() |
| { |
| const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; |
| |
| mContext = eglCreateContext(mDisplay, mConfig, EGL_NO_CONTEXT, contextAttribs); |
| EXPECT_TRUE(mContext != EGL_NO_CONTEXT); |
| return mContext; |
| } |
| |
| EGLSurface createPbuffer(int width, int height) |
| { |
| const EGLint pbufferAttribs[] = { |
| EGL_WIDTH, 500, EGL_HEIGHT, 500, EGL_NONE, |
| }; |
| mPbuffer = eglCreatePbufferSurface(mDisplay, mConfig, pbufferAttribs); |
| EXPECT_TRUE(mPbuffer != EGL_NO_SURFACE); |
| return mPbuffer; |
| } |
| |
| void createFramebuffer(GLFramebuffer *fbo, GLTexture *tex) const |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo->get()); |
| |
| glBindTexture(GL_TEXTURE_2D, tex->get()); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 500, 500, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex->get(), 0); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| } |
| |
| bool checkExtension(bool verbose = true) const |
| { |
| if (!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_surfaceless_context")) |
| { |
| if (verbose) |
| { |
| std::cout << "Test skipped because EGL_KHR_surfaceless_context is not available." |
| << std::endl; |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| EGLContext mContext = EGL_NO_CONTEXT; |
| EGLSurface mPbuffer = EGL_NO_SURFACE; |
| EGLConfig mConfig = 0; |
| EGLDisplay mDisplay = EGL_NO_DISPLAY; |
| }; |
| |
| // Test surfaceless MakeCurrent returns the correct value. |
| TEST_P(EGLSurfacelessContextTest, MakeCurrentSurfaceless) |
| { |
| EGLContext context = createContext(); |
| |
| if (eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context)) |
| { |
| ASSERT_TRUE(checkExtension(false)); |
| } |
| else |
| { |
| // The extension allows EGL_BAD_MATCH with the extension too, but ANGLE |
| // shouldn't do that. |
| ASSERT_FALSE(checkExtension(false)); |
| } |
| } |
| |
| // Test that the scissor and viewport are set correctly |
| TEST_P(EGLSurfacelessContextTest, DefaultScissorAndViewport) |
| { |
| if (!checkExtension()) |
| { |
| return; |
| } |
| |
| EGLContext context = createContext(); |
| ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context)); |
| |
| GLint scissor[4] = {1, 2, 3, 4}; |
| glGetIntegerv(GL_SCISSOR_BOX, scissor); |
| ASSERT_GL_NO_ERROR(); |
| ASSERT_EQ(0, scissor[0]); |
| ASSERT_EQ(0, scissor[1]); |
| ASSERT_EQ(0, scissor[2]); |
| ASSERT_EQ(0, scissor[3]); |
| |
| GLint viewport[4] = {1, 2, 3, 4}; |
| glGetIntegerv(GL_VIEWPORT, viewport); |
| ASSERT_GL_NO_ERROR(); |
| ASSERT_EQ(0, viewport[0]); |
| ASSERT_EQ(0, viewport[1]); |
| ASSERT_EQ(0, viewport[2]); |
| ASSERT_EQ(0, viewport[3]); |
| } |
| |
| // Test the CheckFramebufferStatus returns the correct value. |
| TEST_P(EGLSurfacelessContextTest, CheckFramebufferStatus) |
| { |
| if (!checkExtension()) |
| { |
| return; |
| } |
| |
| EGLContext context = createContext(); |
| ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context)); |
| |
| ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_UNDEFINED_OES, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| GLFramebuffer fbo; |
| GLTexture tex; |
| createFramebuffer(&fbo, &tex); |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo.get()); |
| ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| } |
| |
| // Test that clearing and readpixels work when done in an FBO. |
| TEST_P(EGLSurfacelessContextTest, ClearReadPixelsInFBO) |
| { |
| if (!checkExtension()) |
| { |
| return; |
| } |
| |
| EGLContext context = createContext(); |
| ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context)); |
| |
| GLFramebuffer fbo; |
| GLTexture tex; |
| createFramebuffer(&fbo, &tex); |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo.get()); |
| |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(250, 250, GLColor::red); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test clear+readpixels in an FBO in surfaceless and in the default FBO in a pbuffer |
| TEST_P(EGLSurfacelessContextTest, Switcheroo) |
| { |
| if (!checkExtension()) |
| { |
| return; |
| } |
| |
| EGLContext context = createContext(); |
| EGLSurface pbuffer = createPbuffer(500, 500); |
| |
| // We need to make the context current to do the one time setup of the FBO |
| ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context)); |
| GLFramebuffer fbo; |
| GLTexture tex; |
| createFramebuffer(&fbo, &tex); |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo.get()); |
| |
| for (int i = 0; i < 4; ++i) |
| { |
| // Clear to red in the FBO in surfaceless mode |
| ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context)); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo.get()); |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(250, 250, GLColor::red); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Clear to green in the pbuffer |
| ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, pbuffer, pbuffer, context)); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(250, 250, GLColor::green); |
| ASSERT_GL_NO_ERROR(); |
| } |
| } |
| |
| } // anonymous namespace |
| |
| ANGLE_INSTANTIATE_TEST(EGLSurfacelessContextTest, |
| WithNoFixture(ES2_D3D9()), |
| WithNoFixture(ES2_D3D11()), |
| WithNoFixture(ES2_OPENGL()), |
| WithNoFixture(ES2_VULKAN())); |