1 //
2 // Copyright 2021 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 // EGLBufferAgeTest.cpp:
7 // EGL extension EGL_EXT_buffer_age
8 //
9
10 #include <gtest/gtest.h>
11
12 #include "test_utils/ANGLETest.h"
13 #include "util/EGLWindow.h"
14 #include "util/OSWindow.h"
15
16 using namespace angle;
17
18 class EGLBufferAgeTest : public ANGLETest
19 {
20 public:
EGLBufferAgeTest()21 EGLBufferAgeTest() : mDisplay(EGL_NO_DISPLAY) {}
22
testSetUp()23 void testSetUp() override
24 {
25 EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
26 mDisplay = eglGetPlatformDisplayEXT(
27 EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
28 EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);
29 EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
30 mMajorVersion = GetParam().majorVersion;
31 mExtensionSupported = IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_buffer_age");
32 }
33
testTearDown()34 void testTearDown() override
35 {
36 if (mDisplay != EGL_NO_DISPLAY)
37 {
38 eglTerminate(mDisplay);
39 eglReleaseThread();
40 mDisplay = EGL_NO_DISPLAY;
41 }
42 ASSERT_EGL_SUCCESS() << "Error during test TearDown";
43 }
44
chooseConfig(EGLConfig * config)45 bool chooseConfig(EGLConfig *config)
46 {
47 bool result = false;
48 EGLint count = 0;
49 EGLint clientVersion = mMajorVersion == 3 ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT;
50 EGLint attribs[] = {EGL_RED_SIZE,
51 8,
52 EGL_GREEN_SIZE,
53 8,
54 EGL_BLUE_SIZE,
55 8,
56 EGL_ALPHA_SIZE,
57 0,
58 EGL_RENDERABLE_TYPE,
59 clientVersion,
60 EGL_SURFACE_TYPE,
61 EGL_WINDOW_BIT,
62 EGL_NONE};
63
64 result = eglChooseConfig(mDisplay, attribs, config, 1, &count);
65 EXPECT_EGL_TRUE(result && (count > 0));
66 return result;
67 }
68
createContext(EGLConfig config,EGLContext * context)69 bool createContext(EGLConfig config, EGLContext *context)
70 {
71 bool result = false;
72 EGLint attribs[] = {EGL_CONTEXT_MAJOR_VERSION, mMajorVersion, EGL_NONE};
73
74 *context = eglCreateContext(mDisplay, config, nullptr, attribs);
75 result = (*context != EGL_NO_CONTEXT);
76 EXPECT_TRUE(result);
77 return result;
78 }
79
createWindowSurface(EGLConfig config,EGLNativeWindowType win,EGLSurface * surface)80 bool createWindowSurface(EGLConfig config, EGLNativeWindowType win, EGLSurface *surface)
81 {
82 bool result = false;
83 EGLint attribs[] = {EGL_NONE};
84
85 *surface = eglCreateWindowSurface(mDisplay, config, win, attribs);
86 result = (*surface != EGL_NO_SURFACE);
87 EXPECT_TRUE(result);
88 return result;
89 }
90
queryAge(EGLSurface surface)91 EGLint queryAge(EGLSurface surface)
92 {
93 EGLint value = 0;
94 bool result = eglQuerySurface(mDisplay, surface, EGL_BUFFER_AGE_EXT, &value);
95 EXPECT_TRUE(result);
96 return value;
97 }
98
99 EGLDisplay mDisplay = EGL_NO_DISPLAY;
100 EGLint mMajorVersion = 0;
101 const EGLint kWidth = 64;
102 const EGLint kHeight = 64;
103 bool mExtensionSupported = false;
104 };
105
106 // Query for buffer age
TEST_P(EGLBufferAgeTest,QueryBufferAge)107 TEST_P(EGLBufferAgeTest, QueryBufferAge)
108 {
109 ANGLE_SKIP_TEST_IF(!mExtensionSupported);
110
111 EGLConfig config = EGL_NO_CONFIG_KHR;
112 EXPECT_TRUE(chooseConfig(&config));
113
114 EGLContext context = EGL_NO_CONTEXT;
115 EXPECT_TRUE(createContext(config, &context));
116 ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
117
118 EGLSurface surface = EGL_NO_SURFACE;
119
120 OSWindow *osWindow = OSWindow::New();
121 osWindow->initialize("EGLBufferAgeTest", kWidth, kHeight);
122 EXPECT_TRUE(createWindowSurface(config, osWindow->getNativeWindow(), &surface));
123 ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
124
125 EXPECT_TRUE(eglMakeCurrent(mDisplay, surface, surface, context));
126 ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
127
128 glClearColor(1.0, 0.0, 0.0, 1.0);
129
130 const uint32_t loopcount = 15;
131 EGLint expectedAge = 0;
132 for (uint32_t i = 0; i < loopcount; i++)
133 {
134 EGLint age = queryAge(surface);
135 // Should start with zero age and then flip to buffer count - which we don't know.
136 if ((expectedAge == 0) && (age > 0))
137 {
138 expectedAge = age;
139 }
140 EXPECT_EQ(age, expectedAge);
141
142 glClear(GL_COLOR_BUFFER_BIT);
143 ASSERT_GL_NO_ERROR() << "glClear failed";
144 eglSwapBuffers(mDisplay, surface);
145 ASSERT_EGL_SUCCESS() << "eglSwapBuffers failed.";
146 }
147 EXPECT_GT(expectedAge, 0);
148
149 EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
150 ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
151
152 eglDestroySurface(mDisplay, surface);
153 surface = EGL_NO_SURFACE;
154 osWindow->destroy();
155 OSWindow::Delete(&osWindow);
156
157 eglDestroyContext(mDisplay, context);
158 context = EGL_NO_CONTEXT;
159 }
160
161 // Verify contents of buffer are as expected
TEST_P(EGLBufferAgeTest,VerifyContents)162 TEST_P(EGLBufferAgeTest, VerifyContents)
163 {
164 ANGLE_SKIP_TEST_IF(!mExtensionSupported);
165
166 EGLConfig config = EGL_NO_CONFIG_KHR;
167 EXPECT_TRUE(chooseConfig(&config));
168
169 EGLContext context = EGL_NO_CONTEXT;
170 EXPECT_TRUE(createContext(config, &context));
171 ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
172
173 EGLSurface surface = EGL_NO_SURFACE;
174
175 OSWindow *osWindow = OSWindow::New();
176 osWindow->initialize("EGLBufferAgeTest", kWidth, kHeight);
177 EXPECT_TRUE(createWindowSurface(config, osWindow->getNativeWindow(), &surface));
178 ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
179
180 EXPECT_TRUE(eglMakeCurrent(mDisplay, surface, surface, context));
181 ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
182
183 const angle::GLColor kLightGray(191, 191, 191, 255); // 0.75
184 const angle::GLColor kDarkGray(64, 64, 64, 255); // 0.25
185 const angle::GLColor kColorSet[] = {
186 GLColor::blue, GLColor::cyan, kDarkGray, GLColor::green, GLColor::red,
187 GLColor::white, GLColor::yellow, GLColor::black, GLColor::magenta, kLightGray,
188 GLColor::black, // Extra loops until color cycled through
189 GLColor::black, GLColor::black, GLColor::black, GLColor::black};
190
191 EGLint age = 0;
192 angle::GLColor expectedColor = GLColor::black;
193 int loopCount = (sizeof(kColorSet) / sizeof(kColorSet[0]));
194 for (int i = 0; i < loopCount; i++)
195 {
196 age = queryAge(surface);
197 if (age > 0)
198 {
199 // Check that color/content is what we expect
200 expectedColor = kColorSet[i - age];
201 EXPECT_PIXEL_COLOR_EQ(1, 1, expectedColor);
202 }
203
204 float red = kColorSet[i].R / 255.0;
205 float green = kColorSet[i].G / 255.0;
206 float blue = kColorSet[i].B / 255.0;
207 float alpha = kColorSet[i].A / 255.0;
208
209 glClearColor(red, green, blue, alpha);
210 glClear(GL_COLOR_BUFFER_BIT);
211 ASSERT_GL_NO_ERROR() << "glClear failed";
212 eglSwapBuffers(mDisplay, surface);
213 ASSERT_EGL_SUCCESS() << "eglSwapBuffers failed.";
214 }
215 EXPECT_GT(age, 0);
216
217 EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
218 ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
219
220 eglDestroySurface(mDisplay, surface);
221 surface = EGL_NO_SURFACE;
222 osWindow->destroy();
223 OSWindow::Delete(&osWindow);
224
225 eglDestroyContext(mDisplay, context);
226 context = EGL_NO_CONTEXT;
227 }
228
229 // Verify EGL_BAD_SURFACE when not current
TEST_P(EGLBufferAgeTest,UncurrentContextBadSurface)230 TEST_P(EGLBufferAgeTest, UncurrentContextBadSurface)
231 {
232 ANGLE_SKIP_TEST_IF(!mExtensionSupported);
233
234 EGLConfig config = EGL_NO_CONFIG_KHR;
235 EXPECT_TRUE(chooseConfig(&config));
236
237 EGLContext context = EGL_NO_CONTEXT;
238 EXPECT_TRUE(createContext(config, &context));
239 ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
240
241 EGLSurface surface = EGL_NO_SURFACE;
242
243 OSWindow *osWindow = OSWindow::New();
244 osWindow->initialize("EGLBufferAgeTest", kWidth, kHeight);
245 EXPECT_TRUE(createWindowSurface(config, osWindow->getNativeWindow(), &surface));
246 ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
247
248 // No current context
249
250 EGLint value = 0;
251 EXPECT_EGL_FALSE(eglQuerySurface(mDisplay, surface, EGL_BUFFER_AGE_EXT, &value));
252 EXPECT_EGL_ERROR(EGL_BAD_SURFACE);
253
254 EGLContext otherContext = EGL_NO_CONTEXT;
255 EXPECT_TRUE(createContext(config, &otherContext));
256 ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
257
258 // Surface current to another context
259 EXPECT_TRUE(eglMakeCurrent(mDisplay, surface, surface, otherContext));
260 // Make context the active context
261 EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
262
263 value = 0;
264 EXPECT_EGL_FALSE(eglQuerySurface(mDisplay, surface, EGL_BUFFER_AGE_EXT, &value));
265 EXPECT_EGL_ERROR(EGL_BAD_SURFACE);
266
267 eglDestroySurface(mDisplay, surface);
268 surface = EGL_NO_SURFACE;
269 osWindow->destroy();
270 OSWindow::Delete(&osWindow);
271
272 eglDestroyContext(mDisplay, otherContext);
273 otherContext = EGL_NO_CONTEXT;
274
275 eglDestroyContext(mDisplay, context);
276 context = EGL_NO_CONTEXT;
277 }
278
279 // Expect age always == 1 when EGL_BUFFER_PRESERVED is chosen
TEST_P(EGLBufferAgeTest,BufferPreserved)280 TEST_P(EGLBufferAgeTest, BufferPreserved)
281 {
282 ANGLE_SKIP_TEST_IF(!mExtensionSupported);
283
284 EGLConfig config = EGL_NO_CONFIG_KHR;
285 EGLint count = 0;
286 EGLint clientVersion = mMajorVersion == 3 ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT;
287 EGLint attribs[] = {EGL_RED_SIZE,
288 8,
289 EGL_GREEN_SIZE,
290 8,
291 EGL_BLUE_SIZE,
292 8,
293 EGL_ALPHA_SIZE,
294 0,
295 EGL_RENDERABLE_TYPE,
296 clientVersion,
297 EGL_SURFACE_TYPE,
298 EGL_WINDOW_BIT | EGL_SWAP_BEHAVIOR_PRESERVED_BIT,
299 EGL_NONE};
300
301 EXPECT_EGL_TRUE(eglChooseConfig(mDisplay, attribs, &config, 1, &count));
302 // Skip if no configs, this indicates EGL_BUFFER_PRESERVED is not supported.
303 ANGLE_SKIP_TEST_IF(count == 0);
304
305 EGLContext context = EGL_NO_CONTEXT;
306 EXPECT_TRUE(createContext(config, &context));
307 ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
308
309 EGLSurface surface = EGL_NO_SURFACE;
310
311 OSWindow *osWindow = OSWindow::New();
312 osWindow->initialize("EGLBufferAgeTest", kWidth, kHeight);
313 EXPECT_TRUE(createWindowSurface(config, osWindow->getNativeWindow(), &surface));
314 ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
315
316 EXPECT_TRUE(eglMakeCurrent(mDisplay, surface, surface, context));
317 ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
318
319 glClearColor(1.0, 0.0, 0.0, 1.0);
320
321 const uint32_t loopcount = 10;
322 EGLint expectedAge = 1;
323 for (uint32_t i = 0; i < loopcount; i++)
324 {
325 EGLint age = queryAge(surface);
326 EXPECT_EQ(age, expectedAge);
327
328 glClear(GL_COLOR_BUFFER_BIT);
329 ASSERT_GL_NO_ERROR() << "glClear failed";
330 eglSwapBuffers(mDisplay, surface);
331 ASSERT_EGL_SUCCESS() << "eglSwapBuffers failed.";
332 }
333
334 EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
335 ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
336
337 eglDestroySurface(mDisplay, surface);
338 surface = EGL_NO_SURFACE;
339 osWindow->destroy();
340 OSWindow::Delete(&osWindow);
341
342 eglDestroyContext(mDisplay, context);
343 context = EGL_NO_CONTEXT;
344 }
345
346 ANGLE_INSTANTIATE_TEST(EGLBufferAgeTest,
347 WithNoFixture(ES2_OPENGLES()),
348 WithNoFixture(ES3_OPENGLES()),
349 WithNoFixture(ES2_OPENGL()),
350 WithNoFixture(ES3_OPENGL()),
351 WithNoFixture(ES2_VULKAN()),
352 WithNoFixture(ES3_VULKAN()));