• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
getMessageTypeName(MessageType type)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 
RenderThread(NativeActivity & activity)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 
~RenderThread(void)85 RenderThread::~RenderThread (void)
86 {
87 }
88 
start(void)89 void RenderThread::start (void)
90 {
91 	m_threadRunning = true;
92 	Thread::start();
93 }
94 
stop(void)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 
enqueue(const Message & message)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 
pause(void)113 void RenderThread::pause (void)
114 {
115 	enqueue(Message(MESSAGE_PAUSE));
116 }
117 
resume(void)118 void RenderThread::resume (void)
119 {
120 	enqueue(Message(MESSAGE_RESUME));
121 }
122 
sync(void)123 void RenderThread::sync (void)
124 {
125 	de::Semaphore waitSem(0);
126 	enqueue(Message(MESSAGE_SYNC, &waitSem));
127 	waitSem.decrement();
128 }
129 
processMessage(const Message & message)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 
run(void)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 
RenderActivity(ANativeActivity * activity)307 RenderActivity::RenderActivity (ANativeActivity* activity)
308 	: NativeActivity(activity)
309 	, m_thread		(DE_NULL)
310 {
311 	DBG_PRINT(("RenderActivity::RenderActivity()"));
312 }
313 
~RenderActivity(void)314 RenderActivity::~RenderActivity (void)
315 {
316 	DBG_PRINT(("RenderActivity::~RenderActivity()"));
317 }
318 
setThread(RenderThread * thread)319 void RenderActivity::setThread (RenderThread* thread)
320 {
321 	m_thread = thread;
322 }
323 
onStart(void)324 void RenderActivity::onStart (void)
325 {
326 	DBG_PRINT(("RenderActivity::onStart()"));
327 }
328 
onResume(void)329 void RenderActivity::onResume (void)
330 {
331 	DBG_PRINT(("RenderActivity::onResume()"));
332 
333 	// Resume (or start) test execution
334 	m_thread->resume();
335 }
336 
onPause(void)337 void RenderActivity::onPause (void)
338 {
339 	DBG_PRINT(("RenderActivity::onPause()"));
340 
341 	// Pause test execution
342 	m_thread->pause();
343 }
344 
onStop(void)345 void RenderActivity::onStop (void)
346 {
347 	DBG_PRINT(("RenderActivity::onStop()"));
348 }
349 
onDestroy(void)350 void RenderActivity::onDestroy (void)
351 {
352 	DBG_PRINT(("RenderActivity::onDestroy()"));
353 }
354 
onNativeWindowCreated(ANativeWindow * window)355 void RenderActivity::onNativeWindowCreated (ANativeWindow* window)
356 {
357 	DBG_PRINT(("RenderActivity::onNativeWindowCreated()"));
358 	m_thread->enqueue(Message(MESSAGE_WINDOW_CREATED, window));
359 }
360 
onNativeWindowResized(ANativeWindow * window)361 void RenderActivity::onNativeWindowResized (ANativeWindow* window)
362 {
363 	DBG_PRINT(("RenderActivity::onNativeWindowResized()"));
364 	m_thread->enqueue(Message(MESSAGE_WINDOW_RESIZED, window));
365 }
366 
onNativeWindowRedrawNeeded(ANativeWindow * window)367 void RenderActivity::onNativeWindowRedrawNeeded (ANativeWindow* window)
368 {
369 	DE_UNREF(window);
370 }
371 
onNativeWindowDestroyed(ANativeWindow * window)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 
onInputQueueCreated(AInputQueue * queue)379 void RenderActivity::onInputQueueCreated (AInputQueue* queue)
380 {
381 	DBG_PRINT(("RenderActivity::onInputQueueCreated()"));
382 	m_thread->enqueue(Message(MESSAGE_INPUT_QUEUE_CREATED, queue));
383 }
384 
onInputQueueDestroyed(AInputQueue * queue)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