• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014-2015 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "RenderWindow.h"
16 
17 #include "base/Thread.h"
18 #include "base/MessageChannel.h"
19 #include "host-common/logging.h"
20 #include "FrameBuffer.h"
21 #include "RendererImpl.h"
22 
23 #include <stdarg.h>
24 #include <stdio.h>
25 #ifndef _WIN32
26 #include <signal.h>
27 #include <pthread.h>
28 #endif
29 
30 #define DEBUG 0
31 
32 #if DEBUG
33 #  define D(...) my_debug(__PRETTY_FUNCTION__, __LINE__, __VA_ARGS__)
34 #else
35 #  define D(...) ((void)0)
36 #endif
37 
38 namespace {
39 
40 #if DEBUG
my_debug(const char * function,int line,const char * format,...)41 void my_debug(const char* function, int line, const char* format, ...) {
42     static ::android::base::Lock mutex;
43     va_list args;
44     va_start(args, format);
45     mutex.lock();
46     fprintf(stderr, "%s:%d:", function, line);
47     vfprintf(stderr, format, args);
48     mutex.unlock();
49     va_end(args);
50 }
51 #endif
52 
53 // List of possible commands to send to the render window thread from
54 // the main one.
55 enum Command {
56     CMD_INITIALIZE,
57     CMD_SET_POST_CALLBACK,
58     CMD_SETUP_SUBWINDOW,
59     CMD_REMOVE_SUBWINDOW,
60     CMD_SET_ROTATION,
61     CMD_SET_TRANSLATION,
62     CMD_REPAINT,
63     CMD_HAS_GUEST_POSTED_A_FRAME,
64     CMD_RESET_GUEST_POSTED_A_FRAME,
65     CMD_FINALIZE,
66 };
67 
68 }  // namespace
69 
70 // A single message sent from the main thread to the render window thread.
71 // |cmd| determines which fields are valid to read.
72 struct RenderWindowMessage {
73     Command cmd;
74     union {
75         // CMD_INITIALIZE
76         struct {
77             int width;
78             int height;
79             bool useSubWindow;
80             bool egl2egl;
81         } init;
82 
83         // CMD_SET_POST_CALLBACK
84         struct {
85             emugl::Renderer::OnPostCallback on_post;
86             void* on_post_context;
87             uint32_t on_post_displayId;
88             bool use_bgra_readback;
89         } set_post_callback;
90 
91         // CMD_SETUP_SUBWINDOW
92         struct {
93             FBNativeWindowType parent;
94             int wx;
95             int wy;
96             int ww;
97             int wh;
98             int fbw;
99             int fbh;
100             float dpr;
101             float rotation;
102             bool deleteExisting;
103             bool hideWindow;
104         } subwindow;
105 
106         // CMD_SET_TRANSLATION;
107         struct {
108             float px;
109             float py;
110         } trans;
111 
112         // CMD_SET_ROTATION
113         float rotation;
114 
115         // result of operations.
116         bool result;
117     };
118 
119     // Process the current message, and updates its |result| field.
120     // Returns true on success, or false on failure.
processRenderWindowMessage121     bool process() const {
122         const RenderWindowMessage& msg = *this;
123         FrameBuffer* fb;
124         bool result = false;
125         switch (msg.cmd) {
126             case CMD_INITIALIZE:
127                 GL_LOG("RenderWindow: CMD_INITIALIZE w=%d h=%d",
128                        msg.init.width, msg.init.height);
129                 result = FrameBuffer::initialize(msg.init.width,
130                                                  msg.init.height,
131                                                  msg.init.useSubWindow,
132                                                  msg.init.egl2egl);
133                 break;
134 
135             case CMD_FINALIZE:
136                 GL_LOG("CMD_FINALIZE");
137                 D("CMD_FINALIZE\n");
138                 // this command may be issued even when frame buffer is not
139                 // yet created (e.g. if CMD_INITIALIZE failed),
140                 // so make sure we check if it is there before finalizing
141                 if (const auto fb = FrameBuffer::getFB()) {
142                     fb->finalize();
143                 }
144                 result = true;
145                 break;
146 
147             case CMD_SET_POST_CALLBACK:
148                 GL_LOG("CMD_SET_POST_CALLBACK");
149                 D("CMD_SET_POST_CALLBACK\n");
150                 fb = FrameBuffer::getFB();
151                 fb->setPostCallback(msg.set_post_callback.on_post,
152                                     msg.set_post_callback.on_post_context,
153                                     msg.set_post_callback.on_post_displayId,
154                                     msg.set_post_callback.use_bgra_readback);
155                 result = true;
156                 break;
157 
158             case CMD_SETUP_SUBWINDOW:
159                 GL_LOG("CMD_SETUP_SUBWINDOW: parent=%p wx=%d wy=%d ww=%d wh=%d fbw=%d fbh=%d dpr=%f rotation=%f",
160                        (void*)(intptr_t)msg.subwindow.parent,
161                        msg.subwindow.wx,
162                        msg.subwindow.wy,
163                        msg.subwindow.ww,
164                        msg.subwindow.wh,
165                        msg.subwindow.fbw,
166                        msg.subwindow.fbh,
167                        msg.subwindow.dpr,
168                        msg.subwindow.rotation);
169                 D("CMD_SETUP_SUBWINDOW: parent=%p wx=%d wy=%d ww=%d wh=%d fbw=%d fbh=%d dpr=%f rotation=%f\n",
170                     (void*)(intptr_t)msg.subwindow.parent,
171                     msg.subwindow.wx,
172                     msg.subwindow.wy,
173                     msg.subwindow.ww,
174                     msg.subwindow.wh,
175                     msg.subwindow.fbw,
176                     msg.subwindow.fbh,
177                     msg.subwindow.dpr,
178                     msg.subwindow.rotation);
179                 result = FrameBuffer::getFB()->setupSubWindow(
180                         msg.subwindow.parent,
181                         msg.subwindow.wx,
182                         msg.subwindow.wy,
183                         msg.subwindow.ww,
184                         msg.subwindow.wh,
185                         msg.subwindow.fbw,
186                         msg.subwindow.fbh,
187                         msg.subwindow.dpr,
188                         msg.subwindow.rotation,
189                         msg.subwindow.deleteExisting,
190                         msg.subwindow.hideWindow);
191                 break;
192 
193             case CMD_REMOVE_SUBWINDOW:
194                 GL_LOG("CMD_REMOVE_SUBWINDOW");
195                 D("CMD_REMOVE_SUBWINDOW\n");
196                 result = FrameBuffer::getFB()->removeSubWindow();
197                 break;
198 
199             case CMD_SET_ROTATION:
200                 GL_LOG("CMD_SET_ROTATION rotation=%f", msg.rotation);
201                 D("CMD_SET_ROTATION rotation=%f\n", msg.rotation);
202                 fb = FrameBuffer::getFB();
203                 if (fb) {
204                     fb->setDisplayRotation(msg.rotation);
205                     result = true;
206                 }
207                 break;
208 
209             case CMD_SET_TRANSLATION:
210                 GL_LOG("CMD_SET_TRANSLATION translation=%f,%f", msg.trans.px, msg.trans.py);
211                 D("CMD_SET_TRANSLATION translation=%f,%f\n", msg.trans.px, msg.trans.py);
212                 fb = FrameBuffer::getFB();
213                 if (fb) {
214                     fb->setDisplayTranslation(msg.trans.px, msg.trans.py);
215                     result = true;
216                 }
217                 break;
218 
219             case CMD_REPAINT:
220                 GL_LOG("CMD_REPAINT");
221                 D("CMD_REPAINT\n");
222                 fb = FrameBuffer::getFB();
223                 if (fb) {
224                     fb->repost();
225                     result = true;
226                 } else {
227                     GL_LOG("CMD_REPAINT: no repost, no FrameBuffer");
228                 }
229                 break;
230 
231             case CMD_HAS_GUEST_POSTED_A_FRAME:
232                 GL_LOG("CMD_HAS_GUEST_POSTED_A_FRAME");
233                 D("CMD_HAS_GUEST_POSTED_A_FRAME\n");
234                 fb = FrameBuffer::getFB();
235                 if (fb) {
236                     result = fb->hasGuestPostedAFrame();
237                 } else {
238                     GL_LOG("CMD_HAS_GUEST_POSTED_A_FRAME: no FrameBuffer");
239                 }
240                 break;
241 
242             case CMD_RESET_GUEST_POSTED_A_FRAME:
243                 GL_LOG("CMD_RESET_GUEST_POSTED_A_FRAME");
244                 D("CMD_RESET_GUEST_POSTED_A_FRAME\n");
245                 fb = FrameBuffer::getFB();
246                 if (fb) {
247                     fb->resetGuestPostedAFrame();
248                     result = true;
249                 } else {
250                     GL_LOG("CMD_RESET_GUEST_POSTED_A_FRAME: no FrameBuffer");
251                 }
252                 break;
253 
254 
255             default:
256                 ;
257         }
258         return result;
259     }
260 };
261 
262 // Simple synchronization structure used to exchange data between the
263 // main and render window threads. Usage is the following:
264 //
265 // The main thread does the following in a loop:
266 //
267 //      canWriteCmd.wait()
268 //      updates |message| by writing a new |cmd| value and appropriate
269 //      parameters.
270 //      canReadCmd.signal()
271 //      canReadResult.wait()
272 //      reads |message.result|
273 //      canWriteResult.signal()
274 //
275 // The render window thread will do the following:
276 //
277 //      canReadCmd.wait()
278 //      reads |message.cmd| and acts upon it.
279 //      canWriteResult.wait()
280 //      writes |message.result|
281 //      canReadResult.signal()
282 //      canWriteCmd.signal()
283 //
284 class RenderWindowChannel {
285 public:
RenderWindowChannel()286     RenderWindowChannel() : mIn(), mOut() {}
~RenderWindowChannel()287     ~RenderWindowChannel() {}
288 
289     // Send a message from the main thread.
290     // Note that the content of |msg| is copied into the channel.
291     // Returns with the command's result (true or false).
sendMessageAndGetResult(const RenderWindowMessage & msg)292     bool sendMessageAndGetResult(const RenderWindowMessage& msg) {
293         D("msg.cmd=%d\n", msg.cmd);
294         mIn.send(msg);
295         D("waiting for result\n");
296         bool result = false;
297         mOut.receive(&result);
298         D("result=%s\n", result ? "success" : "failure");
299         return result;
300     }
301 
302     // Receive a message from the render window thread.
303     // On exit, |*msg| gets a copy of the message. The caller
304     // must always call sendResult() after processing the message.
receiveMessage(RenderWindowMessage * msg)305     void receiveMessage(RenderWindowMessage* msg) {
306         D("entering\n");
307         mIn.receive(msg);
308         D("message cmd=%d\n", msg->cmd);
309     }
310 
311     // Send result from the render window thread to the main one.
312     // Must always be called after receiveMessage().
sendResult(bool result)313     void sendResult(bool result) {
314         D("waiting to send result (%s)\n", result ? "success" : "failure");
315         mOut.send(result);
316         D("result sent\n");
317     }
318 
319 private:
320     android::base::MessageChannel<RenderWindowMessage, 16U> mIn;
321     android::base::MessageChannel<bool, 16U> mOut;
322 };
323 
324 namespace {
325 
326 // This class implements the window render thread.
327 // Its purpose is to listen for commands from the main thread in a loop,
328 // process them, then return a boolean result for each one of them.
329 //
330 // The thread ends with a CMD_FINALIZE.
331 //
332 class RenderWindowThread : public android::base::Thread {
333 public:
RenderWindowThread(RenderWindowChannel * channel)334     RenderWindowThread(RenderWindowChannel* channel) : mChannel(channel) {}
335 
main()336     virtual intptr_t main() {
337         D("Entering render window thread thread\n");
338 #ifndef _WIN32
339         sigset_t set;
340         sigfillset(&set);
341         pthread_sigmask(SIG_SETMASK, &set, NULL);
342 #endif
343         bool running = true;
344         while (running) {
345             RenderWindowMessage msg = {};
346 
347             D("Waiting for message from main thread\n");
348             mChannel->receiveMessage(&msg);
349 
350             bool result = msg.process();
351             if (msg.cmd == CMD_FINALIZE) {
352                 running = false;
353             }
354 
355             D("Sending result (%s) to main thread\n", result ? "success" : "failure");
356             mChannel->sendResult(result);
357         }
358         D("Exiting thread\n");
359         return 0;
360     }
361 
362 private:
363     RenderWindowChannel* mChannel;
364 };
365 
366 }  // namespace
367 
RenderWindow(int width,int height,bool use_thread,bool use_sub_window,bool egl2egl)368 RenderWindow::RenderWindow(int width,
369                            int height,
370                            bool use_thread,
371                            bool use_sub_window,
372                            bool egl2egl)
373     : mRepostThread([this] {
374           while (auto cmd = mRepostCommands.receive()) {
375               if (*cmd == RepostCommand::Sync) {
376                   continue;
377               } else if (*cmd == RepostCommand::Repost &&
378                          !mPaused) {
379                   GL_LOG("Reposting thread dequeueing a CMD_REPAINT");
380                   RenderWindowMessage msg = {CMD_REPAINT};
381                   (void)msg.process();
382               }
383           }
384       }) {
385     if (use_thread) {
386         mChannel = new RenderWindowChannel();
387         mThread = new RenderWindowThread(mChannel);
388         mThread->start();
389     } else {
390         mRepostThread.start();
391     }
392 
393     RenderWindowMessage msg = {};
394     msg.cmd = CMD_INITIALIZE;
395     msg.init.width = width;
396     msg.init.height = height;
397     msg.init.useSubWindow = use_sub_window;
398     msg.init.egl2egl = egl2egl;
399     mValid = processMessage(msg);
400 }
401 
~RenderWindow()402 RenderWindow::~RenderWindow() {
403     D("Entering\n");
404     removeSubWindow();
405     mRepostCommands.stop();
406     D("Sending CMD_FINALIZE\n");
407     RenderWindowMessage msg = {};
408     msg.cmd = CMD_FINALIZE;
409     (void) processMessage(msg);
410 
411     if (useThread()) {
412         mThread->wait(NULL);
413         delete mThread;
414         delete mChannel;
415     } else {
416         mRepostThread.wait();
417     }
418 }
419 
setPaused(bool paused)420 void RenderWindow::setPaused(bool paused) {
421     // If pausing, flush commands
422     if (!mPaused && paused) {
423         if (useThread()) {
424             fprintf(stderr,
425                     "WARNING: flushMessages unsupported for RenderWindowThread. "
426                     "Generic snapshot load might segfault.\n");
427         } else {
428             mRepostCommands.waitForEmpty();
429         }
430     }
431 
432     mPaused = paused;
433 }
434 
getHardwareStrings(const char ** vendor,const char ** renderer,const char ** version)435 bool RenderWindow::getHardwareStrings(const char** vendor,
436                                       const char** renderer,
437                                       const char** version) {
438     D("Entering\n");
439     // TODO(digit): Move this to render window thread.
440     FrameBuffer* fb = FrameBuffer::getFB();
441     if (!fb) {
442         D("No framebuffer!\n");
443         return false;
444     }
445     fb->getGLStrings(vendor, renderer, version);
446     D("Exiting vendor=[%s] renderer=[%s] version=[%s]\n",
447       *vendor, *renderer, *version);
448 
449     return true;
450 }
451 
setPostCallback(emugl::Renderer::OnPostCallback onPost,void * onPostContext,uint32_t displayId,bool useBgraReadback)452 void RenderWindow::setPostCallback(emugl::Renderer::OnPostCallback onPost,
453                                    void* onPostContext,
454                                    uint32_t displayId,
455                                    bool useBgraReadback) {
456     D("Entering\n");
457     RenderWindowMessage msg = {};
458     msg.cmd = CMD_SET_POST_CALLBACK;
459     msg.set_post_callback.on_post = onPost;
460     msg.set_post_callback.on_post_context = onPostContext;
461     msg.set_post_callback.on_post_displayId = displayId;
462     msg.set_post_callback.use_bgra_readback = useBgraReadback;
463     (void) processMessage(msg);
464     D("Exiting\n");
465 }
466 
asyncReadbackSupported()467 bool RenderWindow::asyncReadbackSupported() {
468     D("Entering\n");
469     return FrameBuffer::getFB()->asyncReadbackSupported();
470 }
471 
getReadPixelsCallback()472 emugl::Renderer::ReadPixelsCallback RenderWindow::getReadPixelsCallback() {
473     D("Entering\n");
474     return FrameBuffer::getFB()->getReadPixelsCallback();
475 }
476 
477 emugl::Renderer::FlushReadPixelPipeline
getFlushReadPixelPipeline()478 RenderWindow::getFlushReadPixelPipeline() {
479     return FrameBuffer::getFB()->getFlushReadPixelPipeline();
480 }
setupSubWindow(FBNativeWindowType window,int wx,int wy,int ww,int wh,int fbw,int fbh,float dpr,float zRot,bool deleteExisting,bool hideWindow)481 bool RenderWindow::setupSubWindow(FBNativeWindowType window,
482                                   int wx,
483                                   int wy,
484                                   int ww,
485                                   int wh,
486                                   int fbw,
487                                   int fbh,
488                                   float dpr,
489                                   float zRot,
490                                   bool deleteExisting,
491                                   bool hideWindow) {
492     D("Entering mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false");
493 
494     RenderWindowMessage msg = {};
495     msg.cmd = CMD_SETUP_SUBWINDOW;
496     msg.subwindow.parent = window;
497     msg.subwindow.wx = wx;
498     msg.subwindow.wy = wy;
499     msg.subwindow.ww = ww;
500     msg.subwindow.wh = wh;
501     msg.subwindow.fbw = fbw;
502     msg.subwindow.fbh = fbh;
503     msg.subwindow.dpr = dpr;
504     msg.subwindow.rotation = zRot;
505     msg.subwindow.deleteExisting = deleteExisting;
506     msg.subwindow.hideWindow = hideWindow;
507     mHasSubWindow = processMessage(msg);
508 
509     D("Exiting mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false");
510     return mHasSubWindow;
511 }
512 
removeSubWindow()513 bool RenderWindow::removeSubWindow() {
514     D("Entering mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false");
515     if (!mHasSubWindow) {
516         return false;
517     }
518     mHasSubWindow = false;
519     if (!useThread()) {
520         mRepostCommands.send(RepostCommand::Sync);
521         mRepostCommands.waitForEmpty();
522     }
523 
524     RenderWindowMessage msg = {};
525     msg.cmd = CMD_REMOVE_SUBWINDOW;
526     bool result = processMessage(msg);
527     D("Exiting result=%s\n", result ? "success" : "failure");
528     return result;
529 }
530 
setRotation(float zRot)531 void RenderWindow::setRotation(float zRot) {
532     D("Entering rotation=%f\n", zRot);
533     RenderWindowMessage msg = {};
534     msg.cmd = CMD_SET_ROTATION;
535     msg.rotation = zRot;
536     (void) processMessage(msg);
537     D("Exiting\n");
538 }
539 
setTranslation(float px,float py)540 void RenderWindow::setTranslation(float px, float py) {
541     D("Entering translation=%f,%f\n", px, py);
542     RenderWindowMessage msg = {};
543     msg.cmd = CMD_SET_TRANSLATION;
544     msg.trans.px = px;
545     msg.trans.py = py;
546     (void) processMessage(msg);
547     D("Exiting\n");
548 }
549 
setScreenMask(int width,int height,const unsigned char * rgbaData)550 void RenderWindow::setScreenMask(int width, int height, const unsigned char* rgbaData) {
551     FrameBuffer* fb = FrameBuffer::getFB();
552     if (fb) {
553         fb->getTextureDraw()->setScreenMask(width, height, rgbaData);
554     }
555 }
556 
repaint()557 void RenderWindow::repaint() {
558     D("Entering\n");
559     RenderWindowMessage msg = {};
560     msg.cmd = CMD_REPAINT;
561     (void) processMessage(msg);
562     D("Exiting\n");
563 }
564 
hasGuestPostedAFrame()565 bool RenderWindow::hasGuestPostedAFrame() {
566     D("Entering\n");
567     RenderWindowMessage msg = {};
568     msg.cmd = CMD_HAS_GUEST_POSTED_A_FRAME;
569     bool res = processMessage(msg);
570     D("Exiting\n");
571     return res;
572 }
573 
resetGuestPostedAFrame()574 void RenderWindow::resetGuestPostedAFrame() {
575     D("Entering\n");
576     RenderWindowMessage msg = {};
577     msg.cmd = CMD_RESET_GUEST_POSTED_A_FRAME;
578     (void) processMessage(msg);
579     D("Exiting\n");
580 }
581 
processMessage(const RenderWindowMessage & msg)582 bool RenderWindow::processMessage(const RenderWindowMessage& msg) {
583     if (useThread()) {
584         if (msg.cmd == CMD_REPAINT) {
585             GL_LOG("Sending CMD_REPAINT to render window channel");
586         }
587         return mChannel->sendMessageAndGetResult(msg);
588     } else if (msg.cmd == CMD_REPAINT) {
589         GL_LOG("Sending CMD_REPAINT to reposting thread");
590         mRepostCommands.send(RepostCommand::Repost);
591         return true;
592     } else {
593         return msg.process();
594     }
595 }
596