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 { 82 } 83 84 RenderThread::~RenderThread (void) 85 { 86 } 87 88 void RenderThread::start (void) 89 { 90 m_threadRunning = true; 91 Thread::start(); 92 } 93 94 void RenderThread::stop (void) 95 { 96 // Queue finish command 97 enqueue(Message(MESSAGE_FINISH)); 98 99 // Wait for thread to terminate 100 join(); 101 102 m_threadRunning = false; 103 } 104 105 void RenderThread::enqueue (const Message& message) 106 { 107 // \note Thread must be running or otherwise nobody is going to drain the queue. 108 DE_ASSERT(m_threadRunning); 109 m_msgQueue.pushFront(message); 110 } 111 112 void RenderThread::pause (void) 113 { 114 enqueue(Message(MESSAGE_PAUSE)); 115 } 116 117 void RenderThread::resume (void) 118 { 119 enqueue(Message(MESSAGE_RESUME)); 120 } 121 122 void RenderThread::sync (void) 123 { 124 de::Semaphore waitSem(0); 125 enqueue(Message(MESSAGE_SYNC, &waitSem)); 126 waitSem.decrement(); 127 } 128 129 void RenderThread::processMessage (const Message& message) 130 { 131 DBG_PRINT(("RenderThread::processMessage(): message = { %s, %p }\n", getMessageTypeName(message.type), message.payload.window)); 132 133 switch (message.type) 134 { 135 case MESSAGE_RESUME: m_paused = false; break; 136 case MESSAGE_PAUSE: m_paused = true; break; 137 case MESSAGE_FINISH: m_finish = true; break; 138 139 // \note While Platform / WindowRegistry are currently multi-window -capable, 140 // the fact that platform gives us windows too late / at unexpected times 141 // forces us to do some sanity checking and limit system to one window here. 142 case MESSAGE_WINDOW_CREATED: 143 if (m_windowState != WINDOWSTATE_NOT_CREATED && m_windowState != WINDOWSTATE_DESTROYED) 144 throw InternalError("Got unexpected onNativeWindowCreated() event from system"); 145 146 m_windowState = WINDOWSTATE_NOT_INITIALIZED; 147 m_window = message.payload.window; 148 break; 149 150 case MESSAGE_WINDOW_RESIZED: 151 if (m_window != message.payload.window) 152 throw InternalError("Got onNativeWindowResized() event targeting different window"); 153 154 if (m_windowState == WINDOWSTATE_NOT_INITIALIZED) 155 { 156 // Got first resize event, window is ready for use. 157 m_windowState = WINDOWSTATE_READY; 158 onWindowCreated(message.payload.window); 159 } 160 else if (m_windowState == WINDOWSTATE_READY) 161 onWindowResized(message.payload.window); 162 else 163 throw InternalError("Got unexpected onNativeWindowResized() event from system"); 164 165 break; 166 167 case MESSAGE_WINDOW_DESTROYED: 168 if (m_window != message.payload.window) 169 throw InternalError("Got onNativeWindowDestroyed() event targeting different window"); 170 171 if (m_windowState != WINDOWSTATE_NOT_INITIALIZED && m_windowState != WINDOWSTATE_READY) 172 throw InternalError("Got unexpected onNativeWindowDestroyed() event from system"); 173 174 if (m_windowState == WINDOWSTATE_READY) 175 onWindowDestroyed(message.payload.window); 176 177 m_windowState = WINDOWSTATE_DESTROYED; 178 m_window = DE_NULL; 179 break; 180 181 case MESSAGE_INPUT_QUEUE_CREATED: 182 m_inputQueue = message.payload.inputQueue; 183 break; 184 185 case MESSAGE_INPUT_QUEUE_DESTROYED: 186 m_inputQueue = message.payload.inputQueue; 187 break; 188 189 case MESSAGE_SYNC: 190 message.payload.semaphore->increment(); 191 break; 192 193 default: 194 throw std::runtime_error("Unknown message type"); 195 break; 196 } 197 } 198 199 void RenderThread::run (void) 200 { 201 // Init state 202 m_windowState = WINDOWSTATE_NOT_CREATED; 203 m_paused = true; 204 m_finish = false; 205 206 try 207 { 208 while (!m_finish) 209 { 210 if (m_paused || m_windowState != WINDOWSTATE_READY) 211 { 212 // Block until we are not paused and window is ready. 213 Message msg = m_msgQueue.popBack(); 214 processMessage(msg); 215 continue; 216 } 217 218 // Process available commands 219 { 220 Message msg; 221 if (m_msgQueue.tryPopBack(msg)) 222 { 223 processMessage(msg); 224 continue; 225 } 226 } 227 228 DE_ASSERT(m_windowState == WINDOWSTATE_READY); 229 230 // Process input events. 231 // \todo [2013-05-08 pyry] What if system fills up the input queue before we have window ready? 232 while (m_inputQueue && 233 AInputQueue_hasEvents(m_inputQueue) > 0) 234 { 235 AInputEvent* event; 236 TCU_CHECK(AInputQueue_getEvent(m_inputQueue, &event) >= 0); 237 onInputEvent(event); 238 AInputQueue_finishEvent(m_inputQueue, event, 1); 239 } 240 241 // Everything set up - safe to render. 242 if (!render()) 243 { 244 DBG_PRINT(("RenderThread::run(): render\n")); 245 break; 246 } 247 } 248 } 249 catch (const std::exception& e) 250 { 251 print("RenderThread: %s\n", e.what()); 252 } 253 254 // Tell activity to finish. 255 DBG_PRINT(("RenderThread::run(): done, waiting for FINISH\n")); 256 m_activity.finish(); 257 258 // Thread must keep draining message queue until FINISH message is encountered. 259 try 260 { 261 while (!m_finish) 262 { 263 Message msg = m_msgQueue.popBack(); 264 265 // Ignore all but SYNC and FINISH messages. 266 if (msg.type == MESSAGE_SYNC || msg.type == MESSAGE_FINISH) 267 processMessage(msg); 268 } 269 } 270 catch (const std::exception& e) 271 { 272 die("RenderThread: %s\n", e.what()); 273 } 274 275 DBG_PRINT(("RenderThread::run(): exiting...\n")); 276 } 277 278 // RenderActivity 279 280 RenderActivity::RenderActivity (ANativeActivity* activity) 281 : NativeActivity(activity) 282 , m_thread (DE_NULL) 283 { 284 DBG_PRINT(("RenderActivity::RenderActivity()")); 285 } 286 287 RenderActivity::~RenderActivity (void) 288 { 289 DBG_PRINT(("RenderActivity::~RenderActivity()")); 290 } 291 292 void RenderActivity::setThread (RenderThread* thread) 293 { 294 m_thread = thread; 295 } 296 297 void RenderActivity::onStart (void) 298 { 299 DBG_PRINT(("RenderActivity::onStart()")); 300 } 301 302 void RenderActivity::onResume (void) 303 { 304 DBG_PRINT(("RenderActivity::onResume()")); 305 306 // Resume (or start) test execution 307 m_thread->resume(); 308 } 309 310 void RenderActivity::onPause (void) 311 { 312 DBG_PRINT(("RenderActivity::onPause()")); 313 314 // Pause test execution 315 m_thread->pause(); 316 } 317 318 void RenderActivity::onStop (void) 319 { 320 DBG_PRINT(("RenderActivity::onStop()")); 321 } 322 323 void RenderActivity::onDestroy (void) 324 { 325 DBG_PRINT(("RenderActivity::onDestroy()")); 326 } 327 328 void RenderActivity::onNativeWindowCreated (ANativeWindow* window) 329 { 330 DBG_PRINT(("RenderActivity::onNativeWindowCreated()")); 331 m_thread->enqueue(Message(MESSAGE_WINDOW_CREATED, window)); 332 } 333 334 void RenderActivity::onNativeWindowResized (ANativeWindow* window) 335 { 336 DBG_PRINT(("RenderActivity::onNativeWindowResized()")); 337 m_thread->enqueue(Message(MESSAGE_WINDOW_RESIZED, window)); 338 } 339 340 void RenderActivity::onNativeWindowRedrawNeeded (ANativeWindow* window) 341 { 342 DE_UNREF(window); 343 } 344 345 void RenderActivity::onNativeWindowDestroyed (ANativeWindow* window) 346 { 347 DBG_PRINT(("RenderActivity::onNativeWindowDestroyed()")); 348 m_thread->enqueue(Message(MESSAGE_WINDOW_DESTROYED, window)); 349 m_thread->sync(); // Block until thread has processed all messages. 350 } 351 352 void RenderActivity::onInputQueueCreated (AInputQueue* queue) 353 { 354 DBG_PRINT(("RenderActivity::onInputQueueCreated()")); 355 m_thread->enqueue(Message(MESSAGE_INPUT_QUEUE_CREATED, queue)); 356 } 357 358 void RenderActivity::onInputQueueDestroyed (AInputQueue* queue) 359 { 360 DBG_PRINT(("RenderActivity::onInputQueueDestroyed()")); 361 m_thread->enqueue(Message(MESSAGE_INPUT_QUEUE_DESTROYED, queue)); 362 m_thread->sync(); 363 } 364 365 } // Android 366 } // tcu 367