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