xref: /aosp_15_r20/external/deqp/modules/egl/teglColorClearCase.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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