aboutsummaryrefslogtreecommitdiff
path: root/src/gfxstream/host/RenderWindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gfxstream/host/RenderWindow.cpp')
-rw-r--r--src/gfxstream/host/RenderWindow.cpp698
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