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 bool imagesOk = tcu::pixelThresholdCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, RGBA(1,1,1,1) + pixelFmt.getColorThreshold(), tcu::COMPARE_LOG_RESULT);
248
249 if (!imagesOk)
250 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
251 }
252 }
253
254 // MultiThreadColorClearCase
255
256 enum
257 {
258 NUM_CLEARS_PER_PACKET = 2 //!< Number of clears performed in one context activation in one thread.
259 };
260
261 class ColorClearThread;
262
263 typedef de::SharedPtr<ColorClearThread> ColorClearThreadSp;
264 typedef de::SharedPtr<de::Semaphore> SemaphoreSp;
265
266 struct ClearPacket
267 {
ClearPacketdeqp::egl::ClearPacket268 ClearPacket (void)
269 {
270 }
271
272 ClearOp clears[NUM_CLEARS_PER_PACKET];
273 SemaphoreSp wait;
274 SemaphoreSp signal;
275 };
276
277 class ColorClearThread : public de::Thread
278 {
279 public:
ColorClearThread(const Library & egl,EGLDisplay display,EGLSurface surface,EGLContext context,EGLint api,const ApiFunctions & funcs,const std::vector<ClearPacket> & packets)280 ColorClearThread (const Library& egl, EGLDisplay display, EGLSurface surface, EGLContext context, EGLint api, const ApiFunctions& funcs, const std::vector<ClearPacket>& packets)
281 : m_egl (egl)
282 , m_display (display)
283 , m_surface (surface)
284 , m_context (context)
285 , m_api (api)
286 , m_funcs (funcs)
287 , m_packets (packets)
288 {
289 }
290
run(void)291 void run (void)
292 {
293 for (std::vector<ClearPacket>::const_iterator packetIter = m_packets.begin(); packetIter != m_packets.end(); packetIter++)
294 {
295 // Wait until it is our turn.
296 packetIter->wait->decrement();
297
298 // Acquire context.
299 m_egl.makeCurrent(m_display, m_surface, m_surface, m_context);
300
301 // Execute clears.
302 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(packetIter->clears); ndx++)
303 renderClear(m_api, m_funcs, packetIter->clears[ndx]);
304
305 finish(m_api, m_funcs);
306 // Release context.
307 m_egl.makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
308
309 // Signal completion.
310 packetIter->signal->increment();
311 }
312 }
313
314 private:
315 const Library& m_egl;
316 EGLDisplay m_display;
317 EGLSurface m_surface;
318 EGLContext m_context;
319 EGLint m_api;
320 const ApiFunctions& m_funcs;
321 const std::vector<ClearPacket>& m_packets;
322 };
323
MultiThreadColorClearCase(EglTestContext & eglTestCtx,const char * name,const char * description,EGLint api,EGLint surfaceType,const eglu::FilterList & filters,int numContextsPerApi)324 MultiThreadColorClearCase::MultiThreadColorClearCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi)
325 : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
326 {
327 }
328
executeForContexts(EGLDisplay display,EGLSurface surface,const Config & config,const std::vector<std::pair<EGLint,EGLContext>> & contexts)329 void MultiThreadColorClearCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts)
330 {
331 const Library& egl = m_eglTestCtx.getLibrary();
332
333 const tcu::IVec2 surfaceSize = eglu::getSurfaceSize(egl, display, surface);
334 const int width = surfaceSize.x();
335 const int height = surfaceSize.y();
336
337 TestLog& log = m_testCtx.getLog();
338
339 tcu::Surface refFrame (width, height);
340 tcu::Surface frame (width, height);
341 tcu::PixelFormat pixelFmt = getPixelFormat(egl, display, config.config);
342
343 de::Random rnd (deStringHash(getName()));
344
345 ApiFunctions funcs;
346
347 m_eglTestCtx.initGLFunctions(&funcs.gl, glu::ApiType::es(2,0));
348
349 // Create clear packets.
350 const int numPacketsPerThread = 2;
351 int numThreads = (int)contexts.size();
352 int numPackets = numThreads * numPacketsPerThread;
353
354 vector<SemaphoreSp> semaphores (numPackets+1);
355 vector<vector<ClearPacket> > packets (numThreads);
356 vector<ColorClearThreadSp> threads (numThreads);
357
358 // Initialize semaphores.
359 for (vector<SemaphoreSp>::iterator sem = semaphores.begin(); sem != semaphores.end(); ++sem)
360 *sem = SemaphoreSp(new de::Semaphore(0));
361
362 // Create packets.
363 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
364 {
365 packets[threadNdx].resize(numPacketsPerThread);
366
367 for (int packetNdx = 0; packetNdx < numPacketsPerThread; packetNdx++)
368 {
369 ClearPacket& packet = packets[threadNdx][packetNdx];
370
371 // Threads take turns with packets.
372 packet.wait = semaphores[packetNdx*numThreads + threadNdx];
373 packet.signal = semaphores[packetNdx*numThreads + threadNdx + 1];
374
375 for (int clearNdx = 0; clearNdx < DE_LENGTH_OF_ARRAY(packet.clears); clearNdx++)
376 {
377 // First clear is always full-screen black.
378 if (threadNdx == 0 && packetNdx == 0 && clearNdx == 0)
379 packet.clears[clearNdx] = ClearOp(0, 0, width, height, RGBA::black());
380 else
381 packet.clears[clearNdx] = computeRandomClear(rnd, width, height);
382 }
383 }
384 }
385
386 // Create and launch threads (actual rendering starts once first semaphore is signaled).
387 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
388 {
389 threads[threadNdx] = ColorClearThreadSp(new ColorClearThread(egl, display, surface, contexts[threadNdx].second, contexts[threadNdx].first, funcs, packets[threadNdx]));
390 threads[threadNdx]->start();
391 }
392
393 // Signal start and wait until complete.
394 semaphores.front()->increment();
395 semaphores.back()->decrement();
396
397 // Read pixels using first context. \todo [pyry] Randomize?
398 {
399 EGLint api = contexts[0].first;
400 EGLContext context = contexts[0].second;
401
402 egl.makeCurrent(display, surface, surface, context);
403 EGLU_CHECK_MSG(egl, "eglMakeCurrent");
404
405 readPixels(api, funcs, frame);
406 }
407
408 egl.makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
409 EGLU_CHECK_MSG(egl, "eglMakeCurrent");
410
411 // Join threads.
412 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
413 threads[threadNdx]->join();
414
415 // Render reference.
416 for (int packetNdx = 0; packetNdx < numPacketsPerThread; packetNdx++)
417 {
418 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
419 {
420 const ClearPacket& packet = packets[threadNdx][packetNdx];
421 for (int clearNdx = 0; clearNdx < DE_LENGTH_OF_ARRAY(packet.clears); clearNdx++)
422 {
423 tcu::PixelBufferAccess access = tcu::getSubregion(refFrame.getAccess(),
424 packet.clears[clearNdx].x, packet.clears[clearNdx].y, 0,
425 packet.clears[clearNdx].width, packet.clears[clearNdx].height, 1);
426 tcu::clear(access, pixelFmt.convertColor(packet.clears[clearNdx].color).toIVec());
427 }
428 }
429 }
430
431 // Compare images
432 {
433 bool imagesOk = tcu::pixelThresholdCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, RGBA(1,1,1,1) + pixelFmt.getColorThreshold(), tcu::COMPARE_LOG_RESULT);
434
435 if (!imagesOk)
436 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
437 }
438 }
439
440 } // egl
441 } // deqp
442