• 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 {
82 }
83 
~RenderThread(void)84 RenderThread::~RenderThread (void)
85 {
86 }
87 
start(void)88 void RenderThread::start (void)
89 {
90 	m_threadRunning = true;
91 	Thread::start();
92 }
93 
stop(void)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 
enqueue(const Message & message)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 
pause(void)112 void RenderThread::pause (void)
113 {
114 	enqueue(Message(MESSAGE_PAUSE));
115 }
116 
resume(void)117 void RenderThread::resume (void)
118 {
119 	enqueue(Message(MESSAGE_RESUME));
120 }
121 
sync(void)122 void RenderThread::sync (void)
123 {
124 	de::Semaphore waitSem(0);
125 	enqueue(Message(MESSAGE_SYNC, &waitSem));
126 	waitSem.decrement();
127 }
128 
processMessage(const Message & message)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 
run(void)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 
RenderActivity(ANativeActivity * activity)280 RenderActivity::RenderActivity (ANativeActivity* activity)
281 	: NativeActivity(activity)
282 	, m_thread		(DE_NULL)
283 {
284 	DBG_PRINT(("RenderActivity::RenderActivity()"));
285 }
286 
~RenderActivity(void)287 RenderActivity::~RenderActivity (void)
288 {
289 	DBG_PRINT(("RenderActivity::~RenderActivity()"));
290 }
291 
setThread(RenderThread * thread)292 void RenderActivity::setThread (RenderThread* thread)
293 {
294 	m_thread = thread;
295 }
296 
onStart(void)297 void RenderActivity::onStart (void)
298 {
299 	DBG_PRINT(("RenderActivity::onStart()"));
300 }
301 
onResume(void)302 void RenderActivity::onResume (void)
303 {
304 	DBG_PRINT(("RenderActivity::onResume()"));
305 
306 	// Resume (or start) test execution
307 	m_thread->resume();
308 }
309 
onPause(void)310 void RenderActivity::onPause (void)
311 {
312 	DBG_PRINT(("RenderActivity::onPause()"));
313 
314 	// Pause test execution
315 	m_thread->pause();
316 }
317 
onStop(void)318 void RenderActivity::onStop (void)
319 {
320 	DBG_PRINT(("RenderActivity::onStop()"));
321 }
322 
onDestroy(void)323 void RenderActivity::onDestroy (void)
324 {
325 	DBG_PRINT(("RenderActivity::onDestroy()"));
326 }
327 
onNativeWindowCreated(ANativeWindow * window)328 void RenderActivity::onNativeWindowCreated (ANativeWindow* window)
329 {
330 	DBG_PRINT(("RenderActivity::onNativeWindowCreated()"));
331 	m_thread->enqueue(Message(MESSAGE_WINDOW_CREATED, window));
332 }
333 
onNativeWindowResized(ANativeWindow * window)334 void RenderActivity::onNativeWindowResized (ANativeWindow* window)
335 {
336 	DBG_PRINT(("RenderActivity::onNativeWindowResized()"));
337 	m_thread->enqueue(Message(MESSAGE_WINDOW_RESIZED, window));
338 }
339 
onNativeWindowRedrawNeeded(ANativeWindow * window)340 void RenderActivity::onNativeWindowRedrawNeeded (ANativeWindow* window)
341 {
342 	DE_UNREF(window);
343 }
344 
onNativeWindowDestroyed(ANativeWindow * window)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 
onInputQueueCreated(AInputQueue * queue)352 void RenderActivity::onInputQueueCreated (AInputQueue* queue)
353 {
354 	DBG_PRINT(("RenderActivity::onInputQueueCreated()"));
355 	m_thread->enqueue(Message(MESSAGE_INPUT_QUEUE_CREATED, queue));
356 }
357 
onInputQueueDestroyed(AInputQueue * queue)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