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 {
     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