1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program EGL Module
3 * ---------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Color clear case.
22 *//*--------------------------------------------------------------------*/
23
24 #include "teglColorClearCase.hpp"
25 #include "tcuTestLog.hpp"
26 #include "eglwLibrary.hpp"
27 #include "eglwEnums.hpp"
28 #include "egluUtil.hpp"
29 #include "deRandom.hpp"
30 #include "deString.h"
31 #include "tcuImageCompare.hpp"
32 #include "tcuVector.hpp"
33 #include "tcuTextureUtil.hpp"
34 #include "tcuPixelFormat.hpp"
35 #include "glwFunctions.hpp"
36 #include "deThread.hpp"
37 #include "deSemaphore.hpp"
38 #include "deSharedPtr.hpp"
39 #include "teglGLES1RenderUtil.hpp"
40 #include "teglGLES2RenderUtil.hpp"
41 #include "teglVGRenderUtil.hpp"
42
43 #include <memory>
44 #include <iterator>
45
46 namespace deqp
47 {
48 namespace egl
49 {
50
51 using std::vector;
52 using tcu::RGBA;
53 using tcu::TestLog;
54 using namespace eglw;
55
56 // Utilities.
57
58 struct ClearOp
59 {
ClearOpdeqp::egl::ClearOp60 ClearOp(int x_, int y_, int width_, int height_, const tcu::RGBA &color_)
61 : x(x_)
62 , y(y_)
63 , width(width_)
64 , height(height_)
65 , color(color_)
66 {
67 }
68
ClearOpdeqp::egl::ClearOp69 ClearOp(void) : x(0), y(0), width(0), height(0), color(0)
70 {
71 }
72
73 int x;
74 int y;
75 int width;
76 int height;
77 tcu::RGBA color;
78 };
79
80 struct ApiFunctions
81 {
82 glw::Functions gl;
83 };
84
computeRandomClear(de::Random & rnd,int width,int height)85 static ClearOp computeRandomClear(de::Random &rnd, int width, int height)
86 {
87 int w = rnd.getInt(1, width);
88 int h = rnd.getInt(1, height);
89 int x = rnd.getInt(0, width - w);
90 int y = rnd.getInt(0, height - h);
91 tcu::RGBA col(rnd.getUint32());
92
93 return ClearOp(x, y, w, h, col);
94 }
95
renderReference(tcu::Surface & dst,const vector<ClearOp> & clears,const tcu::PixelFormat & pixelFormat)96 static void renderReference(tcu::Surface &dst, const vector<ClearOp> &clears, const tcu::PixelFormat &pixelFormat)
97 {
98 for (vector<ClearOp>::const_iterator clearIter = clears.begin(); clearIter != clears.end(); clearIter++)
99 {
100 tcu::PixelBufferAccess access =
101 tcu::getSubregion(dst.getAccess(), clearIter->x, clearIter->y, 0, clearIter->width, clearIter->height, 1);
102 tcu::clear(access, pixelFormat.convertColor(clearIter->color).toIVec());
103 }
104 }
105
renderClear(EGLint api,const ApiFunctions & func,const ClearOp & clear)106 static void renderClear(EGLint api, const ApiFunctions &func, const ClearOp &clear)
107 {
108 switch (api)
109 {
110 case EGL_OPENGL_ES_BIT:
111 gles1::clear(clear.x, clear.y, clear.width, clear.height, clear.color.toVec());
112 break;
113 case EGL_OPENGL_ES2_BIT:
114 gles2::clear(func.gl, clear.x, clear.y, clear.width, clear.height, clear.color.toVec());
115 break;
116 case EGL_OPENGL_ES3_BIT_KHR:
117 gles2::clear(func.gl, clear.x, clear.y, clear.width, clear.height, clear.color.toVec());
118 break;
119 case EGL_OPENVG_BIT:
120 vg::clear(clear.x, clear.y, clear.width, clear.height, clear.color.toVec());
121 break;
122 default:
123 DE_ASSERT(false);
124 }
125 }
126
finish(EGLint api,const ApiFunctions & func)127 static void finish(EGLint api, const ApiFunctions &func)
128 {
129 switch (api)
130 {
131 case EGL_OPENGL_ES_BIT:
132 gles1::finish();
133 break;
134 case EGL_OPENGL_ES2_BIT:
135 gles2::finish(func.gl);
136 break;
137 case EGL_OPENGL_ES3_BIT_KHR:
138 gles2::finish(func.gl);
139 break;
140 case EGL_OPENVG_BIT:
141 vg::finish();
142 break;
143 default:
144 DE_ASSERT(false);
145 }
146 }
147
readPixels(EGLint api,const ApiFunctions & func,tcu::Surface & dst)148 static void readPixels(EGLint api, const ApiFunctions &func, tcu::Surface &dst)
149 {
150 switch (api)
151 {
152 case EGL_OPENGL_ES_BIT:
153 gles1::readPixels(dst, 0, 0, dst.getWidth(), dst.getHeight());
154 break;
155 case EGL_OPENGL_ES2_BIT:
156 gles2::readPixels(func.gl, dst, 0, 0, dst.getWidth(), dst.getHeight());
157 break;
158 case EGL_OPENGL_ES3_BIT_KHR:
159 gles2::readPixels(func.gl, dst, 0, 0, dst.getWidth(), dst.getHeight());
160 break;
161 case EGL_OPENVG_BIT:
162 vg::readPixels(dst, 0, 0, dst.getWidth(), dst.getHeight());
163 break;
164 default:
165 DE_ASSERT(false);
166 }
167 }
168
getPixelFormat(const Library & egl,EGLDisplay display,EGLConfig config)169 static tcu::PixelFormat getPixelFormat(const Library &egl, EGLDisplay display, EGLConfig config)
170 {
171 tcu::PixelFormat pixelFmt;
172
173 egl.getConfigAttrib(display, config, EGL_RED_SIZE, &pixelFmt.redBits);
174 egl.getConfigAttrib(display, config, EGL_GREEN_SIZE, &pixelFmt.greenBits);
175 egl.getConfigAttrib(display, config, EGL_BLUE_SIZE, &pixelFmt.blueBits);
176 egl.getConfigAttrib(display, config, EGL_ALPHA_SIZE, &pixelFmt.alphaBits);
177
178 return pixelFmt;
179 }
180
181 // SingleThreadColorClearCase
182
SingleThreadColorClearCase(EglTestContext & eglTestCtx,const char * name,const char * description,EGLint api,EGLint surfaceType,const eglu::FilterList & filters,int numContextsPerApi)183 SingleThreadColorClearCase::SingleThreadColorClearCase(EglTestContext &eglTestCtx, const char *name,
184 const char *description, EGLint api, EGLint surfaceType,
185 const eglu::FilterList &filters, int numContextsPerApi)
186 : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
187 {
188 }
189
executeForContexts(EGLDisplay display,EGLSurface surface,const Config & config,const std::vector<std::pair<EGLint,EGLContext>> & contexts)190 void SingleThreadColorClearCase::executeForContexts(EGLDisplay display, EGLSurface surface, const Config &config,
191 const std::vector<std::pair<EGLint, EGLContext>> &contexts)
192 {
193 const Library &egl = m_eglTestCtx.getLibrary();
194
195 const tcu::IVec2 surfaceSize = eglu::getSurfaceSize(egl, display, surface);
196 const int width = surfaceSize.x();
197 const int height = surfaceSize.y();
198
199 TestLog &log = m_testCtx.getLog();
200
201 tcu::Surface refFrame(width, height);
202 tcu::Surface frame(width, height);
203 tcu::PixelFormat pixelFmt = getPixelFormat(egl, display, config.config);
204
205 de::Random rnd(deStringHash(getName()));
206 vector<ClearOp> clears;
207 const int ctxClears = 2;
208 const int numIters = 3;
209
210 ApiFunctions funcs;
211
212 m_eglTestCtx.initGLFunctions(&funcs.gl, glu::ApiType::es(2, 0));
213
214 // Clear to black using first context.
215 {
216 EGLint api = contexts[0].first;
217 EGLContext context = contexts[0].second;
218 ClearOp clear(0, 0, width, height, RGBA::black());
219
220 egl.makeCurrent(display, surface, surface, context);
221 EGLU_CHECK_MSG(egl, "eglMakeCurrent");
222
223 renderClear(api, funcs, clear);
224 finish(api, funcs);
225 clears.push_back(clear);
226 }
227
228 // Render.
229 for (int iterNdx = 0; iterNdx < numIters; iterNdx++)
230 {
231 for (vector<std::pair<EGLint, EGLContext>>::const_iterator ctxIter = contexts.begin();
232 ctxIter != contexts.end(); ctxIter++)
233 {
234 EGLint api = ctxIter->first;
235 EGLContext context = ctxIter->second;
236
237 egl.makeCurrent(display, surface, surface, context);
238 EGLU_CHECK_MSG(egl, "eglMakeCurrent");
239
240 for (int clearNdx = 0; clearNdx < ctxClears; clearNdx++)
241 {
242 ClearOp clear = computeRandomClear(rnd, width, height);
243
244 renderClear(api, funcs, clear);
245 clears.push_back(clear);
246 }
247
248 finish(api, funcs);
249 }
250 }
251
252 // Read pixels using first context. \todo [pyry] Randomize?
253 {
254 EGLint api = contexts[0].first;
255 EGLContext context = contexts[0].second;
256
257 egl.makeCurrent(display, surface, surface, context);
258 EGLU_CHECK_MSG(egl, "eglMakeCurrent");
259
260 readPixels(api, funcs, frame);
261 }
262
263 egl.makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
264 EGLU_CHECK_MSG(egl, "eglMakeCurrent");
265
266 // Render reference.
267 renderReference(refFrame, clears, pixelFmt);
268
269 // Compare images
270 {
271 tcu::RGBA eps = RGBA(1, 1, 1, 1);
272 if (pixelFmt.alphaBits == 1)
273 eps = RGBA(1, 1, 1, 127);
274 else if (pixelFmt.alphaBits == 2)
275 eps = RGBA(1, 1, 1, 63);
276
277 bool imagesOk = tcu::pixelThresholdCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame,
278 eps + pixelFmt.getColorThreshold(), tcu::COMPARE_LOG_RESULT);
279
280 if (!imagesOk)
281 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
282 }
283 }
284
285 // MultiThreadColorClearCase
286
287 enum
288 {
289 NUM_CLEARS_PER_PACKET = 2 //!< Number of clears performed in one context activation in one thread.
290 };
291
292 class ColorClearThread;
293
294 typedef de::SharedPtr<ColorClearThread> ColorClearThreadSp;
295 typedef de::SharedPtr<de::Semaphore> SemaphoreSp;
296
297 struct ClearPacket
298 {
ClearPacketdeqp::egl::ClearPacket299 ClearPacket(void)
300 {
301 }
302
303 ClearOp clears[NUM_CLEARS_PER_PACKET];
304 SemaphoreSp wait;
305 SemaphoreSp signal;
306 };
307
308 class ColorClearThread : public de::Thread
309 {
310 public:
ColorClearThread(const Library & egl,EGLDisplay display,EGLSurface surface,EGLContext context,EGLint api,const ApiFunctions & funcs,const std::vector<ClearPacket> & packets)311 ColorClearThread(const Library &egl, EGLDisplay display, EGLSurface surface, EGLContext context, EGLint api,
312 const ApiFunctions &funcs, const std::vector<ClearPacket> &packets)
313 : m_egl(egl)
314 , m_display(display)
315 , m_surface(surface)
316 , m_context(context)
317 , m_api(api)
318 , m_funcs(funcs)
319 , m_packets(packets)
320 {
321 }
322
run(void)323 void run(void)
324 {
325 for (std::vector<ClearPacket>::const_iterator packetIter = m_packets.begin(); packetIter != m_packets.end();
326 packetIter++)
327 {
328 // Wait until it is our turn.
329 packetIter->wait->decrement();
330
331 // Acquire context.
332 m_egl.makeCurrent(m_display, m_surface, m_surface, m_context);
333
334 // Execute clears.
335 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(packetIter->clears); ndx++)
336 renderClear(m_api, m_funcs, packetIter->clears[ndx]);
337
338 finish(m_api, m_funcs);
339 // Release context.
340 m_egl.makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
341
342 // Signal completion.
343 packetIter->signal->increment();
344 }
345 m_egl.releaseThread();
346 }
347
348 private:
349 const Library &m_egl;
350 EGLDisplay m_display;
351 EGLSurface m_surface;
352 EGLContext m_context;
353 EGLint m_api;
354 const ApiFunctions &m_funcs;
355 const std::vector<ClearPacket> &m_packets;
356 };
357
MultiThreadColorClearCase(EglTestContext & eglTestCtx,const char * name,const char * description,EGLint api,EGLint surfaceType,const eglu::FilterList & filters,int numContextsPerApi)358 MultiThreadColorClearCase::MultiThreadColorClearCase(EglTestContext &eglTestCtx, const char *name,
359 const char *description, EGLint api, EGLint surfaceType,
360 const eglu::FilterList &filters, int numContextsPerApi)
361 : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
362 {
363 }
364
executeForContexts(EGLDisplay display,EGLSurface surface,const Config & config,const std::vector<std::pair<EGLint,EGLContext>> & contexts)365 void MultiThreadColorClearCase::executeForContexts(EGLDisplay display, EGLSurface surface, const Config &config,
366 const std::vector<std::pair<EGLint, EGLContext>> &contexts)
367 {
368 const Library &egl = m_eglTestCtx.getLibrary();
369
370 const tcu::IVec2 surfaceSize = eglu::getSurfaceSize(egl, display, surface);
371 const int width = surfaceSize.x();
372 const int height = surfaceSize.y();
373
374 TestLog &log = m_testCtx.getLog();
375
376 tcu::Surface refFrame(width, height);
377 tcu::Surface frame(width, height);
378 tcu::PixelFormat pixelFmt = getPixelFormat(egl, display, config.config);
379
380 de::Random rnd(deStringHash(getName()));
381
382 ApiFunctions funcs;
383
384 m_eglTestCtx.initGLFunctions(&funcs.gl, glu::ApiType::es(2, 0));
385
386 // Create clear packets.
387 const int numPacketsPerThread = 2;
388 int numThreads = (int)contexts.size();
389 int numPackets = numThreads * numPacketsPerThread;
390
391 vector<SemaphoreSp> semaphores(numPackets + 1);
392 vector<vector<ClearPacket>> packets(numThreads);
393 vector<ColorClearThreadSp> threads(numThreads);
394
395 // Initialize semaphores.
396 for (vector<SemaphoreSp>::iterator sem = semaphores.begin(); sem != semaphores.end(); ++sem)
397 *sem = SemaphoreSp(new de::Semaphore(0));
398
399 // Create packets.
400 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
401 {
402 packets[threadNdx].resize(numPacketsPerThread);
403
404 for (int packetNdx = 0; packetNdx < numPacketsPerThread; packetNdx++)
405 {
406 ClearPacket &packet = packets[threadNdx][packetNdx];
407
408 // Threads take turns with packets.
409 packet.wait = semaphores[packetNdx * numThreads + threadNdx];
410 packet.signal = semaphores[packetNdx * numThreads + threadNdx + 1];
411
412 for (int clearNdx = 0; clearNdx < DE_LENGTH_OF_ARRAY(packet.clears); clearNdx++)
413 {
414 // First clear is always full-screen black.
415 if (threadNdx == 0 && packetNdx == 0 && clearNdx == 0)
416 packet.clears[clearNdx] = ClearOp(0, 0, width, height, RGBA::black());
417 else
418 packet.clears[clearNdx] = computeRandomClear(rnd, width, height);
419 }
420 }
421 }
422
423 // Create and launch threads (actual rendering starts once first semaphore is signaled).
424 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
425 {
426 threads[threadNdx] = ColorClearThreadSp(new ColorClearThread(
427 egl, display, surface, contexts[threadNdx].second, contexts[threadNdx].first, funcs, packets[threadNdx]));
428 threads[threadNdx]->start();
429 }
430
431 // Signal start and wait until complete.
432 semaphores.front()->increment();
433 semaphores.back()->decrement();
434
435 // Read pixels using first context. \todo [pyry] Randomize?
436 {
437 EGLint api = contexts[0].first;
438 EGLContext context = contexts[0].second;
439
440 egl.makeCurrent(display, surface, surface, context);
441 EGLU_CHECK_MSG(egl, "eglMakeCurrent");
442
443 readPixels(api, funcs, frame);
444 }
445
446 egl.makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
447 EGLU_CHECK_MSG(egl, "eglMakeCurrent");
448
449 // Join threads.
450 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
451 threads[threadNdx]->join();
452
453 // Render reference.
454 for (int packetNdx = 0; packetNdx < numPacketsPerThread; packetNdx++)
455 {
456 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
457 {
458 const ClearPacket &packet = packets[threadNdx][packetNdx];
459 for (int clearNdx = 0; clearNdx < DE_LENGTH_OF_ARRAY(packet.clears); clearNdx++)
460 {
461 tcu::PixelBufferAccess access =
462 tcu::getSubregion(refFrame.getAccess(), packet.clears[clearNdx].x, packet.clears[clearNdx].y, 0,
463 packet.clears[clearNdx].width, packet.clears[clearNdx].height, 1);
464 tcu::clear(access, pixelFmt.convertColor(packet.clears[clearNdx].color).toIVec());
465 }
466 }
467 }
468
469 // Compare images
470 {
471 tcu::RGBA eps = RGBA(1, 1, 1, 1);
472 if (pixelFmt.alphaBits == 1)
473 eps = RGBA(1, 1, 1, 127);
474 else if (pixelFmt.alphaBits == 2)
475 eps = RGBA(1, 1, 1, 63);
476
477 bool imagesOk = tcu::pixelThresholdCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame,
478 eps + pixelFmt.getColorThreshold(), tcu::COMPARE_LOG_RESULT);
479
480 if (!imagesOk)
481 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
482 }
483 }
484
485 } // namespace egl
486 } // namespace deqp
487