summaryrefslogtreecommitdiff
path: root/libs/hwui/renderthread/OpenGLPipeline.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui/renderthread/OpenGLPipeline.cpp')
-rw-r--r--libs/hwui/renderthread/OpenGLPipeline.cpp272
1 files changed, 272 insertions, 0 deletions
diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp
new file mode 100644
index 000000000000..e1ae58532a02
--- /dev/null
+++ b/libs/hwui/renderthread/OpenGLPipeline.cpp
@@ -0,0 +1,272 @@
+/*
+ * 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 "OpenGLPipeline.h"
+
+#include "DeferredLayerUpdater.h"
+#include "EglManager.h"
+#include "Frame.h"
+#include "GlLayer.h"
+#include "ProfileRenderer.h"
+#include "renderstate/RenderState.h"
+#include "OpenGLReadback.h"
+
+#include <cutils/properties.h>
+#include <strings.h>
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+OpenGLPipeline::OpenGLPipeline(RenderThread& thread)
+ : mEglManager(thread.eglManager())
+ , mRenderThread(thread) {
+}
+
+MakeCurrentResult OpenGLPipeline::makeCurrent() {
+ // TODO: Figure out why this workaround is needed, see b/13913604
+ // In the meantime this matches the behavior of GLRenderer, so it is not a regression
+ EGLint error = 0;
+ bool haveNewSurface = mEglManager.makeCurrent(mEglSurface, &error);
+
+ Caches::getInstance().textureCache.resetMarkInUse(this);
+ if (!haveNewSurface) {
+ return MakeCurrentResult::AlreadyCurrent;
+ }
+ return error ? MakeCurrentResult::Failed : MakeCurrentResult::Succeeded;
+}
+
+Frame OpenGLPipeline::getFrame() {
+ LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
+ "drawRenderNode called on a context with no surface!");
+ return mEglManager.beginFrame(mEglSurface);
+}
+
+bool OpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
+ const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo,
+ const std::vector< sp<RenderNode> >& renderNodes,
+ FrameInfoVisualizer* profiler) {
+
+ mEglManager.damageFrame(frame, dirty);
+
+ bool drew = false;
+
+
+ auto& caches = Caches::getInstance();
+ FrameBuilder frameBuilder(dirty, frame.width(), frame.height(), lightGeometry, caches);
+
+ frameBuilder.deferLayers(*layerUpdateQueue);
+ layerUpdateQueue->clear();
+
+ frameBuilder.deferRenderNodeScene(renderNodes, contentDrawBounds);
+
+ BakedOpRenderer renderer(caches, mRenderThread.renderState(),
+ opaque, lightInfo);
+ frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
+ ProfileRenderer profileRenderer(renderer);
+ profiler->draw(profileRenderer);
+ drew = renderer.didDraw();
+
+ // post frame cleanup
+ caches.clearGarbage();
+ caches.pathCache.trim();
+ caches.tessellationCache.trim();
+
+#if DEBUG_MEMORY_USAGE
+ caches.dumpMemoryUsage();
+#else
+ if (CC_UNLIKELY(Properties::debugLevel & kDebugMemory)) {
+ caches.dumpMemoryUsage();
+ }
+#endif
+
+ return drew;
+}
+
+bool OpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
+ FrameInfo* currentFrameInfo, bool* requireSwap) {
+
+ GL_CHECKPOINT(LOW);
+
+ // Even if we decided to cancel the frame, from the perspective of jank
+ // metrics the frame was swapped at this point
+ currentFrameInfo->markSwapBuffers();
+
+ *requireSwap = drew || mEglManager.damageRequiresSwap();
+
+ if (*requireSwap && (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty)))) {
+ return false;
+ }
+
+ return *requireSwap;
+}
+
+bool OpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
+ ATRACE_CALL();
+ // acquire most recent buffer for drawing
+ layer->updateTexImage();
+ layer->apply();
+ return OpenGLReadbackImpl::copyLayerInto(mRenderThread,
+ static_cast<GlLayer&>(*layer->backingLayer()), bitmap);
+}
+
+static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
+ SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) {
+ GlLayer* layer = new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha,
+ mode, blend);
+ Caches::getInstance().textureState().activateTexture(0);
+ layer->generateTexture();
+ return layer;
+}
+
+DeferredLayerUpdater* OpenGLPipeline::createTextureLayer() {
+ mEglManager.initialize();
+ return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::OpenGL);
+}
+
+void OpenGLPipeline::onStop() {
+ if (mEglManager.isCurrent(mEglSurface)) {
+ mEglManager.makeCurrent(EGL_NO_SURFACE);
+ }
+}
+
+bool OpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
+
+ if (mEglSurface != EGL_NO_SURFACE) {
+ mEglManager.destroySurface(mEglSurface);
+ mEglSurface = EGL_NO_SURFACE;
+ }
+
+ if (surface) {
+ mEglSurface = mEglManager.createSurface(surface);
+ }
+
+ if (mEglSurface != EGL_NO_SURFACE) {
+ const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
+ mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
+ return true;
+ }
+
+ return false;
+}
+
+bool OpenGLPipeline::isSurfaceReady() {
+ return CC_UNLIKELY(mEglSurface != EGL_NO_SURFACE);
+}
+
+bool OpenGLPipeline::isContextReady() {
+ return CC_LIKELY(mEglManager.hasEglContext());
+}
+
+void OpenGLPipeline::onDestroyHardwareResources() {
+ Caches& caches = Caches::getInstance();
+ // Make sure to release all the textures we were owning as there won't
+ // be another draw
+ caches.textureCache.resetMarkInUse(this);
+ mRenderThread.renderState().flush(Caches::FlushMode::Layers);
+}
+
+void OpenGLPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo) {
+ static const std::vector< sp<RenderNode> > emptyNodeList;
+ auto& caches = Caches::getInstance();
+ FrameBuilder frameBuilder(*layerUpdateQueue, lightGeometry, caches);
+ layerUpdateQueue->clear();
+ BakedOpRenderer renderer(caches, mRenderThread.renderState(),
+ opaque, lightInfo);
+ LOG_ALWAYS_FATAL_IF(renderer.didDraw(), "shouldn't draw in buildlayer case");
+ frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
+}
+
+TaskManager* OpenGLPipeline::getTaskManager() {
+ return &Caches::getInstance().tasks;
+}
+
+static bool layerMatchesWH(OffscreenBuffer* layer, int width, int height) {
+ return layer->viewportWidth == (uint32_t)width && layer->viewportHeight == (uint32_t)height;
+}
+
+bool OpenGLPipeline::createOrUpdateLayer(RenderNode* node,
+ const DamageAccumulator& damageAccumulator) {
+ RenderState& renderState = mRenderThread.renderState();
+ OffscreenBufferPool& layerPool = renderState.layerPool();
+ bool transformUpdateNeeded = false;
+ if (node->getLayer() == nullptr) {
+ node->setLayer(layerPool.get(renderState, node->getWidth(), node->getHeight()));
+ transformUpdateNeeded = true;
+ } else if (!layerMatchesWH(node->getLayer(), node->getWidth(), node->getHeight())) {
+ // TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering)
+ // Or, ideally, maintain damage between frames on node/layer so ordering is always correct
+ if (node->properties().fitsOnLayer()) {
+ node->setLayer(layerPool.resize(node->getLayer(), node->getWidth(), node->getHeight()));
+ } else {
+ destroyLayer(node);
+ }
+ transformUpdateNeeded = true;
+ }
+
+ if (transformUpdateNeeded && node->getLayer()) {
+ // update the transform in window of the layer to reset its origin wrt light source position
+ Matrix4 windowTransform;
+ damageAccumulator.computeCurrentTransform(&windowTransform);
+ node->getLayer()->setWindowTransform(windowTransform);
+ }
+
+ return transformUpdateNeeded;
+}
+
+bool OpenGLPipeline::pinImages(LsaVector<sk_sp<Bitmap>>& images) {
+ TextureCache& cache = Caches::getInstance().textureCache;
+ bool prefetchSucceeded = true;
+ for (auto& bitmapResource : images) {
+ prefetchSucceeded &= cache.prefetchAndMarkInUse(this, bitmapResource.get());
+ }
+ return prefetchSucceeded;
+}
+
+void OpenGLPipeline::unpinImages() {
+ Caches::getInstance().textureCache.resetMarkInUse(this);
+}
+
+void OpenGLPipeline::destroyLayer(RenderNode* node) {
+ if (OffscreenBuffer* layer = node->getLayer()) {
+ layer->renderState.layerPool().putOrDelete(layer);
+ node->setLayer(nullptr);
+ }
+}
+
+void OpenGLPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
+ if (Caches::hasInstance() && thread.eglManager().hasEglContext()) {
+ ATRACE_NAME("Bitmap#prepareToDraw task");
+ Caches::getInstance().textureCache.prefetch(bitmap);
+ }
+}
+
+void OpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
+ DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
+ if (thread.eglManager().hasEglContext()) {
+ mode = DrawGlInfo::kModeProcess;
+ }
+ thread.renderState().invokeFunctor(functor, mode, nullptr);
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */