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