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