You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
630 lines
20 KiB
630 lines
20 KiB
// 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 "RenderThread.h"
|
|
|
|
#include "base/System.h"
|
|
#include "snapshot/common.h"
|
|
#include "host-common/logging.h"
|
|
|
|
#include "ErrorLog.h"
|
|
#include "FenceSync.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;
|
|
|
|
// 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()
|
|
: mCleanupThread([this]() {
|
|
while (const auto id = mCleanupProcessIds.receive()) {
|
|
FrameBuffer::getFB()->cleanupProcGLObjects(*id);
|
|
}
|
|
}) {
|
|
mCleanupThread.start();
|
|
}
|
|
|
|
~ProcessCleanupThread() {
|
|
mCleanupProcessIds.stop();
|
|
mCleanupThread.wait();
|
|
}
|
|
|
|
void cleanup(uint64_t processId) {
|
|
mCleanupProcessIds.send(processId);
|
|
}
|
|
|
|
void stop() {
|
|
mCleanupProcessIds.stop();
|
|
}
|
|
|
|
void waitForCleanup() {
|
|
mCleanupProcessIds.waitForEmpty();
|
|
}
|
|
|
|
private:
|
|
DISALLOW_COPY_AND_ASSIGN(ProcessCleanupThread);
|
|
|
|
android::base::MessageChannel<uint64_t, 64> mCleanupProcessIds;
|
|
android::base::FunctorThread mCleanupThread;
|
|
};
|
|
|
|
RendererImpl::RendererImpl() {
|
|
mCleanupThread.reset(new ProcessCleanupThread());
|
|
}
|
|
|
|
RendererImpl::~RendererImpl() {
|
|
stop(true);
|
|
mRenderWindow.reset();
|
|
}
|
|
|
|
bool RendererImpl::initialize(int width, int height, bool useSubWindow, bool egl2egl) {
|
|
if (android::base::getEnvironmentVariable("ANDROID_EMUGL_VERBOSE") == "1") {
|
|
// base_enable_verbose_logs();
|
|
}
|
|
|
|
if (mRenderWindow) {
|
|
return false;
|
|
}
|
|
|
|
std::unique_ptr<RenderWindow> renderWindow(new RenderWindow(
|
|
width, height, 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();
|
|
}
|
|
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) {
|
|
const auto channel = std::make_shared<RenderChannelImpl>(loadStream);
|
|
{
|
|
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<RenderChannelImpl>& 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();
|
|
}
|
|
|
|
DBG("Started new RenderThread (total %" PRIu64 ") @%p\n",
|
|
static_cast<uint64_t>(mChannels.size()), channel->renderThread());
|
|
}
|
|
|
|
return channel;
|
|
}
|
|
|
|
void* RendererImpl::addressSpaceGraphicsConsumerCreate(
|
|
struct asg_context context,
|
|
android::base::Stream* loadStream,
|
|
android::emulation::asg::ConsumerCallbacks callbacks) {
|
|
auto thread = new RenderThread(context, loadStream, callbacks);
|
|
thread->start();
|
|
return (void*)thread;
|
|
}
|
|
|
|
void RendererImpl::addressSpaceGraphicsConsumerDestroy(void* consumer) {
|
|
RenderThread* thread = (RenderThread*)consumer;
|
|
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();
|
|
}
|
|
|
|
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();
|
|
}
|
|
lock.unlock();
|
|
waitForProcessCleanup();
|
|
}
|
|
|
|
void RendererImpl::resumeAll() {
|
|
{
|
|
android::base::AutoLock lock(mChannelsLock);
|
|
if (mStopped) {
|
|
return;
|
|
}
|
|
for (const auto& c : mChannels) {
|
|
c->renderThread()->resume();
|
|
}
|
|
|
|
for (const auto t: mAdditionalPostLoadRenderThreads) {
|
|
t->resume();
|
|
}
|
|
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);
|
|
|
|
FenceSync::onSave(stream);
|
|
}
|
|
|
|
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);
|
|
FenceSync::onLoad(stream);
|
|
|
|
return res;
|
|
}
|
|
|
|
void RendererImpl::fillGLESUsages(android_studio::EmulatorGLESUsages* usages) {
|
|
auto fb = FrameBuffer::getFB();
|
|
if (fb) fb->fillGLESUsages(usages);
|
|
}
|
|
|
|
void RendererImpl::getScreenshot(unsigned int nChannels, unsigned int* width,
|
|
unsigned int* height, std::vector<unsigned char>& pixels, int displayId,
|
|
int desiredWidth, int desiredHeight, int desiredRotation) {
|
|
auto fb = FrameBuffer::getFB();
|
|
if (fb) fb->getScreenshot(nChannels, width, height, pixels, displayId,
|
|
desiredWidth, desiredHeight, desiredRotation);
|
|
}
|
|
|
|
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::cleanupProcGLObjects(uint64_t puid) {
|
|
mCleanupThread->cleanup(puid);
|
|
}
|
|
|
|
static struct AndroidVirtioGpuOps sVirtioGpuOps = {
|
|
.create_color_buffer_with_handle =
|
|
[](uint32_t width,
|
|
uint32_t height,
|
|
uint32_t format,
|
|
uint32_t fwkFormat,
|
|
uint32_t handle) {
|
|
FrameBuffer::getFB()->createColorBufferWithHandle(
|
|
width, height, (GLenum)format,
|
|
(FrameworkFormat)fwkFormat, handle);
|
|
},
|
|
.open_color_buffer =
|
|
[](uint32_t handle) {
|
|
FrameBuffer::getFB()->openColorBuffer(handle);
|
|
},
|
|
.close_color_buffer =
|
|
[](uint32_t handle) {
|
|
FrameBuffer::getFB()->closeColorBuffer(handle);
|
|
},
|
|
.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_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); },
|
|
.repost = []() { FrameBuffer::getFB()->repost(); },
|
|
.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) {
|
|
FrameBuffer::getFB()->swapTexturesAndUpdateColorBuffer(
|
|
colorbufferhandle, x, y, width, height, format,
|
|
type, texture_type, textures);
|
|
},
|
|
.get_last_posted_color_buffer = []() {
|
|
return FrameBuffer::getFB()->getLastPostedColorBuffer();
|
|
},
|
|
.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);
|
|
},
|
|
.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(true);
|
|
},
|
|
};
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
} // namespace emugl
|