| // 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 "RenderChannelImpl.h" |
| |
| #include "emugl/common/logging.h" |
| #include "ErrorLog.h" |
| #include "FrameBuffer.h" |
| |
| #include <algorithm> |
| #include <utility> |
| |
| #include <assert.h> |
| |
| namespace emugl { |
| |
| // 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; |
| |
| RendererImpl::RendererImpl() |
| : mCleanupThread([this]() { |
| while (const auto id = mCleanupProcessIds.receive()) { |
| FrameBuffer::getFB()->cleanupProcGLObjects(*id); |
| } |
| }) { |
| mCleanupThread.start(); |
| } |
| |
| RendererImpl::~RendererImpl() { |
| stop(); |
| mRenderWindow.reset(); |
| } |
| |
| bool RendererImpl::initialize(int width, int height, bool useSubWindow) { |
| if (mRenderWindow) { |
| return false; |
| } |
| |
| std::unique_ptr<RenderWindow> renderWindow( |
| new RenderWindow(width, height, kUseSubwindowThread, useSubWindow)); |
| 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"); |
| return true; |
| } |
| |
| void RendererImpl::stop() { |
| android::base::AutoLock lock(mThreadVectorLock); |
| mStopped = true; |
| auto threads = std::move(mThreads); |
| lock.unlock(); |
| |
| for (const auto& t : threads) { |
| if (const auto channel = t.second.lock()) { |
| channel->forceStop(); |
| } |
| } |
| // We're stopping the renderer, so there's no need to clean up resources |
| // of some pending processes: we'll destroy everything soon. |
| mCleanupProcessIds.stop(); |
| |
| for (const auto& t : threads) { |
| t.first->wait(); |
| } |
| mCleanupThread.wait(); |
| } |
| |
| RenderChannelPtr RendererImpl::createRenderChannel() { |
| const auto channel = |
| std::make_shared<RenderChannelImpl>(shared_from_this()); |
| |
| std::unique_ptr<RenderThread> rt(RenderThread::create( |
| shared_from_this(), channel)); |
| if (!rt) { |
| fprintf(stderr, "Failed to create RenderThread\n"); |
| return nullptr; |
| } |
| |
| { |
| android::base::AutoLock lock(mThreadVectorLock); |
| |
| if (mStopped) return nullptr; |
| |
| if (!rt->start()) { |
| fprintf(stderr, "Failed to start RenderThread\n"); |
| return nullptr; |
| } |
| |
| // clean up the threads that are no longer running |
| mThreads.erase(std::remove_if(mThreads.begin(), mThreads.end(), |
| [](const ThreadWithChannel& t) { |
| return t.second.expired() || |
| t.first->isFinished(); |
| }), |
| mThreads.end()); |
| |
| DBG("Started new RenderThread (total %" PRIu64 ") @%p\n", |
| static_cast<uint64_t>(mThreads.size() + 1), rt.get()); |
| |
| mThreads.emplace_back(std::move(rt), channel); |
| } |
| |
| return channel; |
| } |
| |
| 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) { |
| assert(mRenderWindow); |
| mRenderWindow->setPostCallback(onPost, context); |
| } |
| |
| bool RendererImpl::showOpenGLSubwindow(FBNativeWindowType window, |
| int wx, |
| int wy, |
| int ww, |
| int wh, |
| int fbw, |
| int fbh, |
| float dpr, |
| float zRot) { |
| assert(mRenderWindow); |
| return mRenderWindow->setupSubWindow(window, wx, wy, ww, wh, fbw, fbh, dpr, |
| zRot); |
| } |
| |
| 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(); |
| } |
| |
| void RendererImpl::cleanupProcGLObjects(uint64_t puid) { |
| mCleanupProcessIds.send(puid); |
| } |
| |
| } // namespace emugl |