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[] = {"RESUME",
54 "PAUSE",
55 "FINISH",
56 "WINDOW_CREATED",
57 "WINDOW_RESIZED",
58 "WINDOW_DESTROYED",
59 "INPUT_QUEUE_CREATED",
60 "INPUT_QUEUE_DESTROYED",
61 "SYNC"};
62 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_names) == MESSAGETYPE_LAST);
63 return s_names[type];
64 }
65 #endif
66
67 // RenderThread
68
RenderThread(NativeActivity & activity)69 RenderThread::RenderThread(NativeActivity &activity)
70 : m_activity(activity)
71 , m_msgQueue(MESSAGE_QUEUE_SIZE)
72 , m_threadRunning(false)
73 , m_inputQueue(DE_NULL)
74 , m_windowState(WINDOWSTATE_NOT_CREATED)
75 , m_window(DE_NULL)
76 , m_paused(false)
77 , m_finish(false)
78 , m_receivedFirstResize(false)
79 {
80 }
81
~RenderThread(void)82 RenderThread::~RenderThread(void)
83 {
84 }
85
start(void)86 void RenderThread::start(void)
87 {
88 m_threadRunning = true;
89 Thread::start();
90 }
91
stop(void)92 void RenderThread::stop(void)
93 {
94 // Queue finish command
95 enqueue(Message(MESSAGE_FINISH));
96
97 // Wait for thread to terminate
98 join();
99
100 m_threadRunning = false;
101 }
102
enqueue(const Message & message)103 void RenderThread::enqueue(const Message &message)
104 {
105 // \note Thread must be running or otherwise nobody is going to drain the queue.
106 DE_ASSERT(m_threadRunning);
107 m_msgQueue.pushFront(message);
108 }
109
pause(void)110 void RenderThread::pause(void)
111 {
112 enqueue(Message(MESSAGE_PAUSE));
113 }
114
resume(void)115 void RenderThread::resume(void)
116 {
117 enqueue(Message(MESSAGE_RESUME));
118 }
119
sync(void)120 void RenderThread::sync(void)
121 {
122 de::Semaphore waitSem(0);
123 enqueue(Message(MESSAGE_SYNC, &waitSem));
124 waitSem.decrement();
125 }
126
processMessage(const Message & message)127 void RenderThread::processMessage(const Message &message)
128 {
129 DBG_PRINT(("RenderThread::processMessage(): message = { %s, %p }\n", getMessageTypeName(message.type),
130 message.payload.window));
131
132 switch (message.type)
133 {
134 case MESSAGE_RESUME:
135 m_paused = false;
136 break;
137 case MESSAGE_PAUSE:
138 m_paused = true;
139 break;
140 case MESSAGE_FINISH:
141 m_finish = true;
142 break;
143
144 // \note While Platform / WindowRegistry are currently multi-window -capable,
145 // the fact that platform gives us windows too late / at unexpected times
146 // forces us to do some quick checking and limit system to one window here.
147 case MESSAGE_WINDOW_CREATED:
148 if (m_windowState != WINDOWSTATE_NOT_CREATED && m_windowState != WINDOWSTATE_DESTROYED)
149 throw InternalError("Got unexpected onNativeWindowCreated() event from system");
150
151 // The documented behavior for the callbacks is that the native activity
152 // will get a call to onNativeWindowCreated(), at which point it should have
153 // a surface to render to, and can then start immediately.
154 //
155 // The actual creation process has the framework making calls to both
156 // onNativeWindowCreated() and then onNativeWindowResized(). The test
157 // waits for that first resize before it considers the window ready for
158 // rendering.
159 //
160 // However subsequent events in the framework may cause the window to be
161 // recreated at a new position without a size change, which sends on
162 // onNativeWindowDestroyed(), and then on onNativeWindowCreated() without
163 // a follow-up onNativeWindowResized(). If this happens, the test will
164 // stop rendering as it is no longer in the ready state, and a watchdog
165 // thread will eventually kill the test, causing it to fail. We therefore
166 // set the window state back to READY and process the window creation here
167 // if we have already observed that first resize call.
168 if (!m_receivedFirstResize)
169 {
170 m_windowState = WINDOWSTATE_NOT_INITIALIZED;
171 }
172 else
173 {
174 m_windowState = WINDOWSTATE_READY;
175 onWindowCreated(message.payload.window);
176 }
177 m_window = message.payload.window;
178 break;
179
180 case MESSAGE_WINDOW_RESIZED:
181 if (m_window != message.payload.window)
182 throw InternalError("Got onNativeWindowResized() event targeting different window");
183
184 // Record that we've the first resize event, in case the window is
185 // recreated later without a resize.
186 m_receivedFirstResize = true;
187
188 if (m_windowState == WINDOWSTATE_NOT_INITIALIZED)
189 {
190 // Got first resize event, window is ready for use.
191 m_windowState = WINDOWSTATE_READY;
192 onWindowCreated(message.payload.window);
193 }
194 else if (m_windowState == WINDOWSTATE_READY)
195 onWindowResized(message.payload.window);
196 else
197 throw InternalError("Got unexpected onNativeWindowResized() event from system");
198
199 break;
200
201 case MESSAGE_WINDOW_DESTROYED:
202 if (m_window != message.payload.window)
203 throw InternalError("Got onNativeWindowDestroyed() event targeting different window");
204
205 if (m_windowState != WINDOWSTATE_NOT_INITIALIZED && m_windowState != WINDOWSTATE_READY)
206 throw InternalError("Got unexpected onNativeWindowDestroyed() event from system");
207
208 if (m_windowState == WINDOWSTATE_READY)
209 onWindowDestroyed(message.payload.window);
210
211 m_windowState = WINDOWSTATE_DESTROYED;
212 m_window = DE_NULL;
213 break;
214
215 case MESSAGE_INPUT_QUEUE_CREATED:
216 m_inputQueue = message.payload.inputQueue;
217 break;
218
219 case MESSAGE_INPUT_QUEUE_DESTROYED:
220 m_inputQueue = message.payload.inputQueue;
221 break;
222
223 case MESSAGE_SYNC:
224 message.payload.semaphore->increment();
225 break;
226
227 default:
228 throw std::runtime_error("Unknown message type");
229 break;
230 }
231 }
232
run(void)233 void RenderThread::run(void)
234 {
235 // Init state
236 m_windowState = WINDOWSTATE_NOT_CREATED;
237 m_paused = true;
238 m_finish = false;
239
240 try
241 {
242 while (!m_finish)
243 {
244 if (m_paused || m_windowState != WINDOWSTATE_READY)
245 {
246 // Block until we are not paused and window is ready.
247 Message msg = m_msgQueue.popBack();
248 processMessage(msg);
249 continue;
250 }
251
252 // Process available commands
253 {
254 Message msg;
255 if (m_msgQueue.tryPopBack(msg))
256 {
257 processMessage(msg);
258 continue;
259 }
260 }
261
262 DE_ASSERT(m_windowState == WINDOWSTATE_READY);
263
264 // Process input events.
265 // \todo [2013-05-08 pyry] What if system fills up the input queue before we have window ready?
266 while (m_inputQueue && AInputQueue_hasEvents(m_inputQueue) > 0)
267 {
268 AInputEvent *event;
269 TCU_CHECK(AInputQueue_getEvent(m_inputQueue, &event) >= 0);
270 onInputEvent(event);
271 AInputQueue_finishEvent(m_inputQueue, event, 1);
272 }
273
274 // Everything set up - safe to render.
275 if (!render())
276 {
277 DBG_PRINT(("RenderThread::run(): render\n"));
278 break;
279 }
280 }
281 }
282 catch (const std::exception &e)
283 {
284 print("RenderThread: %s\n", e.what());
285 }
286
287 // Tell activity to finish.
288 DBG_PRINT(("RenderThread::run(): done, waiting for FINISH\n"));
289 m_activity.finish();
290
291 // Thread must keep draining message queue until FINISH message is encountered.
292 try
293 {
294 while (!m_finish)
295 {
296 Message msg = m_msgQueue.popBack();
297
298 // Ignore all but SYNC and FINISH messages.
299 if (msg.type == MESSAGE_SYNC || msg.type == MESSAGE_FINISH)
300 processMessage(msg);
301 }
302 }
303 catch (const std::exception &e)
304 {
305 die("RenderThread: %s\n", e.what());
306 }
307
308 DBG_PRINT(("RenderThread::run(): exiting...\n"));
309 }
310
311 // RenderActivity
312
RenderActivity(ANativeActivity * activity)313 RenderActivity::RenderActivity(ANativeActivity *activity) : NativeActivity(activity), m_thread(DE_NULL)
314 {
315 DBG_PRINT(("RenderActivity::RenderActivity()"));
316 }
317
~RenderActivity(void)318 RenderActivity::~RenderActivity(void)
319 {
320 DBG_PRINT(("RenderActivity::~RenderActivity()"));
321 }
322
setThread(RenderThread * thread)323 void RenderActivity::setThread(RenderThread *thread)
324 {
325 m_thread = thread;
326 }
327
onStart(void)328 void RenderActivity::onStart(void)
329 {
330 DBG_PRINT(("RenderActivity::onStart()"));
331 }
332
onResume(void)333 void RenderActivity::onResume(void)
334 {
335 DBG_PRINT(("RenderActivity::onResume()"));
336
337 // Resume (or start) test execution
338 m_thread->resume();
339 }
340
onPause(void)341 void RenderActivity::onPause(void)
342 {
343 DBG_PRINT(("RenderActivity::onPause()"));
344
345 // Pause test execution
346 m_thread->pause();
347 }
348
onStop(void)349 void RenderActivity::onStop(void)
350 {
351 DBG_PRINT(("RenderActivity::onStop()"));
352 }
353
onDestroy(void)354 void RenderActivity::onDestroy(void)
355 {
356 DBG_PRINT(("RenderActivity::onDestroy()"));
357 }
358
onNativeWindowCreated(ANativeWindow * window)359 void RenderActivity::onNativeWindowCreated(ANativeWindow *window)
360 {
361 DBG_PRINT(("RenderActivity::onNativeWindowCreated()"));
362 m_thread->enqueue(Message(MESSAGE_WINDOW_CREATED, window));
363 }
364
onNativeWindowResized(ANativeWindow * window)365 void RenderActivity::onNativeWindowResized(ANativeWindow *window)
366 {
367 DBG_PRINT(("RenderActivity::onNativeWindowResized()"));
368 m_thread->enqueue(Message(MESSAGE_WINDOW_RESIZED, window));
369 }
370
onNativeWindowRedrawNeeded(ANativeWindow * window)371 void RenderActivity::onNativeWindowRedrawNeeded(ANativeWindow *window)
372 {
373 DE_UNREF(window);
374 }
375
onNativeWindowDestroyed(ANativeWindow * window)376 void RenderActivity::onNativeWindowDestroyed(ANativeWindow *window)
377 {
378 DBG_PRINT(("RenderActivity::onNativeWindowDestroyed()"));
379 m_thread->enqueue(Message(MESSAGE_WINDOW_DESTROYED, window));
380 m_thread->sync(); // Block until thread has processed all messages.
381 }
382
onInputQueueCreated(AInputQueue * queue)383 void RenderActivity::onInputQueueCreated(AInputQueue *queue)
384 {
385 DBG_PRINT(("RenderActivity::onInputQueueCreated()"));
386 m_thread->enqueue(Message(MESSAGE_INPUT_QUEUE_CREATED, queue));
387 }
388
onInputQueueDestroyed(AInputQueue * queue)389 void RenderActivity::onInputQueueDestroyed(AInputQueue *queue)
390 {
391 DBG_PRINT(("RenderActivity::onInputQueueDestroyed()"));
392 m_thread->enqueue(Message(MESSAGE_INPUT_QUEUE_DESTROYED, queue));
393 m_thread->sync();
394 }
395
396 } // namespace Android
397 } // namespace tcu
398