/* * Copyright (C) 2017 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 "PostWorker.h" #include "ColorBuffer.h" #include "DispatchTables.h" #include "FrameBuffer.h" #include "RenderThreadInfo.h" #include "OpenGLESDispatch/EGLDispatch.h" #include "OpenGLESDispatch/GLESv2Dispatch.h" #include "host-common/misc.h" #include #define POST_DEBUG 0 #if POST_DEBUG >= 1 #define DD(fmt, ...) \ fprintf(stderr, "%s:%d| " fmt, __func__, __LINE__, ##__VA_ARGS__) #else #define DD(fmt, ...) (void)0 #endif static void sDefaultRunOnUiThread(UiUpdateFunc f, void* data, bool wait) { (void)f; (void)data; (void)wait; } PostWorker::PostWorker( PostWorker::BindSubwinCallback&& cb, bool mainThreadPostingOnly, EGLContext eglContext, EGLSurface) : mFb(FrameBuffer::getFB()), mBindSubwin(cb), m_mainThreadPostingOnly(mainThreadPostingOnly), m_runOnUiThread(m_mainThreadPostingOnly ? emugl::get_emugl_window_operations().runOnUiThread : sDefaultRunOnUiThread), mContext(eglContext) {} void PostWorker::fillMultiDisplayPostStruct(ComposeLayer* l, hwc_rect_t displayArea, hwc_frect_t cropArea, hwc_transform_t transform) { l->composeMode = HWC2_COMPOSITION_DEVICE; l->blendMode = HWC2_BLEND_MODE_NONE; l->transform = transform; l->alpha = 1.0; l->displayFrame = displayArea; l->crop = cropArea; } void PostWorker::postImpl(ColorBuffer* cb) { // bind the subwindow eglSurface if (!m_mainThreadPostingOnly && !m_initialized) { m_initialized = mBindSubwin(); } float dpr = mFb->getDpr(); int windowWidth = mFb->windowWidth(); int windowHeight = mFb->windowHeight(); float px = mFb->getPx(); float py = mFb->getPy(); int zRot = mFb->getZrot(); hwc_transform_t rotation = (hwc_transform_t)0; cb->waitSync(); // Find the x and y values at the origin when "fully scrolled." // Multiply by 2 because the texture goes from -1 to 1, not 0 to 1. // Multiply the windowing coordinates by DPR because they ignore // DPR, but the viewport includes DPR. float fx = 2.f * (m_viewportWidth - windowWidth * dpr) / (float)m_viewportWidth; float fy = 2.f * (m_viewportHeight - windowHeight * dpr) / (float)m_viewportHeight; // finally, compute translation values float dx = px * fx; float dy = py * fy; if (emugl::get_emugl_multi_display_operations().isMultiDisplayEnabled()) { uint32_t combinedW, combinedH; emugl::get_emugl_multi_display_operations().getCombinedDisplaySize(&combinedW, &combinedH); mFb->getTextureDraw()->prepareForDrawLayer(); int32_t start_id = -1, x, y; uint32_t id, w, h, c; while(emugl::get_emugl_multi_display_operations().getNextMultiDisplay(start_id, &id, &x, &y, &w, &h, nullptr, nullptr, &c)) { if ((id != 0) && (w == 0 || h == 0 || c == 0)) { start_id = id; continue; } ColorBuffer* multiDisplayCb = id == 0 ? cb : mFb->findColorBuffer(c).get(); if (multiDisplayCb == nullptr) { start_id = id; continue; } ComposeLayer l; hwc_rect_t displayArea = { .left = (int)x, .top = (int)y, .right = (int)(x + w), .bottom = (int)(y + h) }; hwc_frect_t cropArea = { .left = 0.0, .top = (float)multiDisplayCb->getHeight(), .right = (float)multiDisplayCb->getWidth(), .bottom = 0.0 }; fillMultiDisplayPostStruct(&l, displayArea, cropArea, rotation); multiDisplayCb->postLayer(&l, combinedW, combinedH); start_id = id; } mFb->getTextureDraw()->cleanupForDrawLayer(); } else if (emugl::get_emugl_window_operations().isFolded()) { mFb->getTextureDraw()->prepareForDrawLayer(); ComposeLayer l; int x, y, w, h; emugl::get_emugl_window_operations().getFoldedArea(&x, &y, &w, &h); hwc_rect_t displayArea = { .left = 0, .top = 0, .right = windowWidth, .bottom = windowHeight }; hwc_frect_t cropArea = { .left = (float)x, .top = (float)(y + h), .right = (float)(x + w), .bottom = (float)y }; switch ((int)zRot/90) { case 1: rotation = HWC_TRANSFORM_ROT_270; break; case 2: rotation = HWC_TRANSFORM_ROT_180; break; case 3: rotation = HWC_TRANSFORM_ROT_90; break; default: ; } fillMultiDisplayPostStruct(&l, displayArea, cropArea, rotation); cb->postLayer(&l, m_viewportWidth/dpr, m_viewportHeight/dpr); mFb->getTextureDraw()->cleanupForDrawLayer(); } else { // render the color buffer to the window and apply the overlay GLuint tex = cb->scale(); cb->postWithOverlay(tex, zRot, dx, dy); } s_egl.eglSwapBuffers(mFb->getDisplay(), mFb->getWindowSurface()); } // Called whenever the subwindow needs a refresh (FrameBuffer::setupSubWindow). // This rebinds the subwindow context (to account for // when the refresh is a display change, for instance) // and resets the posting viewport. void PostWorker::viewportImpl(int width, int height) { // rebind the subwindow eglSurface unconditionally--- // this could be from a display change if (!m_mainThreadPostingOnly) { m_initialized = mBindSubwin(); } float dpr = mFb->getDpr(); m_viewportWidth = width * dpr; m_viewportHeight = height * dpr; s_gles2.glViewport(0, 0, m_viewportWidth, m_viewportHeight); } // Called when the subwindow refreshes, but there is no // last posted color buffer to show to the user. Instead of // displaying whatever happens to be in the back buffer, // clear() is useful for outputting consistent colors. void PostWorker::clearImpl() { #ifndef __linux__ s_gles2.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); s_egl.eglSwapBuffers(mFb->getDisplay(), mFb->getWindowSurface()); #endif } void PostWorker::composeImpl(ComposeDevice* p) { // bind the subwindow eglSurface if (!m_mainThreadPostingOnly && !m_initialized) { m_initialized = mBindSubwin(); } ComposeLayer* l = (ComposeLayer*)p->layer; GLint vport[4] = { 0, }; s_gles2.glGetIntegerv(GL_VIEWPORT, vport); s_gles2.glViewport(0, 0, mFb->getWidth(),mFb->getHeight()); if (!m_composeFbo) { s_gles2.glGenFramebuffers(1, &m_composeFbo); } s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, m_composeFbo); auto cbPtr = mFb->findColorBuffer(p->targetHandle); if (!cbPtr) { s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0); s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]); return; } s_gles2.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, cbPtr->getTexture(), 0); DD("worker compose %d layers\n", p->numLayers); mFb->getTextureDraw()->prepareForDrawLayer(); for (int i = 0; i < p->numLayers; i++, l++) { DD("\tcomposeMode %d color %d %d %d %d blendMode " "%d alpha %f transform %d %d %d %d %d " "%f %f %f %f\n", l->composeMode, l->color.r, l->color.g, l->color.b, l->color.a, l->blendMode, l->alpha, l->transform, l->displayFrame.left, l->displayFrame.top, l->displayFrame.right, l->displayFrame.bottom, l->crop.left, l->crop.top, l->crop.right, l->crop.bottom); composeLayer(l, mFb->getWidth(), mFb->getHeight()); } cbPtr->setSync(); s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0); s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]); mFb->getTextureDraw()->cleanupForDrawLayer(); } void PostWorker::composev2Impl(ComposeDevice_v2* p) { // bind the subwindow eglSurface if (!m_mainThreadPostingOnly && !m_initialized) { m_initialized = mBindSubwin(); } ComposeLayer* l = (ComposeLayer*)p->layer; GLint vport[4] = { 0, }; s_gles2.glGetIntegerv(GL_VIEWPORT, vport); uint32_t w, h; emugl::get_emugl_multi_display_operations().getMultiDisplay(p->displayId, nullptr, nullptr, &w, &h, nullptr, nullptr, nullptr); s_gles2.glViewport(0, 0, w, h); if (!m_composeFbo) { s_gles2.glGenFramebuffers(1, &m_composeFbo); } s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, m_composeFbo); auto cbPtr = mFb->findColorBuffer(p->targetHandle); if (!cbPtr) { s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0); s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]); return; } s_gles2.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, cbPtr->getTexture(), 0); DD("worker compose %d layers\n", p->numLayers); mFb->getTextureDraw()->prepareForDrawLayer(); for (int i = 0; i < p->numLayers; i++, l++) { DD("\tcomposeMode %d color %d %d %d %d blendMode " "%d alpha %f transform %d %d %d %d %d " "%f %f %f %f\n", l->composeMode, l->color.r, l->color.g, l->color.b, l->color.a, l->blendMode, l->alpha, l->transform, l->displayFrame.left, l->displayFrame.top, l->displayFrame.right, l->displayFrame.bottom, l->crop.left, l->crop.top, l->crop.right, l->crop.bottom); composeLayer(l, w, h); } cbPtr->setSync(); s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0); s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]); mFb->getTextureDraw()->cleanupForDrawLayer(); } void PostWorker::bind() { if (m_mainThreadPostingOnly) { if (mFb->getDisplay() != EGL_NO_DISPLAY) { EGLint res = s_egl.eglMakeCurrent(mFb->getDisplay(), mFb->getWindowSurface(), mFb->getWindowSurface(), mContext); if (!res) fprintf(stderr, "%s: error in binding: 0x%x\n", __func__, s_egl.eglGetError()); } else { fprintf(stderr, "%s: no display!\n", __func__); } } else { mBindSubwin(); } } void PostWorker::unbind() { if (mFb->getDisplay() != EGL_NO_DISPLAY) { s_egl.eglMakeCurrent(mFb->getDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } } void PostWorker::composeLayer(ComposeLayer* l, uint32_t w, uint32_t h) { if (l->composeMode == HWC2_COMPOSITION_DEVICE) { ColorBufferPtr cb = mFb->findColorBuffer(l->cbHandle); if (!cb) { // bad colorbuffer handle // ERR("%s: fail to find colorbuffer %d\n", __FUNCTION__, l->cbHandle); return; } cb->postLayer(l, w, h); } else { // no Colorbuffer associated with SOLID_COLOR mode mFb->getTextureDraw()->drawLayer(l, w, h, 1, 1, 0); } } void PostWorker::screenshot( ColorBuffer* cb, int width, int height, GLenum format, GLenum type, int rotation, void* pixels) { cb->readPixelsScaled( width, height, format, type, rotation, pixels); } PostWorker::~PostWorker() { if (mFb->getDisplay() != EGL_NO_DISPLAY) { s_egl.eglMakeCurrent(mFb->getDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } } void PostWorker::post(ColorBuffer* cb) { if (m_mainThreadPostingOnly) { PostArgs args = { .postCb = cb, }; m_toUiThread.send(args); m_runOnUiThread([](void* data) { PostWorker* p = (PostWorker*)data; PostArgs uiThreadArgs; p->m_toUiThread.receive(&uiThreadArgs); p->bind(); p->postImpl(uiThreadArgs.postCb); }, this, false /* no wait */); } else { postImpl(cb); } } void PostWorker::viewport(int width, int height) { if (m_mainThreadPostingOnly) { PostArgs args = { .width = width, .height = height, }; m_toUiThread.send(args); m_runOnUiThread([](void* data) { PostWorker* p = (PostWorker*)data; PostArgs uiThreadArgs; p->m_toUiThread.receive(&uiThreadArgs); p->bind(); p->viewportImpl(uiThreadArgs.width, uiThreadArgs.height); }, this, false /* no wait */); } else { viewportImpl(width, height); } } void PostWorker::compose(ComposeDevice* p, uint32_t bufferSize) { if (m_mainThreadPostingOnly) { PostArgs args; std::vector buffer(bufferSize, 0); memcpy(buffer.data(), p, bufferSize); args.composeBuffer = buffer; m_toUiThread.send(args); m_runOnUiThread([](void* data) { PostWorker* p = (PostWorker*)data; PostArgs uiThreadArgs; p->m_toUiThread.receive(&uiThreadArgs); p->bind(); p->composeImpl((ComposeDevice*)uiThreadArgs.composeBuffer.data()); }, this, false /* no wait */); } else { composeImpl(p); } } void PostWorker::compose(ComposeDevice_v2* p, uint32_t bufferSize) { if (m_mainThreadPostingOnly) { PostArgs args; std::vector buffer(bufferSize, 0); memcpy(buffer.data(), p, bufferSize); args.composeBuffer = buffer; m_toUiThread.send(args); m_runOnUiThread([](void* data) { PostWorker* p = (PostWorker*)data; PostArgs uiThreadArgs; p->m_toUiThread.receive(&uiThreadArgs); p->bind(); p->composev2Impl((ComposeDevice_v2*)uiThreadArgs.composeBuffer.data()); }, this, false /* no wait */); } else { composev2Impl(p); } } void PostWorker::clear() { if (m_mainThreadPostingOnly) { PostArgs args = { .postCb = 0, }; m_toUiThread.send(args); m_runOnUiThread([](void* data) { PostWorker* p = (PostWorker*)data; PostArgs uiThreadArgs; p->m_toUiThread.receive(&uiThreadArgs); p->bind(); p->clearImpl(); }, this, false /* no wait */); } else { clearImpl(); } }