diff options
Diffstat (limited to 'src/gfxstream/host/RenderWindow.cpp')
-rw-r--r-- | src/gfxstream/host/RenderWindow.cpp | 698 |
1 files changed, 698 insertions, 0 deletions
diff --git a/src/gfxstream/host/RenderWindow.cpp b/src/gfxstream/host/RenderWindow.cpp new file mode 100644 index 00000000000..ace61148123 --- /dev/null +++ b/src/gfxstream/host/RenderWindow.cpp @@ -0,0 +1,698 @@ +// Copyright 2014-2015 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 "RenderWindow.h" + +#include "aemu/base/threads/Thread.h" +#include "aemu/base/synchronization/MessageChannel.h" +#include "host-common/logging.h" +#include "FrameBuffer.h" +#include "RendererImpl.h" + +#include <stdarg.h> +#include <stdio.h> +#ifndef _WIN32 +#include <signal.h> +#include <pthread.h> +#endif + +namespace gfxstream { + +#define DEBUG 0 + +#if DEBUG +# define D(...) my_debug(__PRETTY_FUNCTION__, __LINE__, __VA_ARGS__) +#else +# define D(...) ((void)0) +#endif + +namespace { + +#if DEBUG +void my_debug(const char* function, int line, const char* format, ...) { + static ::android::base::Lock mutex; + va_list args; + va_start(args, format); + mutex.lock(); + fprintf(stderr, "%s:%d:", function, line); + vfprintf(stderr, format, args); + mutex.unlock(); + va_end(args); +} +#endif + +// List of possible commands to send to the render window thread from +// the main one. +enum Command { + CMD_INITIALIZE, + CMD_SET_POST_CALLBACK, + CMD_SETUP_SUBWINDOW, + CMD_REMOVE_SUBWINDOW, + CMD_SET_ROTATION, + CMD_SET_TRANSLATION, + CMD_REPAINT, + CMD_HAS_GUEST_POSTED_A_FRAME, + CMD_RESET_GUEST_POSTED_A_FRAME, + CMD_SET_VSYNC_HZ, + CMD_SET_DISPLAY_CONFIGS, + CMD_SET_DISPLAY_ACTIVE_CONFIG, + CMD_FINALIZE, +}; + +} // namespace + +// A single message sent from the main thread to the render window thread. +// |cmd| determines which fields are valid to read. +struct RenderWindowMessage { + Command cmd; + union { + // CMD_INITIALIZE + struct { + int width; + int height; + bool useSubWindow; + bool egl2egl; + } init; + + // CMD_SET_POST_CALLBACK + struct { + Renderer::OnPostCallback on_post; + void* on_post_context; + uint32_t on_post_displayId; + bool use_bgra_readback; + } set_post_callback; + + // CMD_SETUP_SUBWINDOW + struct { + FBNativeWindowType parent; + int wx; + int wy; + int ww; + int wh; + int fbw; + int fbh; + float dpr; + float rotation; + bool deleteExisting; + bool hideWindow; + } subwindow; + + // CMD_SET_TRANSLATION; + struct { + float px; + float py; + } trans; + + // CMD_SET_ROTATION + float rotation; + + // CMD_SET_VSYNC_HZ + int vsyncHz; + + // CMD_SET_COMPOSE_DIMENSIONS + struct { + int configId; + int width; + int height; + int dpiX; + int dpiY; + } displayConfigs; + + int displayActiveConfig; + + // result of operations. + bool result; + }; + + // Process the current message, and updates its |result| field. + // Returns true on success, or false on failure. + bool process() const { + const RenderWindowMessage& msg = *this; + FrameBuffer* fb; + bool result = false; + switch (msg.cmd) { + case CMD_INITIALIZE: + GL_LOG("RenderWindow: CMD_INITIALIZE w=%d h=%d", + msg.init.width, msg.init.height); + result = FrameBuffer::initialize(msg.init.width, + msg.init.height, + msg.init.useSubWindow, + msg.init.egl2egl); + break; + + case CMD_FINALIZE: + GL_LOG("CMD_FINALIZE"); + D("CMD_FINALIZE\n"); + // this command may be issued even when frame buffer is not + // yet created (e.g. if CMD_INITIALIZE failed), + // so make sure we check if it is there before finalizing + FrameBuffer::finalize(); + result = true; + break; + + case CMD_SET_POST_CALLBACK: + GL_LOG("CMD_SET_POST_CALLBACK"); + D("CMD_SET_POST_CALLBACK\n"); + fb = FrameBuffer::getFB(); + if (fb) { + fb->setPostCallback(msg.set_post_callback.on_post, + msg.set_post_callback.on_post_context, + msg.set_post_callback.on_post_displayId, + msg.set_post_callback.use_bgra_readback); + result = true; + } + break; + + case CMD_SETUP_SUBWINDOW: + GL_LOG("CMD_SETUP_SUBWINDOW: parent=%p wx=%d wy=%d ww=%d wh=%d fbw=%d fbh=%d dpr=%f rotation=%f", + (void*)(intptr_t)msg.subwindow.parent, + msg.subwindow.wx, + msg.subwindow.wy, + msg.subwindow.ww, + msg.subwindow.wh, + msg.subwindow.fbw, + msg.subwindow.fbh, + msg.subwindow.dpr, + msg.subwindow.rotation); + D("CMD_SETUP_SUBWINDOW: parent=%p wx=%d wy=%d ww=%d wh=%d fbw=%d fbh=%d dpr=%f rotation=%f\n", + (void*)(intptr_t)msg.subwindow.parent, + msg.subwindow.wx, + msg.subwindow.wy, + msg.subwindow.ww, + msg.subwindow.wh, + msg.subwindow.fbw, + msg.subwindow.fbh, + msg.subwindow.dpr, + msg.subwindow.rotation); + fb = FrameBuffer::getFB(); + if (fb) { + result = FrameBuffer::getFB()->setupSubWindow( + msg.subwindow.parent, msg.subwindow.wx, msg.subwindow.wy, msg.subwindow.ww, + msg.subwindow.wh, msg.subwindow.fbw, msg.subwindow.fbh, msg.subwindow.dpr, + msg.subwindow.rotation, msg.subwindow.deleteExisting, + msg.subwindow.hideWindow); + } + break; + + case CMD_REMOVE_SUBWINDOW: + GL_LOG("CMD_REMOVE_SUBWINDOW"); + D("CMD_REMOVE_SUBWINDOW\n"); + fb = FrameBuffer::getFB(); + if (fb) { + result = fb->removeSubWindow(); + } + break; + + case CMD_SET_ROTATION: + GL_LOG("CMD_SET_ROTATION rotation=%f", msg.rotation); + D("CMD_SET_ROTATION rotation=%f\n", msg.rotation); + fb = FrameBuffer::getFB(); + if (fb) { + fb->setDisplayRotation(msg.rotation); + result = true; + } + break; + + case CMD_SET_TRANSLATION: + GL_LOG("CMD_SET_TRANSLATION translation=%f,%f", msg.trans.px, msg.trans.py); + D("CMD_SET_TRANSLATION translation=%f,%f\n", msg.trans.px, msg.trans.py); + fb = FrameBuffer::getFB(); + if (fb) { + fb->setDisplayTranslation(msg.trans.px, msg.trans.py); + result = true; + } + break; + + case CMD_REPAINT: + GL_LOG("CMD_REPAINT"); + D("CMD_REPAINT\n"); + fb = FrameBuffer::getFB(); + if (fb) { + fb->repost(); + result = true; + } else { + GL_LOG("CMD_REPAINT: no repost, no FrameBuffer"); + } + break; + + case CMD_HAS_GUEST_POSTED_A_FRAME: + GL_LOG("CMD_HAS_GUEST_POSTED_A_FRAME"); + D("CMD_HAS_GUEST_POSTED_A_FRAME\n"); + fb = FrameBuffer::getFB(); + if (fb) { + result = fb->hasGuestPostedAFrame(); + } else { + GL_LOG("CMD_HAS_GUEST_POSTED_A_FRAME: no FrameBuffer"); + } + break; + + case CMD_RESET_GUEST_POSTED_A_FRAME: + GL_LOG("CMD_RESET_GUEST_POSTED_A_FRAME"); + D("CMD_RESET_GUEST_POSTED_A_FRAME\n"); + fb = FrameBuffer::getFB(); + if (fb) { + fb->resetGuestPostedAFrame(); + result = true; + } else { + GL_LOG("CMD_RESET_GUEST_POSTED_A_FRAME: no FrameBuffer"); + } + break; + + case CMD_SET_VSYNC_HZ: + GL_LOG("CMD_SET_VSYNC_HZ"); + D("CMD_SET_VSYNC_HZ\n"); + fb = FrameBuffer::getFB(); + if (fb) { + fb->setVsyncHz(msg.vsyncHz); + result = true; + } else { + GL_LOG("CMD_RESET_GUEST_POSTED_A_FRAME: no FrameBuffer"); + } + break; + + case CMD_SET_DISPLAY_CONFIGS: + GL_LOG("CMD_SET_DISPLAY_CONFIGS"); + D("CMD_SET_DISPLAY_CONFIGS"); + fb = FrameBuffer::getFB(); + if (fb) { + fb->setDisplayConfigs(msg.displayConfigs.configId, + msg.displayConfigs.width, + msg.displayConfigs.height, + msg.displayConfigs.dpiX, + msg.displayConfigs.dpiY); + result = true; + } else { + GL_LOG("CMD_SET_DISPLAY_CONFIGS: no FrameBuffer"); + } + break; + + case CMD_SET_DISPLAY_ACTIVE_CONFIG: + GL_LOG("CMD_SET_DISPLAY_ACTIVE_CONFIG"); + D("CMD_SET_DISPLAY_ACTIVE_CONFIG"); + fb = FrameBuffer::getFB(); + if (fb) { + fb->setDisplayActiveConfig(msg.displayActiveConfig); + result = true; + } else { + GL_LOG("CMD_SET_DISPLAY_ACTIVE_CONFIG: no FrameBuffer"); + } + break; + + default: + ; + } + return result; + } +}; + +// Simple synchronization structure used to exchange data between the +// main and render window threads. Usage is the following: +// +// The main thread does the following in a loop: +// +// canWriteCmd.wait() +// updates |message| by writing a new |cmd| value and appropriate +// parameters. +// canReadCmd.signal() +// canReadResult.wait() +// reads |message.result| +// canWriteResult.signal() +// +// The render window thread will do the following: +// +// canReadCmd.wait() +// reads |message.cmd| and acts upon it. +// canWriteResult.wait() +// writes |message.result| +// canReadResult.signal() +// canWriteCmd.signal() +// +class RenderWindowChannel { +public: + RenderWindowChannel() : mIn(), mOut() {} + ~RenderWindowChannel() {} + + // Send a message from the main thread. + // Note that the content of |msg| is copied into the channel. + // Returns with the command's result (true or false). + bool sendMessageAndGetResult(const RenderWindowMessage& msg) { + D("msg.cmd=%d\n", msg.cmd); + mIn.send(msg); + D("waiting for result\n"); + bool result = false; + mOut.receive(&result); + D("result=%s\n", result ? "success" : "failure"); + return result; + } + + // Receive a message from the render window thread. + // On exit, |*msg| gets a copy of the message. The caller + // must always call sendResult() after processing the message. + void receiveMessage(RenderWindowMessage* msg) { + D("entering\n"); + mIn.receive(msg); + D("message cmd=%d\n", msg->cmd); + } + + // Send result from the render window thread to the main one. + // Must always be called after receiveMessage(). + void sendResult(bool result) { + D("waiting to send result (%s)\n", result ? "success" : "failure"); + mOut.send(result); + D("result sent\n"); + } + +private: + android::base::MessageChannel<RenderWindowMessage, 16U> mIn; + android::base::MessageChannel<bool, 16U> mOut; +}; + +namespace { + +// This class implements the window render thread. +// Its purpose is to listen for commands from the main thread in a loop, +// process them, then return a boolean result for each one of them. +// +// The thread ends with a CMD_FINALIZE. +// +class RenderWindowThread : public android::base::Thread { +public: + RenderWindowThread(RenderWindowChannel* channel) : mChannel(channel) {} + + virtual intptr_t main() { + D("Entering render window thread thread\n"); +#ifndef _WIN32 + sigset_t set; + sigfillset(&set); + pthread_sigmask(SIG_SETMASK, &set, NULL); +#endif + bool running = true; + while (running) { + RenderWindowMessage msg = {}; + + D("Waiting for message from main thread\n"); + mChannel->receiveMessage(&msg); + + bool result = msg.process(); + if (msg.cmd == CMD_FINALIZE) { + running = false; + } + + D("Sending result (%s) to main thread\n", result ? "success" : "failure"); + mChannel->sendResult(result); + } + D("Exiting thread\n"); + return 0; + } + +private: + RenderWindowChannel* mChannel; +}; + +} // namespace + +RenderWindow::RenderWindow(int width, + int height, + bool use_thread, + bool use_sub_window, + bool egl2egl) + : mRepostThread([this] { + while (auto cmd = mRepostCommands.receive()) { + if (*cmd == RepostCommand::Sync) { + continue; + } else if (*cmd == RepostCommand::Repost && + !mPaused) { + GL_LOG("Reposting thread dequeueing a CMD_REPAINT"); + RenderWindowMessage msg = {CMD_REPAINT}; + (void)msg.process(); + } + } + }) { + if (use_thread) { + mChannel = new RenderWindowChannel(); + mThread = new RenderWindowThread(mChannel); + mThread->start(); + } else { + mRepostThread.start(); + } + RenderWindowMessage msg = {}; + msg.cmd = CMD_INITIALIZE; + msg.init.width = width; + msg.init.height = height; + msg.init.useSubWindow = use_sub_window; + msg.init.egl2egl = egl2egl; + mValid = processMessage(msg); +} + +RenderWindow::~RenderWindow() { + D("Entering\n"); + removeSubWindow(); + mRepostCommands.stop(); + D("Sending CMD_FINALIZE\n"); + RenderWindowMessage msg = {}; + msg.cmd = CMD_FINALIZE; + (void) processMessage(msg); + + if (useThread()) { + mThread->wait(NULL); + delete mThread; + delete mChannel; + } else { + mRepostThread.wait(); + } +} + +void RenderWindow::setPaused(bool paused) { + // If pausing, flush commands + if (!mPaused && paused) { + if (useThread()) { + fprintf(stderr, + "WARNING: flushMessages unsupported for RenderWindowThread. " + "Generic snapshot load might segfault.\n"); + } else { + mRepostCommands.waitForEmpty(); + } + } + + mPaused = paused; +} + +bool RenderWindow::getHardwareStrings(const char** vendor, + const char** renderer, + const char** version) { + D("Entering\n"); + // TODO(digit): Move this to render window thread. + FrameBuffer* fb = FrameBuffer::getFB(); + if (!fb) { + D("No framebuffer!\n"); + return false; + } + +#if GFXSTREAM_ENABLE_HOST_GLES + fb->getGLStrings(vendor, renderer, version); + D("Exiting vendor=[%s] renderer=[%s] version=[%s]\n", + *vendor, *renderer, *version); + + return true; +#else + return false; +#endif +} + +void RenderWindow::setPostCallback(Renderer::OnPostCallback onPost, void* onPostContext, + uint32_t displayId, bool useBgraReadback) { + D("Entering\n"); + RenderWindowMessage msg = {}; + msg.cmd = CMD_SET_POST_CALLBACK; + msg.set_post_callback.on_post = onPost; + msg.set_post_callback.on_post_context = onPostContext; + msg.set_post_callback.on_post_displayId = displayId; + msg.set_post_callback.use_bgra_readback = useBgraReadback; + (void) processMessage(msg); + D("Exiting\n"); +} + +bool RenderWindow::asyncReadbackSupported() { + D("Entering\n"); + return FrameBuffer::getFB()->asyncReadbackSupported(); +} + +Renderer::ReadPixelsCallback RenderWindow::getReadPixelsCallback() { + D("Entering\n"); + return FrameBuffer::getFB()->getReadPixelsCallback(); +} + +void RenderWindow::addListener(Renderer::FrameBufferChangeEventListener* listener) { + FrameBuffer::getFB()->addListener(listener); +} + +void RenderWindow::removeListener(Renderer::FrameBufferChangeEventListener* listener) { + FrameBuffer::getFB()->removeListener(listener); +} + +Renderer::FlushReadPixelPipeline RenderWindow::getFlushReadPixelPipeline() { + return FrameBuffer::getFB()->getFlushReadPixelPipeline(); +} +bool RenderWindow::setupSubWindow(FBNativeWindowType window, + int wx, + int wy, + int ww, + int wh, + int fbw, + int fbh, + float dpr, + float zRot, + bool deleteExisting, + bool hideWindow) { + D("Entering mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false"); + + RenderWindowMessage msg = {}; + msg.cmd = CMD_SETUP_SUBWINDOW; + msg.subwindow.parent = window; + msg.subwindow.wx = wx; + msg.subwindow.wy = wy; + msg.subwindow.ww = ww; + msg.subwindow.wh = wh; + msg.subwindow.fbw = fbw; + msg.subwindow.fbh = fbh; + msg.subwindow.dpr = dpr; + msg.subwindow.rotation = zRot; + msg.subwindow.deleteExisting = deleteExisting; + msg.subwindow.hideWindow = hideWindow; + mHasSubWindow = processMessage(msg); + + D("Exiting mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false"); + return mHasSubWindow; +} + +bool RenderWindow::removeSubWindow() { + D("Entering mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false"); + if (!mHasSubWindow) { + return false; + } + mHasSubWindow = false; + if (!useThread()) { + mRepostCommands.send(RepostCommand::Sync); + mRepostCommands.waitForEmpty(); + } + + RenderWindowMessage msg = {}; + msg.cmd = CMD_REMOVE_SUBWINDOW; + bool result = processMessage(msg); + D("Exiting result=%s\n", result ? "success" : "failure"); + return result; +} + +void RenderWindow::setRotation(float zRot) { + D("Entering rotation=%f\n", zRot); + RenderWindowMessage msg = {}; + msg.cmd = CMD_SET_ROTATION; + msg.rotation = zRot; + (void) processMessage(msg); + D("Exiting\n"); +} + +void RenderWindow::setTranslation(float px, float py) { + D("Entering translation=%f,%f\n", px, py); + RenderWindowMessage msg = {}; + msg.cmd = CMD_SET_TRANSLATION; + msg.trans.px = px; + msg.trans.py = py; + (void) processMessage(msg); + D("Exiting\n"); +} + +void RenderWindow::setScreenMask(int width, int height, const unsigned char* rgbaData) { + if (FrameBuffer* fb = FrameBuffer::getFB()) { +#if GFXSTREAM_ENABLE_HOST_GLES + if (fb->hasEmulationGl()) { + fb->getTextureDraw()->setScreenMask(width, height, rgbaData); + } +#endif + } +} + +void RenderWindow::repaint() { + D("Entering\n"); + RenderWindowMessage msg = {}; + msg.cmd = CMD_REPAINT; + (void) processMessage(msg); + D("Exiting\n"); +} + +bool RenderWindow::hasGuestPostedAFrame() { + D("Entering\n"); + RenderWindowMessage msg = {}; + msg.cmd = CMD_HAS_GUEST_POSTED_A_FRAME; + bool res = processMessage(msg); + D("Exiting\n"); + return res; +} + +void RenderWindow::resetGuestPostedAFrame() { + D("Entering\n"); + RenderWindowMessage msg = {}; + msg.cmd = CMD_RESET_GUEST_POSTED_A_FRAME; + (void) processMessage(msg); + D("Exiting\n"); +} + +void RenderWindow::setVsyncHz(int vsyncHz) { + D("Entering\n"); + RenderWindowMessage msg = {}; + msg.cmd = CMD_SET_VSYNC_HZ; + msg.vsyncHz = vsyncHz; + (void) processMessage(msg); + D("Exiting\n"); +} + +void RenderWindow::setDisplayConfigs(int configId, int w, int h, + int dpiX, int dpiY) { + D("Entering\n"); + RenderWindowMessage msg = {}; + msg.cmd = CMD_SET_DISPLAY_CONFIGS; + msg.displayConfigs.configId = configId; + msg.displayConfigs.width = w; + msg.displayConfigs.height= h; + msg.displayConfigs.dpiX= dpiX; + msg.displayConfigs.dpiY = dpiY; + (void) processMessage(msg); + D("Exiting\n"); +} + +void RenderWindow::setDisplayActiveConfig(int configId) { + D("Entering\n"); + RenderWindowMessage msg = {}; + msg.cmd = CMD_SET_DISPLAY_ACTIVE_CONFIG; + msg.displayActiveConfig = configId; + (void) processMessage(msg); + D("Exiting\n"); +} + +bool RenderWindow::processMessage(const RenderWindowMessage& msg) { + if (useThread()) { + if (msg.cmd == CMD_REPAINT) { + GL_LOG("Sending CMD_REPAINT to render window channel"); + } + return mChannel->sendMessageAndGetResult(msg); + } else if (msg.cmd == CMD_REPAINT) { + GL_LOG("Sending CMD_REPAINT to reposting thread"); + mRepostCommands.send(RepostCommand::Repost); + return true; + } else { + return msg.process(); + } +} + +} // namespace gfxstream |