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 32using 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 40namespace tcu 41{ 42namespace Android 43{ 44 45enum 46{ 47 MESSAGE_QUEUE_SIZE = 8 //!< Length of RenderThread message queue. 48}; 49 50#if defined(DE_DEBUG) 51static 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 72RenderThread::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 85RenderThread::~RenderThread (void) 86{ 87} 88 89void RenderThread::start (void) 90{ 91 m_threadRunning = true; 92 Thread::start(); 93} 94 95void 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 106void 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 113void RenderThread::pause (void) 114{ 115 enqueue(Message(MESSAGE_PAUSE)); 116} 117 118void RenderThread::resume (void) 119{ 120 enqueue(Message(MESSAGE_RESUME)); 121} 122 123void RenderThread::sync (void) 124{ 125 de::Semaphore waitSem(0); 126 enqueue(Message(MESSAGE_SYNC, &waitSem)); 127 waitSem.decrement(); 128} 129 130void 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 quick 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 226void 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 307RenderActivity::RenderActivity (ANativeActivity* activity) 308 : NativeActivity(activity) 309 , m_thread (DE_NULL) 310{ 311 DBG_PRINT(("RenderActivity::RenderActivity()")); 312} 313 314RenderActivity::~RenderActivity (void) 315{ 316 DBG_PRINT(("RenderActivity::~RenderActivity()")); 317} 318 319void RenderActivity::setThread (RenderThread* thread) 320{ 321 m_thread = thread; 322} 323 324void RenderActivity::onStart (void) 325{ 326 DBG_PRINT(("RenderActivity::onStart()")); 327} 328 329void RenderActivity::onResume (void) 330{ 331 DBG_PRINT(("RenderActivity::onResume()")); 332 333 // Resume (or start) test execution 334 m_thread->resume(); 335} 336 337void RenderActivity::onPause (void) 338{ 339 DBG_PRINT(("RenderActivity::onPause()")); 340 341 // Pause test execution 342 m_thread->pause(); 343} 344 345void RenderActivity::onStop (void) 346{ 347 DBG_PRINT(("RenderActivity::onStop()")); 348} 349 350void RenderActivity::onDestroy (void) 351{ 352 DBG_PRINT(("RenderActivity::onDestroy()")); 353} 354 355void RenderActivity::onNativeWindowCreated (ANativeWindow* window) 356{ 357 DBG_PRINT(("RenderActivity::onNativeWindowCreated()")); 358 m_thread->enqueue(Message(MESSAGE_WINDOW_CREATED, window)); 359} 360 361void RenderActivity::onNativeWindowResized (ANativeWindow* window) 362{ 363 DBG_PRINT(("RenderActivity::onNativeWindowResized()")); 364 m_thread->enqueue(Message(MESSAGE_WINDOW_RESIZED, window)); 365} 366 367void RenderActivity::onNativeWindowRedrawNeeded (ANativeWindow* window) 368{ 369 DE_UNREF(window); 370} 371 372void 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 379void RenderActivity::onInputQueueCreated (AInputQueue* queue) 380{ 381 DBG_PRINT(("RenderActivity::onInputQueueCreated()")); 382 m_thread->enqueue(Message(MESSAGE_INPUT_QUEUE_CREATED, queue)); 383} 384 385void 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