1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program Tester Core 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 RenderActivity base class. 22 *//*--------------------------------------------------------------------*/ 23 24 #include "tcuAndroidRenderActivity.hpp" 25 #include "deSemaphore.hpp" 26 27 #include <android/window.h> 28 29 #include <string> 30 #include <stdlib.h> 31 32 using std::string; 33 34 #if defined(DE_DEBUG) 35 # define DBG_PRINT(X) print X 36 #else 37 # define DBG_PRINT(X) 38 #endif 39 40 namespace tcu 41 { 42 namespace Android 43 { 44 45 enum 46 { 47 MESSAGE_QUEUE_SIZE = 8 //!< Length of RenderThread message queue. 48 }; 49 50 #if defined(DE_DEBUG) 51 static const char* getMessageTypeName (MessageType type) 52 { 53 static const char* s_names[] = 54 { 55 "RESUME", 56 "PAUSE", 57 "FINISH", 58 "WINDOW_CREATED", 59 "WINDOW_RESIZED", 60 "WINDOW_DESTROYED", 61 "INPUT_QUEUE_CREATED", 62 "INPUT_QUEUE_DESTROYED", 63 "SYNC" 64 }; 65 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_names) == MESSAGETYPE_LAST); 66 return s_names[type]; 67 } 68 #endif 69 70 // RenderThread 71 72 RenderThread::RenderThread (NativeActivity& activity) 73 : m_activity (activity) 74 , m_msgQueue (MESSAGE_QUEUE_SIZE) 75 , m_threadRunning (false) 76 , m_inputQueue (DE_NULL) 77 , m_windowState (WINDOWSTATE_NOT_CREATED) 78 , m_window (DE_NULL) 79 , m_paused (false) 80 , m_finish (false) 81 , m_receivedFirstResize(false) 82 { 83 } 84 85 RenderThread::~RenderThread (void) 86 { 87 } 88 89 void RenderThread::start (void) 90 { 91 m_threadRunning = true; 92 Thread::start(); 93 } 94 95 void RenderThread::stop (void) 96 { 97 // Queue finish command 98 enqueue(Message(MESSAGE_FINISH)); 99 100 // Wait for thread to terminate 101 join(); 102 103 m_threadRunning = false; 104 } 105 106 void RenderThread::enqueue (const Message& message) 107 { 108 // \note Thread must be running or otherwise nobody is going to drain the queue. 109 DE_ASSERT(m_threadRunning); 110 m_msgQueue.pushFront(message); 111 } 112 113 void RenderThread::pause (void) 114 { 115 enqueue(Message(MESSAGE_PAUSE)); 116 } 117 118 void RenderThread::resume (void) 119 { 120 enqueue(Message(MESSAGE_RESUME)); 121 } 122 123 void RenderThread::sync (void) 124 { 125 de::Semaphore waitSem(0); 126 enqueue(Message(MESSAGE_SYNC, &waitSem)); 127 waitSem.decrement(); 128 } 129 130 void RenderThread::processMessage (const Message& message) 131 { 132 DBG_PRINT(("RenderThread::processMessage(): message = { %s, %p }\n", getMessageTypeName(message.type), message.payload.window)); 133 134 switch (message.type) 135 { 136 case MESSAGE_RESUME: m_paused = false; break; 137 case MESSAGE_PAUSE: m_paused = true; break; 138 case MESSAGE_FINISH: m_finish = true; break; 139 140 // \note While Platform / WindowRegistry are currently multi-window -capable, 141 // the fact that platform gives us windows too late / at unexpected times 142 // forces us to do some sanity checking and limit system to one window here. 143 case MESSAGE_WINDOW_CREATED: 144 if (m_windowState != WINDOWSTATE_NOT_CREATED && m_windowState != WINDOWSTATE_DESTROYED) 145 throw InternalError("Got unexpected onNativeWindowCreated() event from system"); 146 147 // The documented behavior for the callbacks is that the native activity 148 // will get a call to onNativeWindowCreated(), at which point it should have 149 // a surface to render to, and can then start immediately. 150 // 151 // The actual creation process has the framework making calls to both 152 // onNativeWindowCreated() and then onNativeWindowResized(). The test 153 // waits for that first resize before it considers the window ready for 154 // rendering. 155 // 156 // However subsequent events in the framework may cause the window to be 157 // recreated at a new position without a size change, which sends on 158 // onNativeWindowDestroyed(), and then on onNativeWindowCreated() without 159 // a follow-up onNativeWindowResized(). If this happens, the test will 160 // stop rendering as it is no longer in the ready state, and a watchdog 161 // thread will eventually kill the test, causing it to fail. We therefore 162 // set the window state back to READY and process the window creation here 163 // if we have already observed that first resize call. 164 if (!m_receivedFirstResize) { 165 m_windowState = WINDOWSTATE_NOT_INITIALIZED; 166 } else { 167 m_windowState = WINDOWSTATE_READY; 168 onWindowCreated(message.payload.window); 169 } 170 m_window = message.payload.window; 171 break; 172 173 case MESSAGE_WINDOW_RESIZED: 174 if (m_window != message.payload.window) 175 throw InternalError("Got onNativeWindowResized() event targeting different window"); 176 177 // Record that we've the first resize event, in case the window is 178 // recreated later without a resize. 179 m_receivedFirstResize = true; 180 181 if (m_windowState == WINDOWSTATE_NOT_INITIALIZED) 182 { 183 // Got first resize event, window is ready for use. 184 m_windowState = WINDOWSTATE_READY; 185 onWindowCreated(message.payload.window); 186 } 187 else if (m_windowState == WINDOWSTATE_READY) 188 onWindowResized(message.payload.window); 189 else 190 throw InternalError("Got unexpected onNativeWindowResized() event from system"); 191 192 break; 193 194 case MESSAGE_WINDOW_DESTROYED: 195 if (m_window != message.payload.window) 196 throw InternalError("Got onNativeWindowDestroyed() event targeting different window"); 197 198 if (m_windowState != WINDOWSTATE_NOT_INITIALIZED && m_windowState != WINDOWSTATE_READY) 199 throw InternalError("Got unexpected onNativeWindowDestroyed() event from system"); 200 201 if (m_windowState == WINDOWSTATE_READY) 202 onWindowDestroyed(message.payload.window); 203 204 m_windowState = WINDOWSTATE_DESTROYED; 205 m_window = DE_NULL; 206 break; 207 208 case MESSAGE_INPUT_QUEUE_CREATED: 209 m_inputQueue = message.payload.inputQueue; 210 break; 211 212 case MESSAGE_INPUT_QUEUE_DESTROYED: 213 m_inputQueue = message.payload.inputQueue; 214 break; 215 216 case MESSAGE_SYNC: 217 message.payload.semaphore->increment(); 218 break; 219 220 default: 221 throw std::runtime_error("Unknown message type"); 222 break; 223 } 224 } 225 226 void RenderThread::run (void) 227 { 228 // Init state 229 m_windowState = WINDOWSTATE_NOT_CREATED; 230 m_paused = true; 231 m_finish = false; 232 233 try 234 { 235 while (!m_finish) 236 { 237 if (m_paused || m_windowState != WINDOWSTATE_READY) 238 { 239 // Block until we are not paused and window is ready. 240 Message msg = m_msgQueue.popBack(); 241 processMessage(msg); 242 continue; 243 } 244 245 // Process available commands 246 { 247 Message msg; 248 if (m_msgQueue.tryPopBack(msg)) 249 { 250 processMessage(msg); 251 continue; 252 } 253 } 254 255 DE_ASSERT(m_windowState == WINDOWSTATE_READY); 256 257 // Process input events. 258 // \todo [2013-05-08 pyry] What if system fills up the input queue before we have window ready? 259 while (m_inputQueue && 260 AInputQueue_hasEvents(m_inputQueue) > 0) 261 { 262 AInputEvent* event; 263 TCU_CHECK(AInputQueue_getEvent(m_inputQueue, &event) >= 0); 264 onInputEvent(event); 265 AInputQueue_finishEvent(m_inputQueue, event, 1); 266 } 267 268 // Everything set up - safe to render. 269 if (!render()) 270 { 271 DBG_PRINT(("RenderThread::run(): render\n")); 272 break; 273 } 274 } 275 } 276 catch (const std::exception& e) 277 { 278 print("RenderThread: %s\n", e.what()); 279 } 280 281 // Tell activity to finish. 282 DBG_PRINT(("RenderThread::run(): done, waiting for FINISH\n")); 283 m_activity.finish(); 284 285 // Thread must keep draining message queue until FINISH message is encountered. 286 try 287 { 288 while (!m_finish) 289 { 290 Message msg = m_msgQueue.popBack(); 291 292 // Ignore all but SYNC and FINISH messages. 293 if (msg.type == MESSAGE_SYNC || msg.type == MESSAGE_FINISH) 294 processMessage(msg); 295 } 296 } 297 catch (const std::exception& e) 298 { 299 die("RenderThread: %s\n", e.what()); 300 } 301 302 DBG_PRINT(("RenderThread::run(): exiting...\n")); 303 } 304 305 // RenderActivity 306 307 RenderActivity::RenderActivity (ANativeActivity* activity) 308 : NativeActivity(activity) 309 , m_thread (DE_NULL) 310 { 311 DBG_PRINT(("RenderActivity::RenderActivity()")); 312 } 313 314 RenderActivity::~RenderActivity (void) 315 { 316 DBG_PRINT(("RenderActivity::~RenderActivity()")); 317 } 318 319 void RenderActivity::setThread (RenderThread* thread) 320 { 321 m_thread = thread; 322 } 323 324 void RenderActivity::onStart (void) 325 { 326 DBG_PRINT(("RenderActivity::onStart()")); 327 } 328 329 void RenderActivity::onResume (void) 330 { 331 DBG_PRINT(("RenderActivity::onResume()")); 332 333 // Resume (or start) test execution 334 m_thread->resume(); 335 } 336 337 void RenderActivity::onPause (void) 338 { 339 DBG_PRINT(("RenderActivity::onPause()")); 340 341 // Pause test execution 342 m_thread->pause(); 343 } 344 345 void RenderActivity::onStop (void) 346 { 347 DBG_PRINT(("RenderActivity::onStop()")); 348 } 349 350 void RenderActivity::onDestroy (void) 351 { 352 DBG_PRINT(("RenderActivity::onDestroy()")); 353 } 354 355 void RenderActivity::onNativeWindowCreated (ANativeWindow* window) 356 { 357 DBG_PRINT(("RenderActivity::onNativeWindowCreated()")); 358 m_thread->enqueue(Message(MESSAGE_WINDOW_CREATED, window)); 359 } 360 361 void RenderActivity::onNativeWindowResized (ANativeWindow* window) 362 { 363 DBG_PRINT(("RenderActivity::onNativeWindowResized()")); 364 m_thread->enqueue(Message(MESSAGE_WINDOW_RESIZED, window)); 365 } 366 367 void RenderActivity::onNativeWindowRedrawNeeded (ANativeWindow* window) 368 { 369 DE_UNREF(window); 370 } 371 372 void RenderActivity::onNativeWindowDestroyed (ANativeWindow* window) 373 { 374 DBG_PRINT(("RenderActivity::onNativeWindowDestroyed()")); 375 m_thread->enqueue(Message(MESSAGE_WINDOW_DESTROYED, window)); 376 m_thread->sync(); // Block until thread has processed all messages. 377 } 378 379 void RenderActivity::onInputQueueCreated (AInputQueue* queue) 380 { 381 DBG_PRINT(("RenderActivity::onInputQueueCreated()")); 382 m_thread->enqueue(Message(MESSAGE_INPUT_QUEUE_CREATED, queue)); 383 } 384 385 void RenderActivity::onInputQueueDestroyed (AInputQueue* queue) 386 { 387 DBG_PRINT(("RenderActivity::onInputQueueDestroyed()")); 388 m_thread->enqueue(Message(MESSAGE_INPUT_QUEUE_DESTROYED, queue)); 389 m_thread->sync(); 390 } 391 392 } // Android 393 } // tcu 394