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 tcu::TestLog;
52 using tcu::RGBA;
53 using std::vector;
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)
70 : x (0)
71 , y (0)
72 , width (0)
73 , height (0)
74 , color (0)
75 {
76 }
77
78 int x;
79 int y;
80 int width;
81 int height;
82 tcu::RGBA color;
83 };
84
85 struct ApiFunctions
86 {
87 glw::Functions gl;
88 };
89
computeRandomClear(de::Random & rnd,int width,int height)90 static ClearOp computeRandomClear (de::Random& rnd, int width, int height)
91 {
92 int w = rnd.getInt(1, width);
93 int h = rnd.getInt(1, height);
94 int x = rnd.getInt(0, width-w);
95 int y = rnd.getInt(0, height-h);
96 tcu::RGBA col (rnd.getUint32());
97
98 return ClearOp(x, y, w, h, col);
99 }
100
renderReference(tcu::Surface & dst,const vector<ClearOp> & clears,const tcu::PixelFormat & pixelFormat)101 static void renderReference (tcu::Surface& dst, const vector<ClearOp>& clears, const tcu::PixelFormat& pixelFormat)
102 {
103 for (vector<ClearOp>::const_iterator clearIter = clears.begin(); clearIter != clears.end(); clearIter++)
104 {
105 tcu::PixelBufferAccess access = tcu::getSubregion(dst.getAccess(), clearIter->x, clearIter->y, 0, clearIter->width, clearIter->height, 1);
106 tcu::clear(access, pixelFormat.convertColor(clearIter->color).toIVec());
107 }
108 }
109
renderClear(EGLint api,const ApiFunctions & func,const ClearOp & clear)110 static void renderClear (EGLint api, const ApiFunctions& func, const ClearOp& clear)
111 {
112 switch (api)
113 {
114 case EGL_OPENGL_ES_BIT: gles1::clear(clear.x, clear.y, clear.width, clear.height, clear.color.toVec()); break;
115 case EGL_OPENGL_ES2_BIT: gles2::clear(func.gl, clear.x, clear.y, clear.width, clear.height, clear.color.toVec()); break;
116 case EGL_OPENGL_ES3_BIT_KHR: gles2::clear(func.gl, clear.x, clear.y, clear.width, clear.height, clear.color.toVec()); break;
117 case EGL_OPENVG_BIT: vg::clear (clear.x, clear.y, clear.width, clear.height, clear.color.toVec()); break;
118 default:
119 DE_ASSERT(DE_FALSE);
120 }
121 }
122
finish(EGLint api,const ApiFunctions & func)123 static void finish (EGLint api, const ApiFunctions& func)
124 {
125 switch (api)
126 {
127 case EGL_OPENGL_ES_BIT: gles1::finish(); break;
128 case EGL_OPENGL_ES2_BIT: gles2::finish(func.gl); break;
129 case EGL_OPENGL_ES3_BIT_KHR: gles2::finish(func.gl); break;
130 case EGL_OPENVG_BIT: vg::finish(); break;
131 default:
132 DE_ASSERT(DE_FALSE);
133 }
134 }
135
readPixels(EGLint api,const ApiFunctions & func,tcu::Surface & dst)136 static void readPixels (EGLint api, const ApiFunctions& func, tcu::Surface& dst)
137 {
138 switch (api)
139 {
140 case EGL_OPENGL_ES_BIT: gles1::readPixels (dst, 0, 0, dst.getWidth(), dst.getHeight()); break;
141 case EGL_OPENGL_ES2_BIT: gles2::readPixels (func.gl, dst, 0, 0, dst.getWidth(), dst.getHeight()); break;
142 case EGL_OPENGL_ES3_BIT_KHR: gles2::readPixels (func.gl, dst, 0, 0, dst.getWidth(), dst.getHeight()); break;
143 case EGL_OPENVG_BIT: vg::readPixels (dst, 0, 0, dst.getWidth(), dst.getHeight()); break;
144 default:
145 DE_ASSERT(DE_FALSE);
146 }
147 }
148
getPixelFormat(const Library & egl,EGLDisplay display,EGLConfig config)149 static tcu::PixelFormat getPixelFormat (const Library& egl, EGLDisplay display, EGLConfig config)
150 {
151 tcu::PixelFormat pixelFmt;
152
153 egl.getConfigAttrib(display, config, EGL_RED_SIZE, &pixelFmt.redBits);
154 egl.getConfigAttrib(display, config, EGL_GREEN_SIZE, &pixelFmt.greenBits);
155 egl.getConfigAttrib(display, config, EGL_BLUE_SIZE, &pixelFmt.blueBits);
156 egl.getConfigAttrib(display, config, EGL_ALPHA_SIZE, &pixelFmt.alphaBits);
157
158 return pixelFmt;
159 }
160
161 // SingleThreadColorClearCase
162
SingleThreadColorClearCase(EglTestContext & eglTestCtx,const char * name,const char * description,EGLint api,EGLint surfaceType,const eglu::FilterList & filters,int numContextsPerApi)163 SingleThreadColorClearCase::SingleThreadColorClearCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi)
164 : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
165 {
166 }
167
executeForContexts(EGLDisplay display,EGLSurface surface,const Config & config,const std::vector<std::pair<EGLint,EGLContext>> & contexts)168 void SingleThreadColorClearCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts)
169 {
170 const Library& egl = m_eglTestCtx.getLibrary();
171
172 const tcu::IVec2 surfaceSize = eglu::getSurfaceSize(egl, display, surface);
173 const int width = surfaceSize.x();
174 const int height = surfaceSize.y();
175
176 TestLog& log = m_testCtx.getLog();
177
178 tcu::Surface refFrame (width, height);
179 tcu::Surface frame (width, height);
180 tcu::PixelFormat pixelFmt = getPixelFormat(egl, display, config.config);
181
182 de::Random rnd (deStringHash(getName()));
183 vector<ClearOp> clears;
184 const int ctxClears = 2;
185 const int numIters = 3;
186
187 ApiFunctions funcs;
188
189 m_eglTestCtx.initGLFunctions(&funcs.gl, glu::ApiType::es(2,0));
190
191 // Clear to black using first context.
192 {
193 EGLint api = contexts[0].first;
194 EGLContext context = contexts[0].second;
195 ClearOp clear (0, 0, width, height, RGBA::black());
196
197 egl.makeCurrent(display, surface, surface, context);
198 EGLU_CHECK_MSG(egl, "eglMakeCurrent");
199
200 renderClear(api, funcs, clear);
201 finish(api, funcs);
202 clears.push_back(clear);
203 }
204
205 // Render.
206 for (int iterNdx = 0; iterNdx < numIters; iterNdx++)
207 {
208 for (vector<std::pair<EGLint, EGLContext> >::const_iterator ctxIter = contexts.begin(); ctxIter != contexts.end(); ctxIter++)
209 {
210 EGLint api = ctxIter->first;
211 EGLContext context = ctxIter->second;
212
213 egl.makeCurrent(display, surface, surface, context);
214 EGLU_CHECK_MSG(egl, "eglMakeCurrent");
215
216 for (int clearNdx = 0; clearNdx < ctxClears; clearNdx++)
217 {
218 ClearOp clear = computeRandomClear(rnd, width, height);
219
220 renderClear(api, funcs, clear);
221 clears.push_back(clear);
222 }
223
224 finish(api, funcs);
225 }
226 }
227
228 // Read pixels using first context. \todo [pyry] Randomize?
229 {
230 EGLint api = contexts[0].first;
231 EGLContext context = contexts[0].second;
232
233 egl.makeCurrent(display, surface, surface, context);
234 EGLU_CHECK_MSG(egl, "eglMakeCurrent");
235
236 readPixels(api, funcs, frame);
237 }
238
239 egl.makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
240 EGLU_CHECK_MSG(egl, "eglMakeCurrent");
241
242 // Render reference.
243 renderReference(refFrame, clears, pixelFmt);
244
245 // Compare images
246 {
247 tcu::RGBA eps = pixelFmt.alphaBits == 1 ? RGBA(1,1,1,127) : RGBA(1,1,1,1);
248 bool imagesOk = tcu::pixelThresholdCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, eps + pixelFmt.getColorThreshold(), tcu::COMPARE_LOG_RESULT);
249
250 if (!imagesOk)
251 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
252 }
253 }
254
255 // MultiThreadColorClearCase
256
257 enum
258 {
259 NUM_CLEARS_PER_PACKET = 2 //!< Number of clears performed in one context activation in one thread.
260 };
261
262 class ColorClearThread;
263
264 typedef de::SharedPtr<ColorClearThread> ColorClearThreadSp;
265 typedef de::SharedPtr<de::Semaphore> SemaphoreSp;
266
267 struct ClearPacket
268 {
ClearPacketdeqp::egl::ClearPacket269 ClearPacket (void)
270 {
271 }
272
273 ClearOp clears[NUM_CLEARS_PER_PACKET];
274 SemaphoreSp wait;
275 SemaphoreSp signal;
276 };
277
278 class ColorClearThread : public de::Thread
279 {
280 public:
ColorClearThread(const Library & egl,EGLDisplay display,EGLSurface surface,EGLContext context,EGLint api,const ApiFunctions & funcs,const std::vector<ClearPacket> & packets)281 ColorClearThread (const Library& egl, EGLDisplay display, EGLSurface surface, EGLContext context, EGLint api, const ApiFunctions& funcs, const std::vector<ClearPacket>& packets)
282 : m_egl (egl)
283 , m_display (display)
284 , m_surface (surface)
285 , m_context (context)
286 , m_api (api)
287 , m_funcs (funcs)
288 , m_packets (packets)
289 {
290 }
291
run(void)292 void run (void)
293 {
294 for (std::vector<ClearPacket>::const_iterator packetIter = m_packets.begin(); packetIter != m_packets.end(); packetIter++)
295 {
296 // Wait until it is our turn.
297 packetIter->wait->decrement();
298
299 // Acquire context.
300 m_egl.makeCurrent(m_display, m_surface, m_surface, m_context);
301
302 // Execute clears.
303 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(packetIter->clears); ndx++)
304 renderClear(m_api, m_funcs, packetIter->clears[ndx]);
305
306 finish(m_api, m_funcs);
307 // Release context.
308 m_egl.makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
309
310 // Signal completion.
311 packetIter->signal->increment();
312 }
313 m_egl.releaseThread();
314 }
315
316 private:
317 const Library& m_egl;
318 EGLDisplay m_display;
319 EGLSurface m_surface;
320 EGLContext m_context;
321 EGLint m_api;
322 const ApiFunctions& m_funcs;
323 const std::vector<ClearPacket>& m_packets;
324 };
325
MultiThreadColorClearCase(EglTestContext & eglTestCtx,const char * name,const char * description,EGLint api,EGLint surfaceType,const eglu::FilterList & filters,int numContextsPerApi)326 MultiThreadColorClearCase::MultiThreadColorClearCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi)
327 : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
328 {
329 }
330
executeForContexts(EGLDisplay display,EGLSurface surface,const Config & config,const std::vector<std::pair<EGLint,EGLContext>> & contexts)331 void MultiThreadColorClearCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts)
332 {
333 const Library& egl = m_eglTestCtx.getLibrary();
334
335 const tcu::IVec2 surfaceSize = eglu::getSurfaceSize(egl, display, surface);
336 const int width = surfaceSize.x();
337 const int height = surfaceSize.y();
338
339 TestLog& log = m_testCtx.getLog();
340
341 tcu::Surface refFrame (width, height);
342 tcu::Surface frame (width, height);
343 tcu::PixelFormat pixelFmt = getPixelFormat(egl, display, config.config);
344
345 de::Random rnd (deStringHash(getName()));
346
347 ApiFunctions funcs;
348
349 m_eglTestCtx.initGLFunctions(&funcs.gl, glu::ApiType::es(2,0));
350
351 // Create clear packets.
352 const int numPacketsPerThread = 2;
353 int numThreads = (int)contexts.size();
354 int numPackets = numThreads * numPacketsPerThread;
355
356 vector<SemaphoreSp> semaphores (numPackets+1);
357 vector<vector<ClearPacket> > packets (numThreads);
358 vector<ColorClearThreadSp> threads (numThreads);
359
360 // Initialize semaphores.
361 for (vector<SemaphoreSp>::iterator sem = semaphores.begin(); sem != semaphores.end(); ++sem)
362 *sem = SemaphoreSp(new de::Semaphore(0));
363
364 // Create packets.
365 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
366 {
367 packets[threadNdx].resize(numPacketsPerThread);
368
369 for (int packetNdx = 0; packetNdx < numPacketsPerThread; packetNdx++)
370 {
371 ClearPacket& packet = packets[threadNdx][packetNdx];
372
373 // Threads take turns with packets.
374 packet.wait = semaphores[packetNdx*numThreads + threadNdx];
375 packet.signal = semaphores[packetNdx*numThreads + threadNdx + 1];
376
377 for (int clearNdx = 0; clearNdx < DE_LENGTH_OF_ARRAY(packet.clears); clearNdx++)
378 {
379 // First clear is always full-screen black.
380 if (threadNdx == 0 && packetNdx == 0 && clearNdx == 0)
381 packet.clears[clearNdx] = ClearOp(0, 0, width, height, RGBA::black());
382 else
383 packet.clears[clearNdx] = computeRandomClear(rnd, width, height);
384 }
385 }
386 }
387
388 // Create and launch threads (actual rendering starts once first semaphore is signaled).
389 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
390 {
391 threads[threadNdx] = ColorClearThreadSp(new ColorClearThread(egl, display, surface, contexts[threadNdx].second, contexts[threadNdx].first, funcs, packets[threadNdx]));
392 threads[threadNdx]->start();
393 }
394
395 // Signal start and wait until complete.
396 semaphores.front()->increment();
397 semaphores.back()->decrement();
398
399 // Read pixels using first context. \todo [pyry] Randomize?
400 {
401 EGLint api = contexts[0].first;
402 EGLContext context = contexts[0].second;
403
404 egl.makeCurrent(display, surface, surface, context);
405 EGLU_CHECK_MSG(egl, "eglMakeCurrent");
406
407 readPixels(api, funcs, frame);
408 }
409
410 egl.makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
411 EGLU_CHECK_MSG(egl, "eglMakeCurrent");
412
413 // Join threads.
414 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
415 threads[threadNdx]->join();
416
417 // Render reference.
418 for (int packetNdx = 0; packetNdx < numPacketsPerThread; packetNdx++)
419 {
420 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
421 {
422 const ClearPacket& packet = packets[threadNdx][packetNdx];
423 for (int clearNdx = 0; clearNdx < DE_LENGTH_OF_ARRAY(packet.clears); clearNdx++)
424 {
425 tcu::PixelBufferAccess access = tcu::getSubregion(refFrame.getAccess(),
426 packet.clears[clearNdx].x, packet.clears[clearNdx].y, 0,
427 packet.clears[clearNdx].width, packet.clears[clearNdx].height, 1);
428 tcu::clear(access, pixelFmt.convertColor(packet.clears[clearNdx].color).toIVec());
429 }
430 }
431 }
432
433 // Compare images
434 {
435 tcu::RGBA eps = pixelFmt.alphaBits == 1 ? RGBA(1,1,1,127) : RGBA(1,1,1,1);
436 bool imagesOk = tcu::pixelThresholdCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, eps + pixelFmt.getColorThreshold(), tcu::COMPARE_LOG_RESULT);
437
438 if (!imagesOk)
439 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
440 }
441 }
442
443 } // egl
444 } // deqp
445