xref: /aosp_15_r20/external/deqp/framework/platform/android/tcuAndroidRenderActivity.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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