• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2016 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 #include "RendererImpl.h"
15 
16 #include "RenderChannelImpl.h"
17 #include "RenderThread.h"
18 
19 #include "base/System.h"
20 #include "snapshot/common.h"
21 #include "host-common/logging.h"
22 
23 #include "ErrorLog.h"
24 #include "FenceSync.h"
25 #include "FrameBuffer.h"
26 
27 #include <algorithm>
28 #include <utility>
29 
30 #include <assert.h>
31 
32 namespace emugl {
33 
34 // kUseSubwindowThread is used to determine whether the RenderWindow should use
35 // a separate thread to manage its subwindow GL/GLES context.
36 // For now, this feature is disabled entirely for the following
37 // reasons:
38 //
39 // - It must be disabled on Windows at all times, otherwise the main window
40 //   becomes unresponsive after a few seconds of user interaction (e.g. trying
41 //   to move it over the desktop). Probably due to the subtle issues around
42 //   input on this platform (input-queue is global, message-queue is
43 //   per-thread). Also, this messes considerably the display of the
44 //   main window when running the executable under Wine.
45 //
46 // - On Linux/XGL and OSX/Cocoa, this used to be necessary to avoid corruption
47 //   issues with the GL state of the main window when using the SDL UI.
48 //   After the switch to Qt, this is no longer necessary and may actually cause
49 //   undesired interactions between the UI thread and the RenderWindow thread:
50 //   for example, in a multi-monitor setup the context might be recreated when
51 //   dragging the window between monitors, triggering a Qt-specific callback
52 //   in the context of RenderWindow thread, which will become blocked on the UI
53 //   thread, which may in turn be blocked on something else.
54 static const bool kUseSubwindowThread = false;
55 
56 // This object manages the cleanup of guest process resources when the process
57 // exits. It runs the cleanup in a separate thread to never block the main
58 // render thread for a low-priority task.
59 class RendererImpl::ProcessCleanupThread {
60 public:
ProcessCleanupThread()61     ProcessCleanupThread()
62         : mCleanupThread([this]() {
63               while (const auto id = mCleanupProcessIds.receive()) {
64                   FrameBuffer::getFB()->cleanupProcGLObjects(*id);
65               }
66           }) {
67         mCleanupThread.start();
68     }
69 
~ProcessCleanupThread()70     ~ProcessCleanupThread() {
71         mCleanupProcessIds.stop();
72         mCleanupThread.wait();
73     }
74 
cleanup(uint64_t processId)75     void cleanup(uint64_t processId) {
76         mCleanupProcessIds.send(processId);
77     }
78 
stop()79     void stop() {
80         mCleanupProcessIds.stop();
81     }
82 
waitForCleanup()83     void waitForCleanup() {
84         mCleanupProcessIds.waitForEmpty();
85     }
86 
87 private:
88     DISALLOW_COPY_AND_ASSIGN(ProcessCleanupThread);
89 
90     android::base::MessageChannel<uint64_t, 64> mCleanupProcessIds;
91     android::base::FunctorThread mCleanupThread;
92 };
93 
RendererImpl()94 RendererImpl::RendererImpl() {
95     mCleanupThread.reset(new ProcessCleanupThread());
96 }
97 
~RendererImpl()98 RendererImpl::~RendererImpl() {
99     stop(true);
100     mRenderWindow.reset();
101 }
102 
initialize(int width,int height,bool useSubWindow,bool egl2egl)103 bool RendererImpl::initialize(int width, int height, bool useSubWindow, bool egl2egl) {
104     if (android::base::getEnvironmentVariable("ANDROID_EMUGL_VERBOSE") == "1") {
105         // base_enable_verbose_logs();
106     }
107 
108     if (mRenderWindow) {
109         return false;
110     }
111 
112     std::unique_ptr<RenderWindow> renderWindow(new RenderWindow(
113             width, height, kUseSubwindowThread, useSubWindow, egl2egl));
114     if (!renderWindow) {
115         ERR("Could not create rendering window class\n");
116         GL_LOG("Could not create rendering window class");
117         return false;
118     }
119     if (!renderWindow->isValid()) {
120         ERR("Could not initialize emulated framebuffer\n");
121         return false;
122     }
123 
124     mRenderWindow = std::move(renderWindow);
125     GL_LOG("OpenGL renderer initialized successfully");
126 
127     // This render thread won't do anything but will only preload resources
128     // for the real threads to start faster.
129     mLoaderRenderThread.reset(new RenderThread(nullptr));
130     mLoaderRenderThread->start();
131 
132     return true;
133 }
134 
stop(bool wait)135 void RendererImpl::stop(bool wait) {
136     android::base::AutoLock lock(mChannelsLock);
137     mStopped = true;
138     auto channels = std::move(mChannels);
139     lock.unlock();
140 
141     if (const auto fb = FrameBuffer::getFB()) {
142         fb->setShuttingDown();
143     }
144     for (const auto& c : channels) {
145         c->stopFromHost();
146     }
147     // We're stopping the renderer, so there's no need to clean up resources
148     // of some pending processes: we'll destroy everything soon.
149     mCleanupThread->stop();
150 
151     mStoppedChannels.insert(mStoppedChannels.end(),
152                             std::make_move_iterator(channels.begin()),
153                             std::make_move_iterator(channels.end()));
154 
155     if (!wait) {
156         return;
157     }
158 
159     // Each render channel is referenced in the corresponing pipe object, so
160     // even if we clear the |channels| vector they could still be alive
161     // for a while. This means we need to make sure to wait for render thread
162     // exit explicitly.
163     for (const auto& c : mStoppedChannels) {
164         c->renderThread()->wait();
165     }
166     mStoppedChannels.clear();
167 }
168 
finish()169 void RendererImpl::finish() {
170     {
171         android::base::AutoLock lock(mChannelsLock);
172         mRenderWindow->setPaused(true);
173     }
174     cleanupRenderThreads();
175     {
176         android::base::AutoLock lock(mChannelsLock);
177         mRenderWindow->setPaused(false);
178     }
179 }
180 
cleanupRenderThreads()181 void RendererImpl::cleanupRenderThreads() {
182     android::base::AutoLock lock(mChannelsLock);
183     const auto channels = std::move(mChannels);
184     assert(mChannels.empty());
185     lock.unlock();
186     for (const auto& c : channels) {
187         // Please DO NOT notify the guest about this event (DO NOT call
188         // stopFromHost() ), because this is used to kill old threads when
189         // loading from a snapshot, and the newly loaded guest should not
190         // be notified for those behavior.
191         c->stop();
192     }
193     for (const auto& c : channels) {
194         c->renderThread()->wait();
195     }
196 }
197 
waitForProcessCleanup()198 void RendererImpl::waitForProcessCleanup() {
199     mCleanupThread->waitForCleanup();
200     // Recreate it to make sure we've started from scratch and that we've
201     // finished all in-progress cleanups as well.
202     mCleanupThread.reset(new ProcessCleanupThread());
203 }
204 
createRenderChannel(android::base::Stream * loadStream)205 RenderChannelPtr RendererImpl::createRenderChannel(
206         android::base::Stream* loadStream) {
207     const auto channel = std::make_shared<RenderChannelImpl>(loadStream);
208     {
209         android::base::AutoLock lock(mChannelsLock);
210 
211         if (mStopped) {
212             return nullptr;
213         }
214 
215         // Clean up the stopped channels.
216         mChannels.erase(
217                 std::remove_if(mChannels.begin(), mChannels.end(),
218                                [](const std::shared_ptr<RenderChannelImpl>& c) {
219                                    return c->renderThread()->isFinished();
220                                }),
221                 mChannels.end());
222         mChannels.emplace_back(channel);
223 
224         // Take the time to check if our loader thread is done as well.
225         if (mLoaderRenderThread && mLoaderRenderThread->isFinished()) {
226             mLoaderRenderThread->wait();
227             mLoaderRenderThread.reset();
228         }
229 
230         DBG("Started new RenderThread (total %" PRIu64 ") @%p\n",
231             static_cast<uint64_t>(mChannels.size()), channel->renderThread());
232     }
233 
234     return channel;
235 }
236 
addressSpaceGraphicsConsumerCreate(struct asg_context context,android::base::Stream * loadStream,android::emulation::asg::ConsumerCallbacks callbacks)237 void* RendererImpl::addressSpaceGraphicsConsumerCreate(
238     struct asg_context context,
239     android::base::Stream* loadStream,
240     android::emulation::asg::ConsumerCallbacks callbacks) {
241     auto thread = new RenderThread(context, loadStream, callbacks);
242     thread->start();
243     return (void*)thread;
244 }
245 
addressSpaceGraphicsConsumerDestroy(void * consumer)246 void RendererImpl::addressSpaceGraphicsConsumerDestroy(void* consumer) {
247     RenderThread* thread = (RenderThread*)consumer;
248     thread->wait();
249     delete thread;
250 }
251 
addressSpaceGraphicsConsumerPreSave(void * consumer)252 void RendererImpl::addressSpaceGraphicsConsumerPreSave(void* consumer) {
253     RenderThread* thread = (RenderThread*)consumer;
254     thread->pausePreSnapshot();
255 }
256 
addressSpaceGraphicsConsumerSave(void * consumer,android::base::Stream * stream)257 void RendererImpl::addressSpaceGraphicsConsumerSave(void* consumer, android::base::Stream* stream) {
258     RenderThread* thread = (RenderThread*)consumer;
259     thread->save(stream);
260 }
261 
addressSpaceGraphicsConsumerPostSave(void * consumer)262 void RendererImpl::addressSpaceGraphicsConsumerPostSave(void* consumer) {
263     RenderThread* thread = (RenderThread*)consumer;
264     thread->resume();
265 }
266 
addressSpaceGraphicsConsumerRegisterPostLoadRenderThread(void * consumer)267 void RendererImpl::addressSpaceGraphicsConsumerRegisterPostLoadRenderThread(void* consumer) {
268     RenderThread* thread = (RenderThread*)consumer;
269     mAdditionalPostLoadRenderThreads.push_back(thread);
270 }
271 
pauseAllPreSave()272 void RendererImpl::pauseAllPreSave() {
273     android::base::AutoLock lock(mChannelsLock);
274     if (mStopped) {
275         return;
276     }
277     for (const auto& c : mChannels) {
278         c->renderThread()->pausePreSnapshot();
279     }
280     lock.unlock();
281     waitForProcessCleanup();
282 }
283 
resumeAll()284 void RendererImpl::resumeAll() {
285     {
286         android::base::AutoLock lock(mChannelsLock);
287         if (mStopped) {
288             return;
289         }
290         for (const auto& c : mChannels) {
291             c->renderThread()->resume();
292         }
293 
294         for (const auto t: mAdditionalPostLoadRenderThreads) {
295             t->resume();
296         }
297         mAdditionalPostLoadRenderThreads.clear();
298     }
299 
300     repaintOpenGLDisplay();
301 }
302 
save(android::base::Stream * stream,const android::snapshot::ITextureSaverPtr & textureSaver)303 void RendererImpl::save(android::base::Stream* stream,
304                         const android::snapshot::ITextureSaverPtr& textureSaver) {
305     stream->putByte(mStopped);
306     if (mStopped) {
307         return;
308     }
309     auto fb = FrameBuffer::getFB();
310     assert(fb);
311     fb->onSave(stream, textureSaver);
312 
313     FenceSync::onSave(stream);
314 }
315 
load(android::base::Stream * stream,const android::snapshot::ITextureLoaderPtr & textureLoader)316 bool RendererImpl::load(android::base::Stream* stream,
317                         const android::snapshot::ITextureLoaderPtr& textureLoader) {
318 
319 #ifdef SNAPSHOT_PROFILE
320     android::base::System::Duration startTime =
321             android::base::System::get()->getUnixTimeUs();
322 #endif
323     waitForProcessCleanup();
324 #ifdef SNAPSHOT_PROFILE
325     printf("Previous session cleanup time: %lld ms\n",
326            (long long)(android::base::System::get()
327                                ->getUnixTimeUs() -
328                        startTime) /
329                    1000);
330 #endif
331 
332     mStopped = stream->getByte();
333     if (mStopped) {
334         return true;
335     }
336     auto fb = FrameBuffer::getFB();
337     assert(fb);
338 
339     bool res = true;
340 
341     res = fb->onLoad(stream, textureLoader);
342     FenceSync::onLoad(stream);
343 
344     return res;
345 }
346 
fillGLESUsages(android_studio::EmulatorGLESUsages * usages)347 void RendererImpl::fillGLESUsages(android_studio::EmulatorGLESUsages* usages) {
348     auto fb = FrameBuffer::getFB();
349     if (fb) fb->fillGLESUsages(usages);
350 }
351 
getScreenshot(unsigned int nChannels,unsigned int * width,unsigned int * height,std::vector<unsigned char> & pixels,int displayId,int desiredWidth,int desiredHeight,int desiredRotation)352 void RendererImpl::getScreenshot(unsigned int nChannels, unsigned int* width,
353         unsigned int* height, std::vector<unsigned char>& pixels, int displayId,
354         int desiredWidth, int desiredHeight, int desiredRotation) {
355     auto fb = FrameBuffer::getFB();
356     if (fb) fb->getScreenshot(nChannels, width, height, pixels, displayId,
357                               desiredWidth, desiredHeight, desiredRotation);
358 }
359 
setMultiDisplay(uint32_t id,int32_t x,int32_t y,uint32_t w,uint32_t h,uint32_t dpi,bool add)360 void RendererImpl::setMultiDisplay(uint32_t id,
361                                    int32_t x,
362                                    int32_t y,
363                                    uint32_t w,
364                                    uint32_t h,
365                                    uint32_t dpi,
366                                    bool add) {
367     auto fb = FrameBuffer::getFB();
368     if (fb) {
369         if (add) {
370             fb->createDisplay(&id);
371             fb->setDisplayPose(id, x, y, w, h, dpi);
372         } else {
373             fb->destroyDisplay(id);
374         }
375     }
376 }
377 
setMultiDisplayColorBuffer(uint32_t id,uint32_t cb)378 void RendererImpl::setMultiDisplayColorBuffer(uint32_t id, uint32_t cb) {
379     auto fb = FrameBuffer::getFB();
380     if (fb) {
381         fb->setDisplayColorBuffer(id, cb);
382     }
383 }
384 
getHardwareStrings()385 RendererImpl::HardwareStrings RendererImpl::getHardwareStrings() {
386     assert(mRenderWindow);
387 
388     const char* vendor = nullptr;
389     const char* renderer = nullptr;
390     const char* version = nullptr;
391     if (!mRenderWindow->getHardwareStrings(&vendor, &renderer, &version)) {
392         return {};
393     }
394     HardwareStrings res;
395     res.vendor = vendor ? vendor : "";
396     res.renderer = renderer ? renderer : "";
397     res.version = version ? version : "";
398     return res;
399 }
400 
setPostCallback(RendererImpl::OnPostCallback onPost,void * context,bool useBgraReadback,uint32_t displayId)401 void RendererImpl::setPostCallback(RendererImpl::OnPostCallback onPost,
402                                    void* context,
403                                    bool useBgraReadback,
404                                    uint32_t displayId) {
405     assert(mRenderWindow);
406     mRenderWindow->setPostCallback(onPost, context, displayId, useBgraReadback);
407 }
408 
asyncReadbackSupported()409 bool RendererImpl::asyncReadbackSupported() {
410     assert(mRenderWindow);
411     return mRenderWindow->asyncReadbackSupported();
412 }
413 
414 RendererImpl::ReadPixelsCallback
getReadPixelsCallback()415 RendererImpl::getReadPixelsCallback() {
416     assert(mRenderWindow);
417     return mRenderWindow->getReadPixelsCallback();
418 }
419 
420 RendererImpl::FlushReadPixelPipeline
getFlushReadPixelPipeline()421 RendererImpl::getFlushReadPixelPipeline() {
422     assert(mRenderWindow);
423     return mRenderWindow->getFlushReadPixelPipeline();
424 }
425 
showOpenGLSubwindow(FBNativeWindowType window,int wx,int wy,int ww,int wh,int fbw,int fbh,float dpr,float zRot,bool deleteExisting,bool hideWindow)426 bool RendererImpl::showOpenGLSubwindow(FBNativeWindowType window,
427                                        int wx,
428                                        int wy,
429                                        int ww,
430                                        int wh,
431                                        int fbw,
432                                        int fbh,
433                                        float dpr,
434                                        float zRot,
435                                        bool deleteExisting,
436                                        bool hideWindow) {
437     assert(mRenderWindow);
438     return mRenderWindow->setupSubWindow(window, wx, wy, ww, wh, fbw, fbh, dpr,
439                                          zRot, deleteExisting, hideWindow);
440 }
441 
destroyOpenGLSubwindow()442 bool RendererImpl::destroyOpenGLSubwindow() {
443     assert(mRenderWindow);
444     return mRenderWindow->removeSubWindow();
445 }
446 
setOpenGLDisplayRotation(float zRot)447 void RendererImpl::setOpenGLDisplayRotation(float zRot) {
448     assert(mRenderWindow);
449     mRenderWindow->setRotation(zRot);
450 }
451 
setOpenGLDisplayTranslation(float px,float py)452 void RendererImpl::setOpenGLDisplayTranslation(float px, float py) {
453     assert(mRenderWindow);
454     mRenderWindow->setTranslation(px, py);
455 }
456 
repaintOpenGLDisplay()457 void RendererImpl::repaintOpenGLDisplay() {
458     assert(mRenderWindow);
459     mRenderWindow->repaint();
460 }
461 
hasGuestPostedAFrame()462 bool RendererImpl::hasGuestPostedAFrame() {
463     if (mRenderWindow) {
464         return mRenderWindow->hasGuestPostedAFrame();
465     }
466     return false;
467 }
468 
resetGuestPostedAFrame()469 void RendererImpl::resetGuestPostedAFrame() {
470     if (mRenderWindow) {
471         mRenderWindow->resetGuestPostedAFrame();
472     }
473 }
474 
setScreenMask(int width,int height,const unsigned char * rgbaData)475 void RendererImpl::setScreenMask(int width, int height, const unsigned char* rgbaData) {
476     assert(mRenderWindow);
477     mRenderWindow->setScreenMask(width, height, rgbaData);
478 }
479 
cleanupProcGLObjects(uint64_t puid)480 void RendererImpl::cleanupProcGLObjects(uint64_t puid) {
481     mCleanupThread->cleanup(puid);
482 }
483 
484 static struct AndroidVirtioGpuOps sVirtioGpuOps = {
485         .create_color_buffer_with_handle =
486                 [](uint32_t width,
487                    uint32_t height,
488                    uint32_t format,
489                    uint32_t fwkFormat,
__anona47589360302(uint32_t width, uint32_t height, uint32_t format, uint32_t fwkFormat, uint32_t handle) 490                    uint32_t handle) {
491                     FrameBuffer::getFB()->createColorBufferWithHandle(
492                             width, height, (GLenum)format,
493                             (FrameworkFormat)fwkFormat, handle);
494                 },
495         .open_color_buffer =
__anona47589360402(uint32_t handle) 496                 [](uint32_t handle) {
497                     FrameBuffer::getFB()->openColorBuffer(handle);
498                 },
499         .close_color_buffer =
__anona47589360502(uint32_t handle) 500                 [](uint32_t handle) {
501                     FrameBuffer::getFB()->closeColorBuffer(handle);
502                 },
503         .update_color_buffer =
504                 [](uint32_t handle,
505                    int x,
506                    int y,
507                    int width,
508                    int height,
509                    uint32_t format,
510                    uint32_t type,
__anona47589360602(uint32_t handle, int x, int y, int width, int height, uint32_t format, uint32_t type, void* pixels) 511                    void* pixels) {
512                     FrameBuffer::getFB()->updateColorBuffer(
513                             handle, x, y, width, height, format, type, pixels);
514                 },
515         .read_color_buffer =
516                 [](uint32_t handle,
517                    int x,
518                    int y,
519                    int width,
520                    int height,
521                    uint32_t format,
522                    uint32_t type,
__anona47589360702(uint32_t handle, int x, int y, int width, int height, uint32_t format, uint32_t type, void* pixels) 523                    void* pixels) {
524                     FrameBuffer::getFB()->readColorBuffer(
525                             handle, x, y, width, height, format, type, pixels);
526                 },
527         .read_color_buffer_yuv =
528                 [](uint32_t handle,
529                    int x,
530                    int y,
531                    int width,
532                    int height,
533                    void* pixels,
__anona47589360802(uint32_t handle, int x, int y, int width, int height, void* pixels, uint32_t pixels_size) 534                    uint32_t pixels_size) {
535                     FrameBuffer::getFB()->readColorBufferYUV(
536                             handle, x, y, width, height, pixels, pixels_size);
537                 },
538         .post_color_buffer =
__anona47589360902(uint32_t handle) 539                 [](uint32_t handle) { FrameBuffer::getFB()->post(handle); },
__anona47589360a02() 540         .repost = []() { FrameBuffer::getFB()->repost(); },
541         .create_yuv_textures =
542                 [](uint32_t type,
543                    uint32_t count,
544                    int width,
545                    int height,
__anona47589360b02(uint32_t type, uint32_t count, int width, int height, uint32_t* output) 546                    uint32_t* output) {
547                     FrameBuffer::getFB()->createYUVTextures(type, count, width,
548                                                             height, output);
549                 },
550         .destroy_yuv_textures =
__anona47589360c02(uint32_t type, uint32_t count, uint32_t* textures) 551                 [](uint32_t type, uint32_t count, uint32_t* textures) {
552                     FrameBuffer::getFB()->destroyYUVTextures(type, count,
553                                                              textures);
554                 },
555         .update_yuv_textures =
556                 [](uint32_t type,
557                    uint32_t* textures,
558                    void* privData,
__anona47589360d02(uint32_t type, uint32_t* textures, void* privData, void* func) 559                    void* func) {
560                     FrameBuffer::getFB()->updateYUVTextures(type, textures,
561                                                             privData, func);
562                 },
563         .swap_textures_and_update_color_buffer =
564                 [](uint32_t colorbufferhandle,
565                    int x,
566                    int y,
567                    int width,
568                    int height,
569                    uint32_t format,
570                    uint32_t type,
571                    uint32_t texture_type,
__anona47589360e02(uint32_t colorbufferhandle, int x, int y, int width, int height, uint32_t format, uint32_t type, uint32_t texture_type, uint32_t* textures) 572                    uint32_t* textures) {
573                     FrameBuffer::getFB()->swapTexturesAndUpdateColorBuffer(
574                             colorbufferhandle, x, y, width, height, format,
575                             type, texture_type, textures);
576                 },
__anona47589360f02() 577         .get_last_posted_color_buffer = []() {
578             return FrameBuffer::getFB()->getLastPostedColorBuffer();
579         },
__anona47589361002(uint32_t handle) 580         .bind_color_buffer_to_texture = [](uint32_t handle) {
581             FrameBuffer::getFB()->bindColorBufferToTexture2(handle);
582         },
__anona47589361102() 583         .get_global_egl_context = []() {
584             return FrameBuffer::getFB()->getGlobalEGLContext();
585         },
__anona47589361202(uint64_t eglsync) 586         .wait_for_gpu = [](uint64_t eglsync) {
587             FrameBuffer::getFB()->waitForGpu(eglsync);
588         },
__anona47589361302(uint64_t device, uint64_t fence) 589         .wait_for_gpu_vulkan = [](uint64_t device, uint64_t fence) {
590             FrameBuffer::getFB()->waitForGpuVulkan(device, fence);
591         },
__anona47589361402(bool guestManaged) 592         .set_guest_managed_color_buffer_lifetime = [](bool guestManaged) {
593             FrameBuffer::getFB()->setGuestManagedColorBufferLifetime(true);
594         },
595 };
596 
getVirtioGpuOps()597 struct AndroidVirtioGpuOps* RendererImpl::getVirtioGpuOps() {
598     return &sVirtioGpuOps;
599 }
600 
snapshotOperationCallback(int op,int stage)601 void RendererImpl::snapshotOperationCallback(int op, int stage) {
602     using namespace android::snapshot;
603     switch (op) {
604         case SNAPSHOTTER_OPERATION_LOAD:
605             if (stage == SNAPSHOTTER_STAGE_START) {
606 #ifdef SNAPSHOT_PROFILE
607              android::base::System::Duration startTime =
608                      android::base::System::get()->getUnixTimeUs();
609 #endif
610                 mRenderWindow->setPaused(true);
611                 cleanupRenderThreads();
612 #ifdef SNAPSHOT_PROFILE
613                 printf("Previous session suspend time: %lld ms\n",
614                        (long long)(android::base::System::get()
615                                            ->getUnixTimeUs() -
616                                    startTime) /
617                                1000);
618 #endif
619             }
620             if (stage == SNAPSHOTTER_STAGE_END) {
621                 mRenderWindow->setPaused(false);
622             }
623             break;
624         default:
625             break;
626     }
627 }
628 
629 }  // namespace emugl
630