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