tcuAndroidRenderActivity.cpp revision 217426c2d19d5247e094e363615d7a0b84daf3a7
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	DE_ASSERT(!m_thread && thread);
295	m_thread = thread;
296}
297
298void RenderActivity::onStart (void)
299{
300	DBG_PRINT(("RenderActivity::onStart()"));
301}
302
303void RenderActivity::onResume (void)
304{
305	DBG_PRINT(("RenderActivity::onResume()"));
306
307	// Resume (or start) test execution
308	m_thread->resume();
309}
310
311void RenderActivity::onPause (void)
312{
313	DBG_PRINT(("RenderActivity::onPause()"));
314
315	// Pause test execution
316	m_thread->pause();
317}
318
319void RenderActivity::onStop (void)
320{
321	DBG_PRINT(("RenderActivity::onStop()"));
322}
323
324void RenderActivity::onDestroy (void)
325{
326	DBG_PRINT(("RenderActivity::onDestroy()"));
327}
328
329void RenderActivity::onNativeWindowCreated (ANativeWindow* window)
330{
331	DBG_PRINT(("RenderActivity::onNativeWindowCreated()"));
332	m_thread->enqueue(Message(MESSAGE_WINDOW_CREATED, window));
333}
334
335void RenderActivity::onNativeWindowResized (ANativeWindow* window)
336{
337	DBG_PRINT(("RenderActivity::onNativeWindowResized()"));
338	m_thread->enqueue(Message(MESSAGE_WINDOW_RESIZED, window));
339}
340
341void RenderActivity::onNativeWindowRedrawNeeded (ANativeWindow* window)
342{
343	DE_UNREF(window);
344}
345
346void RenderActivity::onNativeWindowDestroyed (ANativeWindow* window)
347{
348	DBG_PRINT(("RenderActivity::onNativeWindowDestroyed()"));
349	m_thread->enqueue(Message(MESSAGE_WINDOW_DESTROYED, window));
350	m_thread->sync(); // Block until thread has processed all messages.
351}
352
353void RenderActivity::onInputQueueCreated (AInputQueue* queue)
354{
355	DBG_PRINT(("RenderActivity::onInputQueueCreated()"));
356	m_thread->enqueue(Message(MESSAGE_INPUT_QUEUE_CREATED, queue));
357}
358
359void RenderActivity::onInputQueueDestroyed (AInputQueue* queue)
360{
361	DBG_PRINT(("RenderActivity::onInputQueueDestroyed()"));
362	m_thread->enqueue(Message(MESSAGE_INPUT_QUEUE_DESTROYED, queue));
363	m_thread->sync();
364}
365
366} // Android
367} // tcu
368