| // | |
| // 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())); |