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