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 break;
52 }
53 }
54 ASSERT_NE(nullptr, mConfig);
55 }
56
testTearDown()57 void testTearDown() override
58 {
59 eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
60
61 if (mContext != EGL_NO_CONTEXT)
62 {
63 eglDestroyContext(mDisplay, mContext);
64 }
65
66 if (mPbuffer != EGL_NO_SURFACE)
67 {
68 eglDestroySurface(mDisplay, mPbuffer);
69 }
70
71 eglTerminate(mDisplay);
72 }
73
74 protected:
createContext()75 EGLContext createContext()
76 {
77 const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
78
79 mContext = eglCreateContext(mDisplay, mConfig, EGL_NO_CONTEXT, contextAttribs);
80 EXPECT_TRUE(mContext != EGL_NO_CONTEXT);
81 return mContext;
82 }
83
createPbuffer(int width,int height)84 EGLSurface createPbuffer(int width, int height)
85 {
86 const EGLint pbufferAttribs[] = {
87 EGL_WIDTH, 500, EGL_HEIGHT, 500, EGL_NONE,
88 };
89 mPbuffer = eglCreatePbufferSurface(mDisplay, mConfig, pbufferAttribs);
90 EXPECT_TRUE(mPbuffer != EGL_NO_SURFACE);
91 return mPbuffer;
92 }
93
createFramebuffer(GLFramebuffer * fbo,GLTexture * tex) const94 void createFramebuffer(GLFramebuffer *fbo, GLTexture *tex) const
95 {
96 glBindFramebuffer(GL_FRAMEBUFFER, fbo->get());
97
98 glBindTexture(GL_TEXTURE_2D, tex->get());
99 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 500, 500, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
100
101 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex->get(), 0);
102 EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
103 }
104
checkExtension(bool verbose=true) const105 bool checkExtension(bool verbose = true) const
106 {
107 if (!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_surfaceless_context"))
108 {
109 if (verbose)
110 {
111 std::cout << "Test skipped because EGL_KHR_surfaceless_context is not available."
112 << std::endl;
113 }
114 return false;
115 }
116 return true;
117 }
118
119 EGLContext mContext = EGL_NO_CONTEXT;
120 EGLSurface mPbuffer = EGL_NO_SURFACE;
121 EGLConfig mConfig = 0;
122 EGLDisplay mDisplay = EGL_NO_DISPLAY;
123 };
124
125 // Test surfaceless MakeCurrent returns the correct value.
TEST_P(EGLSurfacelessContextTest,MakeCurrentSurfaceless)126 TEST_P(EGLSurfacelessContextTest, MakeCurrentSurfaceless)
127 {
128 EGLContext context = createContext();
129
130 if (eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context))
131 {
132 ASSERT_TRUE(checkExtension(false));
133 }
134 else
135 {
136 // The extension allows EGL_BAD_MATCH with the extension too, but ANGLE
137 // shouldn't do that.
138 ASSERT_FALSE(checkExtension(false));
139 }
140 }
141
142 // Test that the scissor and viewport are set correctly
TEST_P(EGLSurfacelessContextTest,DefaultScissorAndViewport)143 TEST_P(EGLSurfacelessContextTest, DefaultScissorAndViewport)
144 {
145 if (!checkExtension())
146 {
147 return;
148 }
149
150 EGLContext context = createContext();
151 ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
152
153 GLint scissor[4] = {1, 2, 3, 4};
154 glGetIntegerv(GL_SCISSOR_BOX, scissor);
155 ASSERT_GL_NO_ERROR();
156 ASSERT_EQ(0, scissor[0]);
157 ASSERT_EQ(0, scissor[1]);
158 ASSERT_EQ(0, scissor[2]);
159 ASSERT_EQ(0, scissor[3]);
160
161 GLint viewport[4] = {1, 2, 3, 4};
162 glGetIntegerv(GL_VIEWPORT, viewport);
163 ASSERT_GL_NO_ERROR();
164 ASSERT_EQ(0, viewport[0]);
165 ASSERT_EQ(0, viewport[1]);
166 ASSERT_EQ(0, viewport[2]);
167 ASSERT_EQ(0, viewport[3]);
168 }
169
170 // Test the CheckFramebufferStatus returns the correct value.
TEST_P(EGLSurfacelessContextTest,CheckFramebufferStatus)171 TEST_P(EGLSurfacelessContextTest, CheckFramebufferStatus)
172 {
173 if (!checkExtension())
174 {
175 return;
176 }
177
178 EGLContext context = createContext();
179 ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
180
181 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_UNDEFINED_OES, glCheckFramebufferStatus(GL_FRAMEBUFFER));
182
183 GLFramebuffer fbo;
184 GLTexture tex;
185 createFramebuffer(&fbo, &tex);
186 glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
187 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
188 }
189
190 // Test that clearing and readpixels work when done in an FBO.
TEST_P(EGLSurfacelessContextTest,ClearReadPixelsInFBO)191 TEST_P(EGLSurfacelessContextTest, ClearReadPixelsInFBO)
192 {
193 if (!checkExtension())
194 {
195 return;
196 }
197
198 EGLContext context = createContext();
199 ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
200
201 GLFramebuffer fbo;
202 GLTexture tex;
203 createFramebuffer(&fbo, &tex);
204 glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
205
206 glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
207 glClear(GL_COLOR_BUFFER_BIT);
208 ASSERT_GL_NO_ERROR();
209
210 EXPECT_PIXEL_COLOR_EQ(250, 250, GLColor::red);
211 ASSERT_GL_NO_ERROR();
212 }
213
214 // Test clear+readpixels in an FBO in surfaceless and in the default FBO in a pbuffer
TEST_P(EGLSurfacelessContextTest,Switcheroo)215 TEST_P(EGLSurfacelessContextTest, Switcheroo)
216 {
217 if (!checkExtension())
218 {
219 return;
220 }
221
222 EGLContext context = createContext();
223 EGLSurface pbuffer = createPbuffer(500, 500);
224
225 // We need to make the context current to do the one time setup of the FBO
226 ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
227 GLFramebuffer fbo;
228 GLTexture tex;
229 createFramebuffer(&fbo, &tex);
230 glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
231
232 for (int i = 0; i < 4; ++i)
233 {
234 // Clear to red in the FBO in surfaceless mode
235 ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
236
237 glBindFramebuffer(GL_FRAMEBUFFER, fbo.get());
238 glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
239 glClear(GL_COLOR_BUFFER_BIT);
240 ASSERT_GL_NO_ERROR();
241
242 EXPECT_PIXEL_COLOR_EQ(250, 250, GLColor::red);
243 ASSERT_GL_NO_ERROR();
244
245 // Clear to green in the pbuffer
246 ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, pbuffer, pbuffer, context));
247
248 glBindFramebuffer(GL_FRAMEBUFFER, 0);
249 glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
250 glClear(GL_COLOR_BUFFER_BIT);
251 ASSERT_GL_NO_ERROR();
252
253 EXPECT_PIXEL_COLOR_EQ(250, 250, GLColor::green);
254 ASSERT_GL_NO_ERROR();
255 }
256 }
257
258 } // anonymous namespace
259
260 ANGLE_INSTANTIATE_TEST(EGLSurfacelessContextTest,
261 WithNoFixture(ES2_D3D9()),
262 WithNoFixture(ES2_D3D11()),
263 WithNoFixture(ES2_OPENGL()),
264 WithNoFixture(ES2_VULKAN()));
265