// Copyright (C) 2016 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "RendererImpl.h" #include #include #include #include #include "FrameBuffer.h" #include "RenderChannelImpl.h" #include "RenderThread.h" #include "aemu/base/system/System.h" #include "aemu/base/threads/WorkerThread.h" #include "host-common/logging.h" #include "snapshot/common.h" #if GFXSTREAM_ENABLE_HOST_GLES #include "gl/EmulatedEglFenceSync.h" #endif namespace gfxstream { // kUseSubwindowThread is used to determine whether the RenderWindow should use // a separate thread to manage its subwindow GL/GLES context. // For now, this feature is disabled entirely for the following // reasons: // // - It must be disabled on Windows at all times, otherwise the main window // becomes unresponsive after a few seconds of user interaction (e.g. trying // to move it over the desktop). Probably due to the subtle issues around // input on this platform (input-queue is global, message-queue is // per-thread). Also, this messes considerably the display of the // main window when running the executable under Wine. // // - On Linux/XGL and OSX/Cocoa, this used to be necessary to avoid corruption // issues with the GL state of the main window when using the SDL UI. // After the switch to Qt, this is no longer necessary and may actually cause // undesired interactions between the UI thread and the RenderWindow thread: // for example, in a multi-monitor setup the context might be recreated when // dragging the window between monitors, triggering a Qt-specific callback // in the context of RenderWindow thread, which will become blocked on the UI // thread, which may in turn be blocked on something else. static const bool kUseSubwindowThread = false; // This object manages the cleanup of guest process resources when the process // exits. It runs the cleanup in a separate thread to never block the main // render thread for a low-priority task. class RendererImpl::ProcessCleanupThread { public: ProcessCleanupThread() : mCleanupWorker([](Cmd cmd) { using android::base::WorkerProcessingResult; struct { WorkerProcessingResult operator()(CleanProcessResources resources) { FrameBuffer::getFB()->cleanupProcGLObjects(resources.puid); // resources.resource are destroyed automatically when going out of the scope. return WorkerProcessingResult::Continue; } WorkerProcessingResult operator()(Exit) { return WorkerProcessingResult::Stop; } } visitor; return std::visit(visitor, std::move(cmd)); }) { mCleanupWorker.start(); } ~ProcessCleanupThread() { mCleanupWorker.enqueue(Exit{}); } void cleanup(uint64_t processId, std::unique_ptr resource) { mCleanupWorker.enqueue(CleanProcessResources{ .puid = processId, .resource = std::move(resource), }); } void stop() { mCleanupWorker.enqueue(Exit{}); mCleanupWorker.join(); } void waitForCleanup() { mCleanupWorker.waitQueuedItems(); } private: struct CleanProcessResources { uint64_t puid; std::unique_ptr resource; }; struct Exit {}; using Cmd = std::variant; DISALLOW_COPY_AND_ASSIGN(ProcessCleanupThread); android::base::WorkerThread mCleanupWorker; }; RendererImpl::RendererImpl() { mCleanupThread.reset(new ProcessCleanupThread()); } RendererImpl::~RendererImpl() { stop(true); // We can't finish until the loader render thread has // completed else can get a crash at the end of the destructor. if (mLoaderRenderThread) { mLoaderRenderThread->wait(); } mRenderWindow.reset(); } bool RendererImpl::initialize(int width, int height, gfxstream::host::FeatureSet features, bool useSubWindow, bool egl2egl) { if (android::base::getEnvironmentVariable("ANDROID_EMUGL_VERBOSE") == "1") { // base_enable_verbose_logs(); } if (mRenderWindow) { return false; } std::unique_ptr renderWindow(new RenderWindow( width, height, features, kUseSubwindowThread, useSubWindow, egl2egl)); if (!renderWindow) { ERR("Could not create rendering window class\n"); GL_LOG("Could not create rendering window class"); return false; } if (!renderWindow->isValid()) { ERR("Could not initialize emulated framebuffer\n"); return false; } mRenderWindow = std::move(renderWindow); GL_LOG("OpenGL renderer initialized successfully"); // This render thread won't do anything but will only preload resources // for the real threads to start faster. mLoaderRenderThread.reset(new RenderThread(nullptr)); mLoaderRenderThread->start(); return true; } void RendererImpl::stop(bool wait) { android::base::AutoLock lock(mChannelsLock); mStopped = true; auto channels = std::move(mChannels); lock.unlock(); if (const auto fb = FrameBuffer::getFB()) { fb->setShuttingDown(); } for (const auto& c : channels) { c->stopFromHost(); } // We're stopping the renderer, so there's no need to clean up resources // of some pending processes: we'll destroy everything soon. mCleanupThread->stop(); mStoppedChannels.insert(mStoppedChannels.end(), std::make_move_iterator(channels.begin()), std::make_move_iterator(channels.end())); if (!wait) { return; } // Each render channel is referenced in the corresponing pipe object, so // even if we clear the |channels| vector they could still be alive // for a while. This means we need to make sure to wait for render thread // exit explicitly. for (const auto& c : mStoppedChannels) { c->renderThread()->wait(); } mCleanupThread->waitForCleanup(); mStoppedChannels.clear(); } void RendererImpl::finish() { { android::base::AutoLock lock(mChannelsLock); mRenderWindow->setPaused(true); } cleanupRenderThreads(); { android::base::AutoLock lock(mChannelsLock); mRenderWindow->setPaused(false); } } void RendererImpl::cleanupRenderThreads() { android::base::AutoLock lock(mChannelsLock); const auto channels = std::move(mChannels); assert(mChannels.empty()); lock.unlock(); for (const auto& c : channels) { // Please DO NOT notify the guest about this event (DO NOT call // stopFromHost() ), because this is used to kill old threads when // loading from a snapshot, and the newly loaded guest should not // be notified for those behavior. c->stop(); } for (const auto& c : channels) { c->renderThread()->wait(); } } void RendererImpl::waitForProcessCleanup() { mCleanupThread->waitForCleanup(); // Recreate it to make sure we've started from scratch and that we've // finished all in-progress cleanups as well. mCleanupThread.reset(new ProcessCleanupThread()); } RenderChannelPtr RendererImpl::createRenderChannel( android::base::Stream* loadStream, uint32_t virtioGpuContextId) { const auto channel = std::make_shared(loadStream, virtioGpuContextId); { android::base::AutoLock lock(mChannelsLock); if (mStopped) { return nullptr; } // Clean up the stopped channels. mChannels.erase( std::remove_if(mChannels.begin(), mChannels.end(), [](const std::shared_ptr& c) { return c->renderThread()->isFinished(); }), mChannels.end()); mChannels.emplace_back(channel); // Take the time to check if our loader thread is done as well. if (mLoaderRenderThread && mLoaderRenderThread->isFinished()) { mLoaderRenderThread->wait(); mLoaderRenderThread.reset(); } GL_LOG("Started new RenderThread (total %" PRIu64 ") @%p", static_cast(mChannels.size()), channel->renderThread()); } return channel; } void RendererImpl::addListener(FrameBufferChangeEventListener* listener) { mRenderWindow->addListener(listener); } void RendererImpl::removeListener(FrameBufferChangeEventListener* listener) { mRenderWindow->removeListener(listener); } void* RendererImpl::addressSpaceGraphicsConsumerCreate( struct asg_context context, android::base::Stream* loadStream, android::emulation::asg::ConsumerCallbacks callbacks, uint32_t contextId, uint32_t capsetId, std::optional nameOpt) { auto thread = new RenderThread(context, loadStream, callbacks, contextId, capsetId, std::move(nameOpt)); thread->start(); android::base::AutoLock lock(mAddressSpaceRenderThreadLock); mAddressSpaceRenderThreads.emplace(thread); return (void*)thread; } void RendererImpl::addressSpaceGraphicsConsumerDestroy(void* consumer) { RenderThread* thread = (RenderThread*)consumer; { android::base::AutoLock lock(mAddressSpaceRenderThreadLock); mAddressSpaceRenderThreads.erase(thread); } thread->wait(); delete thread; } void RendererImpl::addressSpaceGraphicsConsumerPreSave(void* consumer) { RenderThread* thread = (RenderThread*)consumer; thread->pausePreSnapshot(); } void RendererImpl::addressSpaceGraphicsConsumerSave(void* consumer, android::base::Stream* stream) { RenderThread* thread = (RenderThread*)consumer; thread->save(stream); } void RendererImpl::addressSpaceGraphicsConsumerPostSave(void* consumer) { RenderThread* thread = (RenderThread*)consumer; thread->resume(true); } void RendererImpl::addressSpaceGraphicsConsumerRegisterPostLoadRenderThread(void* consumer) { RenderThread* thread = (RenderThread*)consumer; mAdditionalPostLoadRenderThreads.push_back(thread); } void RendererImpl::pauseAllPreSave() { { android::base::AutoLock lock(mChannelsLock); if (mStopped) { return; } for (const auto& c : mChannels) { c->renderThread()->pausePreSnapshot(); } } { android::base::AutoLock lock(mAddressSpaceRenderThreadLock); for (const auto& thread : mAddressSpaceRenderThreads) { thread->pausePreSnapshot(); } } waitForProcessCleanup(); } void RendererImpl::resumeAll(bool waitForSave) { { android::base::AutoLock lock(mAddressSpaceRenderThreadLock); for (const auto t : mAdditionalPostLoadRenderThreads) { t->resume(waitForSave); } } { android::base::AutoLock lock(mChannelsLock); if (mStopped) { return; } for (const auto& c : mChannels) { c->renderThread()->resume(waitForSave); } for (const auto& thread : mAddressSpaceRenderThreads) { thread->resume(waitForSave); } mAdditionalPostLoadRenderThreads.clear(); } repaintOpenGLDisplay(); } void RendererImpl::save(android::base::Stream* stream, const android::snapshot::ITextureSaverPtr& textureSaver) { stream->putByte(mStopped); if (mStopped) { return; } auto fb = FrameBuffer::getFB(); assert(fb); fb->onSave(stream, textureSaver); } bool RendererImpl::load(android::base::Stream* stream, const android::snapshot::ITextureLoaderPtr& textureLoader) { #ifdef SNAPSHOT_PROFILE android::base::System::Duration startTime = android::base::System::get()->getUnixTimeUs(); #endif waitForProcessCleanup(); #ifdef SNAPSHOT_PROFILE printf("Previous session cleanup time: %lld ms\n", (long long)(android::base::System::get() ->getUnixTimeUs() - startTime) / 1000); #endif mStopped = stream->getByte(); if (mStopped) { return true; } auto fb = FrameBuffer::getFB(); assert(fb); bool res = true; res = fb->onLoad(stream, textureLoader); #if GFXSTREAM_ENABLE_HOST_GLES gl::EmulatedEglFenceSync::onLoad(stream); #endif return res; } void RendererImpl::fillGLESUsages(android_studio::EmulatorGLESUsages* usages) { auto fb = FrameBuffer::getFB(); #if GFXSTREAM_ENABLE_HOST_GLES if (fb) fb->fillGLESUsages(usages); #endif } int RendererImpl::getScreenshot(unsigned int nChannels, unsigned int* width, unsigned int* height, uint8_t* pixels, size_t* cPixels, int displayId = 0, int desiredWidth = 0, int desiredHeight = 0, int desiredRotation = 0, Rect rect = {{0, 0}, {0, 0}}) { auto fb = FrameBuffer::getFB(); if (fb) { return fb->getScreenshot(nChannels, width, height, pixels, cPixels, displayId, desiredWidth, desiredHeight, desiredRotation, rect); } *cPixels = 0; return -1; } void RendererImpl::setMultiDisplay(uint32_t id, int32_t x, int32_t y, uint32_t w, uint32_t h, uint32_t dpi, bool add) { auto fb = FrameBuffer::getFB(); if (fb) { if (add) { fb->createDisplay(&id); fb->setDisplayPose(id, x, y, w, h, dpi); } else { fb->destroyDisplay(id); } } } void RendererImpl::setMultiDisplayColorBuffer(uint32_t id, uint32_t cb) { auto fb = FrameBuffer::getFB(); if (fb) { fb->setDisplayColorBuffer(id, cb); } } RendererImpl::HardwareStrings RendererImpl::getHardwareStrings() { assert(mRenderWindow); const char* vendor = nullptr; const char* renderer = nullptr; const char* version = nullptr; if (!mRenderWindow->getHardwareStrings(&vendor, &renderer, &version)) { return {}; } HardwareStrings res; res.vendor = vendor ? vendor : ""; res.renderer = renderer ? renderer : ""; res.version = version ? version : ""; return res; } void RendererImpl::setPostCallback(RendererImpl::OnPostCallback onPost, void* context, bool useBgraReadback, uint32_t displayId) { assert(mRenderWindow); mRenderWindow->setPostCallback(onPost, context, displayId, useBgraReadback); } bool RendererImpl::asyncReadbackSupported() { assert(mRenderWindow); return mRenderWindow->asyncReadbackSupported(); } RendererImpl::ReadPixelsCallback RendererImpl::getReadPixelsCallback() { assert(mRenderWindow); return mRenderWindow->getReadPixelsCallback(); } RendererImpl::FlushReadPixelPipeline RendererImpl::getFlushReadPixelPipeline() { assert(mRenderWindow); return mRenderWindow->getFlushReadPixelPipeline(); } bool RendererImpl::showOpenGLSubwindow(FBNativeWindowType window, int wx, int wy, int ww, int wh, int fbw, int fbh, float dpr, float zRot, bool deleteExisting, bool hideWindow) { assert(mRenderWindow); return mRenderWindow->setupSubWindow(window, wx, wy, ww, wh, fbw, fbh, dpr, zRot, deleteExisting, hideWindow); } bool RendererImpl::destroyOpenGLSubwindow() { assert(mRenderWindow); return mRenderWindow->removeSubWindow(); } void RendererImpl::setOpenGLDisplayRotation(float zRot) { assert(mRenderWindow); mRenderWindow->setRotation(zRot); } void RendererImpl::setOpenGLDisplayTranslation(float px, float py) { assert(mRenderWindow); mRenderWindow->setTranslation(px, py); } void RendererImpl::repaintOpenGLDisplay() { assert(mRenderWindow); mRenderWindow->repaint(); } bool RendererImpl::hasGuestPostedAFrame() { if (mRenderWindow) { return mRenderWindow->hasGuestPostedAFrame(); } return false; } void RendererImpl::resetGuestPostedAFrame() { if (mRenderWindow) { mRenderWindow->resetGuestPostedAFrame(); } } void RendererImpl::setScreenMask(int width, int height, const unsigned char* rgbaData) { assert(mRenderWindow); mRenderWindow->setScreenMask(width, height, rgbaData); } void RendererImpl::onGuestGraphicsProcessCreate(uint64_t puid) { FrameBuffer::getFB()->createGraphicsProcessResources(puid); } void RendererImpl::cleanupProcGLObjects(uint64_t puid) { std::unique_ptr resource = FrameBuffer::getFB()->removeGraphicsProcessResources(puid); mCleanupThread->cleanup(puid, std::move(resource)); } static struct AndroidVirtioGpuOps sVirtioGpuOps = { .create_buffer_with_handle = [](uint64_t size, uint32_t handle) { FrameBuffer::getFB()->createBufferWithHandle(size, handle); }, .create_color_buffer_with_handle = [](uint32_t width, uint32_t height, uint32_t format, uint32_t fwkFormat, uint32_t handle, bool linear) { FrameBuffer::getFB()->createColorBufferWithHandle( width, height, (GLenum)format, (FrameworkFormat)fwkFormat, handle, linear); }, .open_color_buffer = [](uint32_t handle) { FrameBuffer::getFB()->openColorBuffer(handle); }, .close_buffer = [](uint32_t handle) { FrameBuffer::getFB()->closeBuffer(handle); }, .close_color_buffer = [](uint32_t handle) { FrameBuffer::getFB()->closeColorBuffer(handle); }, .update_buffer = [](uint32_t handle, uint64_t offset, uint64_t size, void* bytes) { FrameBuffer::getFB()->updateBuffer(handle, offset, size, bytes); }, .update_color_buffer = [](uint32_t handle, int x, int y, int width, int height, uint32_t format, uint32_t type, void* pixels) { FrameBuffer::getFB()->updateColorBuffer(handle, x, y, width, height, format, type, pixels); }, .read_buffer = [](uint32_t handle, uint64_t offset, uint64_t size, void* bytes) { FrameBuffer::getFB()->readBuffer(handle, offset, size, bytes); }, .read_color_buffer = [](uint32_t handle, int x, int y, int width, int height, uint32_t format, uint32_t type, void* pixels) { FrameBuffer::getFB()->readColorBuffer(handle, x, y, width, height, format, type, pixels); }, .read_color_buffer_yuv = [](uint32_t handle, int x, int y, int width, int height, void* pixels, uint32_t pixels_size) { FrameBuffer::getFB()->readColorBufferYUV(handle, x, y, width, height, pixels, pixels_size); }, .post_color_buffer = [](uint32_t handle) { FrameBuffer::getFB()->post(handle); }, .async_post_color_buffer = [](uint32_t handle, CpuCompletionCallback cb) { FrameBuffer::getFB()->postWithCallback(handle, cb); }, .repost = []() { FrameBuffer::getFB()->repost(); }, #if GFXSTREAM_ENABLE_HOST_GLES .create_yuv_textures = [](uint32_t type, uint32_t count, int width, int height, uint32_t* output) { FrameBuffer::getFB()->createYUVTextures(type, count, width, height, output); }, .destroy_yuv_textures = [](uint32_t type, uint32_t count, uint32_t* textures) { FrameBuffer::getFB()->destroyYUVTextures(type, count, textures); }, .update_yuv_textures = [](uint32_t type, uint32_t* textures, void* privData, void* func) { FrameBuffer::getFB()->updateYUVTextures(type, textures, privData, func); }, .swap_textures_and_update_color_buffer = [](uint32_t colorbufferhandle, int x, int y, int width, int height, uint32_t format, uint32_t type, uint32_t texture_type, uint32_t* textures, void* metadata) { FrameBuffer::getFB()->swapTexturesAndUpdateColorBuffer( colorbufferhandle, x, y, width, height, format, type, texture_type, textures); }, #endif .get_last_posted_color_buffer = []() { return FrameBuffer::getFB()->getLastPostedColorBuffer(); }, #if GFXSTREAM_ENABLE_HOST_GLES .bind_color_buffer_to_texture = [](uint32_t handle) { FrameBuffer::getFB()->bindColorBufferToTexture2(handle); }, .get_global_egl_context = []() { return FrameBuffer::getFB()->getGlobalEGLContext(); }, .wait_for_gpu = [](uint64_t eglsync) { FrameBuffer::getFB()->waitForGpu(eglsync); }, #endif .wait_for_gpu_vulkan = [](uint64_t device, uint64_t fence) { FrameBuffer::getFB()->waitForGpuVulkan(device, fence); }, .set_guest_managed_color_buffer_lifetime = [](bool guestManaged) { FrameBuffer::getFB()->setGuestManagedColorBufferLifetime(guestManaged); }, #if GFXSTREAM_ENABLE_HOST_GLES .async_wait_for_gpu_with_cb = [](uint64_t eglsync, FenceCompletionCallback cb) { FrameBuffer::getFB()->asyncWaitForGpuWithCb(eglsync, cb); }, #endif .async_wait_for_gpu_vulkan_with_cb = [](uint64_t device, uint64_t fence, FenceCompletionCallback cb) { FrameBuffer::getFB()->asyncWaitForGpuVulkanWithCb(device, fence, cb); }, .async_wait_for_gpu_vulkan_qsri_with_cb = [](uint64_t image, FenceCompletionCallback cb) { FrameBuffer::getFB()->asyncWaitForGpuVulkanQsriWithCb(image, cb); }, .wait_for_gpu_vulkan_qsri = [](uint64_t image) { FrameBuffer::getFB()->waitForGpuVulkanQsri(image); }, .update_color_buffer_from_framework_format = [](uint32_t handle, int x, int y, int width, int height, uint32_t fwkFormat, uint32_t format, uint32_t type, void* pixels, void* pMetadata) { FrameBuffer::getFB()->updateColorBufferFromFrameworkFormat( handle, x, y, width, height, (FrameworkFormat)fwkFormat, format, type, pixels, pMetadata); }, .platform_import_resource = [](uint32_t handle, uint32_t info, void* resource) { return FrameBuffer::getFB()->platformImportResource(handle, info, resource); }, .platform_resource_info = [](uint32_t handle, int32_t* width, int32_t* height, int32_t* internal_format) { return FrameBuffer::getFB()->getColorBufferInfo(handle, width, height, internal_format); }, #if GFXSTREAM_ENABLE_HOST_GLES .platform_create_shared_egl_context = []() { return FrameBuffer::getFB()->platformCreateSharedEglContext(); }, .platform_destroy_shared_egl_context = [](void* context) { return FrameBuffer::getFB()->platformDestroySharedEglContext(context); }, #endif }; struct AndroidVirtioGpuOps* RendererImpl::getVirtioGpuOps() { return &sVirtioGpuOps; } void RendererImpl::snapshotOperationCallback(int op, int stage) { using namespace android::snapshot; switch (op) { case SNAPSHOTTER_OPERATION_LOAD: if (stage == SNAPSHOTTER_STAGE_START) { #ifdef SNAPSHOT_PROFILE android::base::System::Duration startTime = android::base::System::get()->getUnixTimeUs(); #endif mRenderWindow->setPaused(true); cleanupRenderThreads(); #ifdef SNAPSHOT_PROFILE printf("Previous session suspend time: %lld ms\n", (long long)(android::base::System::get() ->getUnixTimeUs() - startTime) / 1000); #endif } if (stage == SNAPSHOTTER_STAGE_END) { mRenderWindow->setPaused(false); } break; default: break; } } void RendererImpl::setVsyncHz(int vsyncHz) { if (mRenderWindow) { mRenderWindow->setVsyncHz(vsyncHz); } } void RendererImpl::setDisplayConfigs(int configId, int w, int h, int dpiX, int dpiY) { if (mRenderWindow) { mRenderWindow->setDisplayConfigs(configId, w, h, dpiX, dpiY); } } void RendererImpl::setDisplayActiveConfig(int configId) { if (mRenderWindow) { mRenderWindow->setDisplayActiveConfig(configId); } } const void* RendererImpl::getEglDispatch() { #if GFXSTREAM_ENABLE_HOST_GLES return FrameBuffer::getFB()->getEglDispatch(); #else return nullptr; #endif } const void* RendererImpl::getGles2Dispatch() { #if GFXSTREAM_ENABLE_HOST_GLES return FrameBuffer::getFB()->getGles2Dispatch(); #else return nullptr; #endif } } // namespace gfxstream