Home | History | Annotate | Download | only in android
      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