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
450 RenderWindowMessage msg = {};
451 msg.cmd = CMD_INITIALIZE;
452 msg.init.width = width;
453 msg.init.height = height;
454 msg.init.useSubWindow = use_sub_window;
455 msg.init.egl2egl = egl2egl;
456 mValid = processMessage(msg);
457 }
458
~RenderWindow()459 RenderWindow::~RenderWindow() {
460 D("Entering\n");
461 removeSubWindow();
462 mRepostCommands.stop();
463 D("Sending CMD_FINALIZE\n");
464 RenderWindowMessage msg = {};
465 msg.cmd = CMD_FINALIZE;
466 (void) processMessage(msg);
467
468 if (useThread()) {
469 mThread->wait(NULL);
470 delete mThread;
471 delete mChannel;
472 } else {
473 mRepostThread.wait();
474 }
475 }
476
setPaused(bool paused)477 void RenderWindow::setPaused(bool paused) {
478 // If pausing, flush commands
479 if (!mPaused && paused) {
480 if (useThread()) {
481 fprintf(stderr,
482 "WARNING: flushMessages unsupported for RenderWindowThread. "
483 "Generic snapshot load might segfault.\n");
484 } else {
485 mRepostCommands.waitForEmpty();
486 }
487 }
488
489 mPaused = paused;
490 }
491
getHardwareStrings(const char ** vendor,const char ** renderer,const char ** version)492 bool RenderWindow::getHardwareStrings(const char** vendor,
493 const char** renderer,
494 const char** version) {
495 D("Entering\n");
496 // TODO(digit): Move this to render window thread.
497 FrameBuffer* fb = FrameBuffer::getFB();
498 if (!fb) {
499 D("No framebuffer!\n");
500 return false;
501 }
502 fb->getGLStrings(vendor, renderer, version);
503 D("Exiting vendor=[%s] renderer=[%s] version=[%s]\n",
504 *vendor, *renderer, *version);
505
506 return true;
507 }
508
setPostCallback(Renderer::OnPostCallback onPost,void * onPostContext,uint32_t displayId,bool useBgraReadback)509 void RenderWindow::setPostCallback(Renderer::OnPostCallback onPost, void* onPostContext,
510 uint32_t displayId, bool useBgraReadback) {
511 D("Entering\n");
512 RenderWindowMessage msg = {};
513 msg.cmd = CMD_SET_POST_CALLBACK;
514 msg.set_post_callback.on_post = onPost;
515 msg.set_post_callback.on_post_context = onPostContext;
516 msg.set_post_callback.on_post_displayId = displayId;
517 msg.set_post_callback.use_bgra_readback = useBgraReadback;
518 (void) processMessage(msg);
519 D("Exiting\n");
520 }
521
asyncReadbackSupported()522 bool RenderWindow::asyncReadbackSupported() {
523 D("Entering\n");
524 return FrameBuffer::getFB()->asyncReadbackSupported();
525 }
526
getReadPixelsCallback()527 Renderer::ReadPixelsCallback RenderWindow::getReadPixelsCallback() {
528 D("Entering\n");
529 return FrameBuffer::getFB()->getReadPixelsCallback();
530 }
531
addListener(Renderer::FrameBufferChangeEventListener * listener)532 void RenderWindow::addListener(Renderer::FrameBufferChangeEventListener* listener) {
533 FrameBuffer::getFB()->addListener(listener);
534 }
535
removeListener(Renderer::FrameBufferChangeEventListener * listener)536 void RenderWindow::removeListener(Renderer::FrameBufferChangeEventListener* listener) {
537 FrameBuffer::getFB()->removeListener(listener);
538 }
539
getFlushReadPixelPipeline()540 Renderer::FlushReadPixelPipeline RenderWindow::getFlushReadPixelPipeline() {
541 return FrameBuffer::getFB()->getFlushReadPixelPipeline();
542 }
setupSubWindow(FBNativeWindowType window,int wx,int wy,int ww,int wh,int fbw,int fbh,float dpr,float zRot,bool deleteExisting,bool hideWindow)543 bool RenderWindow::setupSubWindow(FBNativeWindowType window,
544 int wx,
545 int wy,
546 int ww,
547 int wh,
548 int fbw,
549 int fbh,
550 float dpr,
551 float zRot,
552 bool deleteExisting,
553 bool hideWindow) {
554 D("Entering mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false");
555
556 RenderWindowMessage msg = {};
557 msg.cmd = CMD_SETUP_SUBWINDOW;
558 msg.subwindow.parent = window;
559 msg.subwindow.wx = wx;
560 msg.subwindow.wy = wy;
561 msg.subwindow.ww = ww;
562 msg.subwindow.wh = wh;
563 msg.subwindow.fbw = fbw;
564 msg.subwindow.fbh = fbh;
565 msg.subwindow.dpr = dpr;
566 msg.subwindow.rotation = zRot;
567 msg.subwindow.deleteExisting = deleteExisting;
568 msg.subwindow.hideWindow = hideWindow;
569 mHasSubWindow = processMessage(msg);
570
571 D("Exiting mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false");
572 return mHasSubWindow;
573 }
574
removeSubWindow()575 bool RenderWindow::removeSubWindow() {
576 D("Entering mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false");
577 if (!mHasSubWindow) {
578 return false;
579 }
580 mHasSubWindow = false;
581 if (!useThread()) {
582 mRepostCommands.send(RepostCommand::Sync);
583 mRepostCommands.waitForEmpty();
584 }
585
586 RenderWindowMessage msg = {};
587 msg.cmd = CMD_REMOVE_SUBWINDOW;
588 bool result = processMessage(msg);
589 D("Exiting result=%s\n", result ? "success" : "failure");
590 return result;
591 }
592
setRotation(float zRot)593 void RenderWindow::setRotation(float zRot) {
594 D("Entering rotation=%f\n", zRot);
595 RenderWindowMessage msg = {};
596 msg.cmd = CMD_SET_ROTATION;
597 msg.rotation = zRot;
598 (void) processMessage(msg);
599 D("Exiting\n");
600 }
601
setTranslation(float px,float py)602 void RenderWindow::setTranslation(float px, float py) {
603 D("Entering translation=%f,%f\n", px, py);
604 RenderWindowMessage msg = {};
605 msg.cmd = CMD_SET_TRANSLATION;
606 msg.trans.px = px;
607 msg.trans.py = py;
608 (void) processMessage(msg);
609 D("Exiting\n");
610 }
611
setScreenMask(int width,int height,const unsigned char * rgbaData)612 void RenderWindow::setScreenMask(int width, int height, const unsigned char* rgbaData) {
613 if (FrameBuffer* fb = FrameBuffer::getFB()) {
614 if (fb->hasEmulationGl()) {
615 fb->getTextureDraw()->setScreenMask(width, height, rgbaData);
616 } else {
617 ERR("RenderWindow::setScreenMask() not supported without GL emulation.");
618 }
619 }
620 }
621
repaint()622 void RenderWindow::repaint() {
623 D("Entering\n");
624 RenderWindowMessage msg = {};
625 msg.cmd = CMD_REPAINT;
626 (void) processMessage(msg);
627 D("Exiting\n");
628 }
629
hasGuestPostedAFrame()630 bool RenderWindow::hasGuestPostedAFrame() {
631 D("Entering\n");
632 RenderWindowMessage msg = {};
633 msg.cmd = CMD_HAS_GUEST_POSTED_A_FRAME;
634 bool res = processMessage(msg);
635 D("Exiting\n");
636 return res;
637 }
638
resetGuestPostedAFrame()639 void RenderWindow::resetGuestPostedAFrame() {
640 D("Entering\n");
641 RenderWindowMessage msg = {};
642 msg.cmd = CMD_RESET_GUEST_POSTED_A_FRAME;
643 (void) processMessage(msg);
644 D("Exiting\n");
645 }
646
setVsyncHz(int vsyncHz)647 void RenderWindow::setVsyncHz(int vsyncHz) {
648 D("Entering\n");
649 RenderWindowMessage msg = {};
650 msg.cmd = CMD_SET_VSYNC_HZ;
651 msg.vsyncHz = vsyncHz;
652 (void) processMessage(msg);
653 D("Exiting\n");
654 }
655
setDisplayConfigs(int configId,int w,int h,int dpiX,int dpiY)656 void RenderWindow::setDisplayConfigs(int configId, int w, int h,
657 int dpiX, int dpiY) {
658 D("Entering\n");
659 RenderWindowMessage msg = {};
660 msg.cmd = CMD_SET_DISPLAY_CONFIGS;
661 msg.displayConfigs.configId = configId;
662 msg.displayConfigs.width = w;
663 msg.displayConfigs.height= h;
664 msg.displayConfigs.dpiX= dpiX;
665 msg.displayConfigs.dpiY = dpiY;
666 (void) processMessage(msg);
667 D("Exiting\n");
668 }
669
setDisplayActiveConfig(int configId)670 void RenderWindow::setDisplayActiveConfig(int configId) {
671 D("Entering\n");
672 RenderWindowMessage msg = {};
673 msg.cmd = CMD_SET_DISPLAY_ACTIVE_CONFIG;
674 msg.displayActiveConfig = configId;
675 (void) processMessage(msg);
676 D("Exiting\n");
677 }
678
processMessage(const RenderWindowMessage & msg)679 bool RenderWindow::processMessage(const RenderWindowMessage& msg) {
680 if (useThread()) {
681 if (msg.cmd == CMD_REPAINT) {
682 GL_LOG("Sending CMD_REPAINT to render window channel");
683 }
684 return mChannel->sendMessageAndGetResult(msg);
685 } else if (msg.cmd == CMD_REPAINT) {
686 GL_LOG("Sending CMD_REPAINT to reposting thread");
687 mRepostCommands.send(RepostCommand::Repost);
688 return true;
689 } else {
690 return msg.process();
691 }
692 }
693
694 } // namespace gfxstream