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 "deRandom.hpp" 27 #include "deString.h" 28 #include "tcuImageCompare.hpp" 29 #include "tcuVector.hpp" 30 #include "tcuTextureUtil.hpp" 31 #include "deThread.hpp" 32 #include "deSemaphore.hpp" 33 #include "deSharedPtr.hpp" 34 #include "teglGLES1RenderUtil.hpp" 35 #include "teglGLES2RenderUtil.hpp" 36 #include "teglVGRenderUtil.hpp" 37 38 #include <memory> 39 #include <iterator> 40 41 #include <EGL/eglext.h> 42 43 #if !defined(EGL_OPENGL_ES3_BIT_KHR) 44 # define EGL_OPENGL_ES3_BIT_KHR 0x0040 45 #endif 46 #if !defined(EGL_CONTEXT_MAJOR_VERSION_KHR) 47 # define EGL_CONTEXT_MAJOR_VERSION_KHR EGL_CONTEXT_CLIENT_VERSION 48 #endif 49 50 using tcu::TestLog; 51 using tcu::RGBA; 52 53 using std::vector; 54 55 namespace deqp 56 { 57 namespace egl 58 { 59 60 // Utilities. 61 62 struct ClearOp 63 { 64 ClearOp (int x_, int y_, int width_, int height_, const tcu::RGBA& color_) 65 : x (x_) 66 , y (y_) 67 , width (width_) 68 , height (height_) 69 , color (color_) 70 { 71 } 72 73 ClearOp (void) 74 : x (0) 75 , y (0) 76 , width (0) 77 , height (0) 78 , color (0) 79 { 80 } 81 82 int x; 83 int y; 84 int width; 85 int height; 86 tcu::RGBA color; 87 }; 88 89 static ClearOp computeRandomClear (de::Random& rnd, int width, int height) 90 { 91 int w = rnd.getInt(1, width); 92 int h = rnd.getInt(1, height); 93 int x = rnd.getInt(0, width-w); 94 int y = rnd.getInt(0, height-h); 95 tcu::RGBA col (rnd.getUint32()); 96 97 return ClearOp(x, y, w, h, col); 98 } 99 100 static void renderReference (tcu::Surface& dst, const vector<ClearOp>& clears, const tcu::PixelFormat& pixelFormat) 101 { 102 for (vector<ClearOp>::const_iterator clearIter = clears.begin(); clearIter != clears.end(); clearIter++) 103 { 104 tcu::PixelBufferAccess access = tcu::getSubregion(dst.getAccess(), clearIter->x, clearIter->y, 0, clearIter->width, clearIter->height, 1); 105 tcu::clear(access, pixelFormat.convertColor(clearIter->color).toIVec()); 106 } 107 } 108 109 static void renderClear (EGLint api, const ClearOp& clear) 110 { 111 switch (api) 112 { 113 case EGL_OPENGL_ES_BIT: gles1::clear(clear.x, clear.y, clear.width, clear.height, clear.color.toVec()); break; 114 case EGL_OPENGL_ES2_BIT: gles2::clear(clear.x, clear.y, clear.width, clear.height, clear.color.toVec()); break; 115 case EGL_OPENGL_ES3_BIT_KHR: gles2::clear(clear.x, clear.y, clear.width, clear.height, clear.color.toVec()); break; 116 case EGL_OPENVG_BIT: vg::clear (clear.x, clear.y, clear.width, clear.height, clear.color.toVec()); break; 117 default: 118 DE_ASSERT(DE_FALSE); 119 } 120 } 121 122 static void readPixels (EGLint api, tcu::Surface& dst) 123 { 124 switch (api) 125 { 126 case EGL_OPENGL_ES_BIT: gles1::readPixels (dst, 0, 0, dst.getWidth(), dst.getHeight()); break; 127 case EGL_OPENGL_ES2_BIT: gles2::readPixels (dst, 0, 0, dst.getWidth(), dst.getHeight()); break; 128 case EGL_OPENGL_ES3_BIT_KHR: gles2::readPixels (dst, 0, 0, dst.getWidth(), dst.getHeight()); break; 129 case EGL_OPENVG_BIT: vg::readPixels (dst, 0, 0, dst.getWidth(), dst.getHeight()); break; 130 default: 131 DE_ASSERT(DE_FALSE); 132 } 133 } 134 135 // SingleThreadColorClearCase 136 137 SingleThreadColorClearCase::SingleThreadColorClearCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const std::vector<EGLint>& configIds, int numContextsPerApi) 138 : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, configIds, numContextsPerApi) 139 { 140 } 141 142 void SingleThreadColorClearCase::executeForContexts (tcu::egl::Display& display, tcu::egl::Surface& surface, EGLConfig config, const std::vector<std::pair<EGLint, tcu::egl::Context*> >& contexts) 143 { 144 int width = surface.getWidth(); 145 int height = surface.getHeight(); 146 147 TestLog& log = m_testCtx.getLog(); 148 149 tcu::Surface refFrame (width, height); 150 tcu::Surface frame (width, height); 151 tcu::PixelFormat pixelFmt; 152 153 de::Random rnd (deStringHash(getName())); 154 vector<ClearOp> clears; 155 const int ctxClears = 2; 156 const int numIters = 3; 157 158 // Query pixel format. 159 display.describeConfig(config, pixelFmt); 160 161 // Clear to black using first context. 162 { 163 EGLint api = contexts[0].first; 164 tcu::egl::Context* context = contexts[0].second; 165 ClearOp clear (0, 0, width, height, RGBA::black); 166 167 eglMakeCurrent(display.getEGLDisplay(), surface.getEGLSurface(), surface.getEGLSurface(), context->getEGLContext()); 168 TCU_CHECK_EGL(); 169 170 renderClear(api, clear); 171 clears.push_back(clear); 172 } 173 174 // Render. 175 for (int iterNdx = 0; iterNdx < numIters; iterNdx++) 176 { 177 for (vector<std::pair<EGLint, tcu::egl::Context*> >::const_iterator ctxIter = contexts.begin(); ctxIter != contexts.end(); ctxIter++) 178 { 179 EGLint api = ctxIter->first; 180 tcu::egl::Context* context = ctxIter->second; 181 182 eglMakeCurrent(display.getEGLDisplay(), surface.getEGLSurface(), surface.getEGLSurface(), context->getEGLContext()); 183 TCU_CHECK_EGL(); 184 185 for (int clearNdx = 0; clearNdx < ctxClears; clearNdx++) 186 { 187 ClearOp clear = computeRandomClear(rnd, width, height); 188 189 renderClear(api, clear); 190 clears.push_back(clear); 191 } 192 } 193 } 194 195 // Read pixels using first context. \todo [pyry] Randomize? 196 { 197 EGLint api = contexts[0].first; 198 tcu::egl::Context* context = contexts[0].second; 199 200 eglMakeCurrent(display.getEGLDisplay(), surface.getEGLSurface(), surface.getEGLSurface(), context->getEGLContext()); 201 TCU_CHECK_EGL(); 202 203 readPixels(api, frame); 204 } 205 206 // Render reference. 207 renderReference(refFrame, clears, pixelFmt); 208 209 // Compare images 210 { 211 bool imagesOk = tcu::pixelThresholdCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, RGBA(1,1,1,1) + pixelFmt.getColorThreshold(), tcu::COMPARE_LOG_RESULT); 212 213 if (!imagesOk) 214 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); 215 } 216 } 217 218 // MultiThreadColorClearCase 219 220 enum 221 { 222 NUM_CLEARS_PER_PACKET = 2 //!< Number of clears performed in one context activation in one thread. 223 }; 224 225 class ColorClearThread; 226 227 typedef de::SharedPtr<ColorClearThread> ColorClearThreadSp; 228 typedef de::SharedPtr<de::Semaphore> SemaphoreSp; 229 230 struct ClearPacket 231 { 232 ClearPacket (void) 233 { 234 } 235 236 ClearOp clears[NUM_CLEARS_PER_PACKET]; 237 SemaphoreSp wait; 238 SemaphoreSp signal; 239 }; 240 241 class ColorClearThread : public de::Thread 242 { 243 public: 244 ColorClearThread (tcu::egl::Display& display, tcu::egl::Surface& surface, tcu::egl::Context& context, EGLint api, const std::vector<ClearPacket>& packets) 245 : m_display (display) 246 , m_surface (surface) 247 , m_context (context) 248 , m_api (api) 249 , m_packets (packets) 250 { 251 } 252 253 void run (void) 254 { 255 for (std::vector<ClearPacket>::const_iterator packetIter = m_packets.begin(); packetIter != m_packets.end(); packetIter++) 256 { 257 // Wait until it is our turn. 258 packetIter->wait->decrement(); 259 260 // Acquire context. 261 eglMakeCurrent(m_display.getEGLDisplay(), m_surface.getEGLSurface(), m_surface.getEGLSurface(), m_context.getEGLContext()); 262 263 // Execute clears. 264 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(packetIter->clears); ndx++) 265 renderClear(m_api, packetIter->clears[ndx]); 266 267 // Release context. 268 eglMakeCurrent(m_display.getEGLDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 269 270 // Signal completion. 271 packetIter->signal->increment(); 272 } 273 } 274 275 private: 276 tcu::egl::Display& m_display; 277 tcu::egl::Surface& m_surface; 278 tcu::egl::Context& m_context; 279 EGLint m_api; 280 const std::vector<ClearPacket>& m_packets; 281 }; 282 283 MultiThreadColorClearCase::MultiThreadColorClearCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const std::vector<EGLint>& configIds, int numContextsPerApi) 284 : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, configIds, numContextsPerApi) 285 { 286 } 287 288 void MultiThreadColorClearCase::executeForContexts (tcu::egl::Display& display, tcu::egl::Surface& surface, EGLConfig config, const std::vector<std::pair<EGLint, tcu::egl::Context*> >& contexts) 289 { 290 int width = surface.getWidth(); 291 int height = surface.getHeight(); 292 293 TestLog& log = m_testCtx.getLog(); 294 295 tcu::Surface refFrame (width, height); 296 tcu::Surface frame (width, height); 297 tcu::PixelFormat pixelFmt; 298 299 de::Random rnd (deStringHash(getName())); 300 301 // Query pixel format. 302 display.describeConfig(config, pixelFmt); 303 304 // Create clear packets. 305 const int numPacketsPerThread = 2; 306 int numThreads = (int)contexts.size(); 307 int numPackets = numThreads * numPacketsPerThread; 308 309 vector<SemaphoreSp> semaphores (numPackets+1); 310 vector<vector<ClearPacket> > packets (numThreads); 311 vector<ColorClearThreadSp> threads (numThreads); 312 313 // Initialize semaphores. 314 for (vector<SemaphoreSp>::iterator sem = semaphores.begin(); sem != semaphores.end(); ++sem) 315 *sem = SemaphoreSp(new de::Semaphore(0)); 316 317 // Create packets. 318 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++) 319 { 320 packets[threadNdx].resize(numPacketsPerThread); 321 322 for (int packetNdx = 0; packetNdx < numPacketsPerThread; packetNdx++) 323 { 324 ClearPacket& packet = packets[threadNdx][packetNdx]; 325 326 // Threads take turns with packets. 327 packet.wait = semaphores[packetNdx*numThreads + threadNdx]; 328 packet.signal = semaphores[packetNdx*numThreads + threadNdx + 1]; 329 330 for (int clearNdx = 0; clearNdx < DE_LENGTH_OF_ARRAY(packet.clears); clearNdx++) 331 { 332 // First clear is always full-screen black. 333 if (threadNdx == 0 && packetNdx == 0 && clearNdx == 0) 334 packet.clears[clearNdx] = ClearOp(0, 0, width, height, RGBA::black); 335 else 336 packet.clears[clearNdx] = computeRandomClear(rnd, width, height); 337 } 338 } 339 } 340 341 // Create and launch threads (actual rendering starts once first semaphore is signaled). 342 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++) 343 { 344 threads[threadNdx] = ColorClearThreadSp(new ColorClearThread(display, surface, *contexts[threadNdx].second, contexts[threadNdx].first, packets[threadNdx])); 345 threads[threadNdx]->start(); 346 } 347 348 // Signal start and wait until complete. 349 semaphores.front()->increment(); 350 semaphores.back()->decrement(); 351 352 // Read pixels using first context. \todo [pyry] Randomize? 353 { 354 EGLint api = contexts[0].first; 355 tcu::egl::Context* context = contexts[0].second; 356 357 eglMakeCurrent(display.getEGLDisplay(), surface.getEGLSurface(), surface.getEGLSurface(), context->getEGLContext()); 358 TCU_CHECK_EGL(); 359 360 readPixels(api, frame); 361 } 362 363 // Join threads. 364 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++) 365 threads[threadNdx]->join(); 366 367 // Render reference. 368 for (int packetNdx = 0; packetNdx < numPacketsPerThread; packetNdx++) 369 { 370 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++) 371 { 372 const ClearPacket& packet = packets[threadNdx][packetNdx]; 373 for (int clearNdx = 0; clearNdx < DE_LENGTH_OF_ARRAY(packet.clears); clearNdx++) 374 { 375 tcu::PixelBufferAccess access = tcu::getSubregion(refFrame.getAccess(), 376 packet.clears[clearNdx].x, packet.clears[clearNdx].y, 0, 377 packet.clears[clearNdx].width, packet.clears[clearNdx].height, 1); 378 tcu::clear(access, pixelFmt.convertColor(packet.clears[clearNdx].color).toIVec()); 379 } 380 } 381 } 382 383 // Compare images 384 { 385 bool imagesOk = tcu::pixelThresholdCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, RGBA(1,1,1,1) + pixelFmt.getColorThreshold(), tcu::COMPARE_LOG_RESULT); 386 387 if (!imagesOk) 388 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); 389 } 390 } 391 392 } // egl 393 } // deqp 394