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{ 82} 83 84RenderThread::~RenderThread (void) 85{ 86} 87 88void RenderThread::start (void) 89{ 90 m_threadRunning = true; 91 Thread::start(); 92} 93 94void 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 105void 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 112void RenderThread::pause (void) 113{ 114 enqueue(Message(MESSAGE_PAUSE)); 115} 116 117void RenderThread::resume (void) 118{ 119 enqueue(Message(MESSAGE_RESUME)); 120} 121 122void RenderThread::sync (void) 123{ 124 de::Semaphore waitSem(0); 125 enqueue(Message(MESSAGE_SYNC, &waitSem)); 126 waitSem.decrement(); 127} 128 129void 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 199void 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 280RenderActivity::RenderActivity (ANativeActivity* activity) 281 : NativeActivity(activity) 282 , m_thread (DE_NULL) 283{ 284 DBG_PRINT(("RenderActivity::RenderActivity()")); 285} 286 287RenderActivity::~RenderActivity (void) 288{ 289 DBG_PRINT(("RenderActivity::~RenderActivity()")); 290} 291 292void RenderActivity::setThread (RenderThread* thread) 293{ 294 m_thread = thread; 295} 296 297void RenderActivity::onStart (void) 298{ 299 DBG_PRINT(("RenderActivity::onStart()")); 300} 301 302void RenderActivity::onResume (void) 303{ 304 DBG_PRINT(("RenderActivity::onResume()")); 305 306 // Resume (or start) test execution 307 m_thread->resume(); 308} 309 310void RenderActivity::onPause (void) 311{ 312 DBG_PRINT(("RenderActivity::onPause()")); 313 314 // Pause test execution 315 m_thread->pause(); 316} 317 318void RenderActivity::onStop (void) 319{ 320 DBG_PRINT(("RenderActivity::onStop()")); 321} 322 323void RenderActivity::onDestroy (void) 324{ 325 DBG_PRINT(("RenderActivity::onDestroy()")); 326} 327 328void RenderActivity::onNativeWindowCreated (ANativeWindow* window) 329{ 330 DBG_PRINT(("RenderActivity::onNativeWindowCreated()")); 331 m_thread->enqueue(Message(MESSAGE_WINDOW_CREATED, window)); 332} 333 334void RenderActivity::onNativeWindowResized (ANativeWindow* window) 335{ 336 DBG_PRINT(("RenderActivity::onNativeWindowResized()")); 337 m_thread->enqueue(Message(MESSAGE_WINDOW_RESIZED, window)); 338} 339 340void RenderActivity::onNativeWindowRedrawNeeded (ANativeWindow* window) 341{ 342 DE_UNREF(window); 343} 344 345void 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 352void RenderActivity::onInputQueueCreated (AInputQueue* queue) 353{ 354 DBG_PRINT(("RenderActivity::onInputQueueCreated()")); 355 m_thread->enqueue(Message(MESSAGE_INPUT_QUEUE_CREATED, queue)); 356} 357 358void 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