1 //
2 // Copyright 2017 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 
7 // EGLSurfacelessContextTest.cpp:
8 //   Tests for the EGL_KHR_surfaceless_context and GL_OES_surfaceless_context
9 
10 #include <gtest/gtest.h>
11 
12 #include "test_utils/ANGLETest.h"
13 #include "test_utils/angle_test_configs.h"
14 #include "test_utils/gl_raii.h"
15 
16 using namespace angle;
17 
18 namespace
19 {
20 
21 class EGLSurfacelessContextTest : public ANGLETest
22 {
23   public:
EGLSurfacelessContextTest()24     EGLSurfacelessContextTest() : mDisplay(0) {}
25 
testSetUp()26     void testSetUp() override
27     {
28         EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
29         mDisplay           = eglGetPlatformDisplayEXT(
30             EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
31         ASSERT_TRUE(mDisplay != EGL_NO_DISPLAY);
32 
33         ASSERT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
34 
35         int nConfigs = 0;
36         ASSERT_EGL_TRUE(eglGetConfigs(mDisplay, nullptr, 0, &nConfigs));
37         ASSERT_TRUE(nConfigs != 0);
38 
39         int nReturnedConfigs = 0;
40         std::vector<EGLConfig> configs(nConfigs);
41         ASSERT_EGL_TRUE(eglGetConfigs(mDisplay, configs.data(), nConfigs, &nReturnedConfigs));
42         ASSERT_TRUE(nConfigs == nReturnedConfigs);
43 
44         for (auto config : configs)
45         {
46             EGLint surfaceType;
47             eglGetConfigAttrib(mDisplay, config, EGL_SURFACE_TYPE, &surfaceType);
48             if (surfaceType & EGL_PBUFFER_BIT)
49             {
50                 mConfig           = config;
51                 mSupportsPbuffers = true;
52                 break;
53             }
54         }
55 
56         if (!mConfig)
57         {
58             mConfig = configs[0];
59         }
60 
61         ASSERT_NE(nullptr, mConfig);
62     }
63 
testTearDown()64     void testTearDown() override
65     {
66         eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
67 
68         if (mContext != EGL_NO_CONTEXT)
69         {
70             eglDestroyContext(mDisplay, mContext);
71         }
72 
73         if (mPbuffer != EGL_NO_SURFACE)
74         {
75             eglDestroySurface(mDisplay, mPbuffer);
76         }
77 
78         eglTerminate(mDisplay);
79     }
80 
81   protected:
createContext()82     EGLContext createContext()
83     {
84         const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
85 
86         mContext = eglCreateContext(mDisplay, mConfig, EGL_NO_CONTEXT, contextAttribs);
87         EXPECT_TRUE(mContext != EGL_NO_CONTEXT);
88         return mContext;
89     }
90 
createPbuffer(int width,int height)91     EGLSurface createPbuffer(int width, int height)
92     {
93         if (!mSupportsPbuffers)
94         {
95             return EGL_NO_SURFACE;
96         }
97 
98         const EGLint pbufferAttribs[] = {
99             EGL_WIDTH, 500, EGL_HEIGHT, 500, EGL_NONE,
100         };
101         mPbuffer = eglCreatePbufferSurface(mDisplay, mConfig, pbufferAttribs);
102         EXPECT_TRUE(mPbuffer != EGL_NO_SURFACE);
103         return mPbuffer;
104     }
105 
createFramebuffer(GLFramebuffer * fbo,GLTexture * tex) const106     void createFramebuffer(GLFramebuffer *fbo, GLTexture *tex) const
107     {
108         glBindFramebuffer(GL_FRAMEBUFFER, fbo->get());
109 
110         glBindTexture(GL_TEXTURE_2D, tex->get());
111         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 500, 500, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
112 
113         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex->get(), 0);
114         ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
115     }
116 
checkExtension(bool verbose=true) const117     bool checkExtension(bool verbose = true) const
118     {
119         if (!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_surfaceless_context"))
120         {
121             if (verbose)
122             {
123                 std::cout << "Test skipped because EGL_KHR_surfaceless_context is not available."
124                           << std::endl;
125             }
126             return false;
127         }
128         return true;
129     }
130 
131     EGLContext mContext    = EGL_NO_CONTEXT;
132     EGLSurface mPbuffer    = EGL_NO_SURFACE;
133     bool mSupportsPbuffers = false;
134     EGLConfig mConfig      = 0;
135     EGLDisplay mDisplay    = EGL_NO_DISPLAY;
136 };
137 
138 // Test surfaceless MakeCurrent returns the correct value.
TEST_P(EGLSurfacelessContextTest,MakeCurrentSurfaceless)139 TEST_P(EGLSurfacelessContextTest, MakeCurrentSurfaceless)
140 {
141     EGLContext context = createContext();
142 
143     if (eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context))
144     {
145         ASSERT_TRUE(checkExtension(false));
146     }
147     else
148     {
149         // The extension allows EGL_BAD_MATCH with the extension too, but ANGLE
150         // shouldn't do that.
151         ASSERT_FALSE(checkExtension(false));
152     }
153 }
154 
155 // Test that the scissor and viewport are set correctly
TEST_P(EGLSurfacelessContextTest,DefaultScissorAndViewport)156 TEST_P(EGLSurfacelessContextTest, DefaultScissorAndViewport)
157 {
158     if (!checkExtension())
159     {
160         return;
161     }
162 
163     EGLContext context = createContext();
164     ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
165 
166     GLint scissor[4] = {1, 2, 3, 4};
167     glGetIntegerv(GL_SCISSOR_BOX, scissor);
168     ASSERT_GL_NO_ERROR();
169     ASSERT_EQ(0, scissor[0]);
170     ASSERT_EQ(0, scissor[1]);
171     ASSERT_EQ(0, scissor[2]);
172     ASSERT_EQ(0, scissor[3]);
173 
174     GLint viewport[4] = {1, 2, 3, 4};
175     glGetIntegerv(GL_VIEWPORT, viewport);
176     ASSERT_GL_NO_ERROR();
177     ASSERT_EQ(0, viewport[0]);
178     ASSERT_EQ(0, viewport[1]);
179     ASSERT_EQ(0, viewport[2]);
180     ASSERT_EQ(0, viewport[3]);
181 }
182 
183 // Test the CheckFramebufferStatus returns the correct value.
TEST_P(EGLSurfacelessContextTest,CheckFramebufferStatus)184 TEST_P(EGLSurfacelessContextTest, CheckFramebufferStatus)
185 {
186     if (!checkExtension())
187     {
188         return;
189     }
190 
191     EGLContext context = createContext();
192     ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
193 
194     ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_UNDEFINED_OES, glCheckFramebufferStatus(GL_FRAMEBUFFER));
195 
196     GLFramebuffer fbo;
197     GLTexture tex;
198     createFramebuffer(&fbo, &tex);
199     glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
200     ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
201 }
202 
203 // Test that clearing and readpixels work when done in an FBO.
TEST_P(EGLSurfacelessContextTest,ClearReadPixelsInFBO)204 TEST_P(EGLSurfacelessContextTest, ClearReadPixelsInFBO)
205 {
206     if (!checkExtension())
207     {
208         return;
209     }
210 
211     EGLContext context = createContext();
212     ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
213 
214     GLFramebuffer fbo;
215     GLTexture tex;
216     createFramebuffer(&fbo, &tex);
217     glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
218 
219     glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
220     glClear(GL_COLOR_BUFFER_BIT);
221     ASSERT_GL_NO_ERROR();
222 
223     EXPECT_PIXEL_COLOR_EQ(250, 250, GLColor::red);
224     ASSERT_GL_NO_ERROR();
225 }
226 
227 // Test clear+readpixels in an FBO in surfaceless and in the default FBO in a pbuffer
TEST_P(EGLSurfacelessContextTest,Switcheroo)228 TEST_P(EGLSurfacelessContextTest, Switcheroo)
229 {
230     ANGLE_SKIP_TEST_IF(!checkExtension());
231     ANGLE_SKIP_TEST_IF(!mSupportsPbuffers);
232 
233     // Fails on NVIDIA Shield TV (http://anglebug.com/4924)
234     ANGLE_SKIP_TEST_IF(IsAndroid() && IsNVIDIA());
235 
236     EGLContext context = createContext();
237     EGLSurface pbuffer = createPbuffer(500, 500);
238 
239     // We need to make the context current to do the one time setup of the FBO
240     ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
241     GLFramebuffer fbo;
242     GLTexture tex;
243     createFramebuffer(&fbo, &tex);
244     glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
245 
246     for (int i = 0; i < 4; ++i)
247     {
248         // Clear to red in the FBO in surfaceless mode
249         ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
250 
251         glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
252         glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
253         glClear(GL_COLOR_BUFFER_BIT);
254         ASSERT_GL_NO_ERROR();
255 
256         EXPECT_PIXEL_COLOR_EQ(250, 250, GLColor::red);
257         ASSERT_GL_NO_ERROR();
258 
259         // Clear to green in the pbuffer
260         ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, pbuffer, pbuffer, context));
261 
262         glBindFramebuffer(GL_FRAMEBUFFER, 0);
263         glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
264         glClear(GL_COLOR_BUFFER_BIT);
265         ASSERT_GL_NO_ERROR();
266 
267         EXPECT_PIXEL_COLOR_EQ(250, 250, GLColor::green);
268         ASSERT_GL_NO_ERROR();
269     }
270 }
271 
272 }  // anonymous namespace
273 
274 ANGLE_INSTANTIATE_TEST(EGLSurfacelessContextTest,
275                        WithNoFixture(ES2_D3D9()),
276                        WithNoFixture(ES2_D3D11()),
277                        WithNoFixture(ES2_METAL()),
278                        WithNoFixture(ES2_OPENGL()),
279                        WithNoFixture(ES2_OPENGLES()),
280                        WithNoFixture(ES2_VULKAN()));
281