1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "OpenGLPipeline.h"
18
19#include "DeferredLayerUpdater.h"
20#include "EglManager.h"
21#include "Frame.h"
22#include "GlLayer.h"
23#include "ProfileRenderer.h"
24#include "renderstate/RenderState.h"
25#include "OpenGLReadback.h"
26
27#include <cutils/properties.h>
28#include <strings.h>
29
30namespace android {
31namespace uirenderer {
32namespace renderthread {
33
34OpenGLPipeline::OpenGLPipeline(RenderThread& thread)
35        :  mEglManager(thread.eglManager())
36        , mRenderThread(thread) {
37}
38
39MakeCurrentResult OpenGLPipeline::makeCurrent() {
40    // TODO: Figure out why this workaround is needed, see b/13913604
41    // In the meantime this matches the behavior of GLRenderer, so it is not a regression
42    EGLint error = 0;
43    bool haveNewSurface = mEglManager.makeCurrent(mEglSurface, &error);
44
45    Caches::getInstance().textureCache.resetMarkInUse(this);
46    if (!haveNewSurface) {
47        return MakeCurrentResult::AlreadyCurrent;
48    }
49    return error ? MakeCurrentResult::Failed : MakeCurrentResult::Succeeded;
50}
51
52Frame OpenGLPipeline::getFrame() {
53    LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
54                "drawRenderNode called on a context with no surface!");
55    return mEglManager.beginFrame(mEglSurface);
56}
57
58bool OpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
59        const FrameBuilder::LightGeometry& lightGeometry,
60        LayerUpdateQueue* layerUpdateQueue,
61        const Rect& contentDrawBounds, bool opaque,
62        const BakedOpRenderer::LightInfo& lightInfo,
63        const std::vector< sp<RenderNode> >& renderNodes,
64        FrameInfoVisualizer* profiler) {
65
66    mEglManager.damageFrame(frame, dirty);
67
68    bool drew = false;
69
70
71    auto& caches = Caches::getInstance();
72    FrameBuilder frameBuilder(dirty, frame.width(), frame.height(), lightGeometry, caches);
73
74    frameBuilder.deferLayers(*layerUpdateQueue);
75    layerUpdateQueue->clear();
76
77    frameBuilder.deferRenderNodeScene(renderNodes, contentDrawBounds);
78
79    BakedOpRenderer renderer(caches, mRenderThread.renderState(),
80            opaque, lightInfo);
81    frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
82    ProfileRenderer profileRenderer(renderer);
83    profiler->draw(profileRenderer);
84    drew = renderer.didDraw();
85
86    // post frame cleanup
87    caches.clearGarbage();
88    caches.pathCache.trim();
89    caches.tessellationCache.trim();
90
91#if DEBUG_MEMORY_USAGE
92    caches.dumpMemoryUsage();
93#else
94    if (CC_UNLIKELY(Properties::debugLevel & kDebugMemory)) {
95        caches.dumpMemoryUsage();
96    }
97#endif
98
99    return drew;
100}
101
102bool OpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
103        FrameInfo* currentFrameInfo, bool* requireSwap) {
104
105    GL_CHECKPOINT(LOW);
106
107    // Even if we decided to cancel the frame, from the perspective of jank
108    // metrics the frame was swapped at this point
109    currentFrameInfo->markSwapBuffers();
110
111    *requireSwap = drew || mEglManager.damageRequiresSwap();
112
113    if (*requireSwap && (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty)))) {
114        return false;
115    }
116
117    return *requireSwap;
118}
119
120bool OpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
121    ATRACE_CALL();
122    // acquire most recent buffer for drawing
123    layer->updateTexImage();
124    layer->apply();
125    return OpenGLReadbackImpl::copyLayerInto(mRenderThread,
126            static_cast<GlLayer&>(*layer->backingLayer()), bitmap);
127}
128
129static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
130        SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) {
131    GlLayer* layer = new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha,
132            mode, blend);
133    Caches::getInstance().textureState().activateTexture(0);
134    layer->generateTexture();
135    return layer;
136}
137
138DeferredLayerUpdater* OpenGLPipeline::createTextureLayer() {
139    mEglManager.initialize();
140    return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::OpenGL);
141}
142
143void OpenGLPipeline::onStop() {
144    if (mEglManager.isCurrent(mEglSurface)) {
145        mEglManager.makeCurrent(EGL_NO_SURFACE);
146    }
147}
148
149bool OpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
150
151    if (mEglSurface != EGL_NO_SURFACE) {
152        mEglManager.destroySurface(mEglSurface);
153        mEglSurface = EGL_NO_SURFACE;
154    }
155
156    if (surface) {
157        mEglSurface = mEglManager.createSurface(surface);
158    }
159
160    if (mEglSurface != EGL_NO_SURFACE) {
161        const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
162        mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
163        return true;
164    }
165
166    return false;
167}
168
169bool OpenGLPipeline::isSurfaceReady() {
170    return CC_UNLIKELY(mEglSurface != EGL_NO_SURFACE);
171}
172
173bool OpenGLPipeline::isContextReady() {
174    return CC_LIKELY(mEglManager.hasEglContext());
175}
176
177void OpenGLPipeline::onDestroyHardwareResources() {
178    Caches& caches = Caches::getInstance();
179    // Make sure to release all the textures we were owning as there won't
180    // be another draw
181    caches.textureCache.resetMarkInUse(this);
182    mRenderThread.renderState().flush(Caches::FlushMode::Layers);
183}
184
185void OpenGLPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
186        LayerUpdateQueue* layerUpdateQueue, bool opaque,
187        const BakedOpRenderer::LightInfo& lightInfo) {
188    static const std::vector< sp<RenderNode> > emptyNodeList;
189    auto& caches = Caches::getInstance();
190    FrameBuilder frameBuilder(*layerUpdateQueue, lightGeometry, caches);
191    layerUpdateQueue->clear();
192    BakedOpRenderer renderer(caches, mRenderThread.renderState(),
193            opaque, lightInfo);
194    LOG_ALWAYS_FATAL_IF(renderer.didDraw(), "shouldn't draw in buildlayer case");
195    frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
196}
197
198TaskManager* OpenGLPipeline::getTaskManager() {
199    return &Caches::getInstance().tasks;
200}
201
202static bool layerMatchesWH(OffscreenBuffer* layer, int width, int height) {
203    return layer->viewportWidth == (uint32_t)width && layer->viewportHeight == (uint32_t)height;
204}
205
206bool OpenGLPipeline::createOrUpdateLayer(RenderNode* node,
207        const DamageAccumulator& damageAccumulator) {
208    RenderState& renderState = mRenderThread.renderState();
209    OffscreenBufferPool& layerPool = renderState.layerPool();
210    bool transformUpdateNeeded = false;
211    if (node->getLayer() == nullptr) {
212        node->setLayer(layerPool.get(renderState, node->getWidth(), node->getHeight()));
213        transformUpdateNeeded = true;
214    } else if (!layerMatchesWH(node->getLayer(), node->getWidth(), node->getHeight())) {
215        // TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering)
216        // Or, ideally, maintain damage between frames on node/layer so ordering is always correct
217        if (node->properties().fitsOnLayer()) {
218            node->setLayer(layerPool.resize(node->getLayer(), node->getWidth(), node->getHeight()));
219        } else {
220            destroyLayer(node);
221        }
222        transformUpdateNeeded = true;
223    }
224
225    if (transformUpdateNeeded && node->getLayer()) {
226        // update the transform in window of the layer to reset its origin wrt light source position
227        Matrix4 windowTransform;
228        damageAccumulator.computeCurrentTransform(&windowTransform);
229        node->getLayer()->setWindowTransform(windowTransform);
230    }
231
232    return transformUpdateNeeded;
233}
234
235bool OpenGLPipeline::pinImages(LsaVector<sk_sp<Bitmap>>& images) {
236    TextureCache& cache = Caches::getInstance().textureCache;
237    bool prefetchSucceeded = true;
238    for (auto& bitmapResource : images) {
239        prefetchSucceeded &= cache.prefetchAndMarkInUse(this, bitmapResource.get());
240    }
241    return prefetchSucceeded;
242}
243
244void OpenGLPipeline::unpinImages() {
245    Caches::getInstance().textureCache.resetMarkInUse(this);
246}
247
248void OpenGLPipeline::destroyLayer(RenderNode* node) {
249    if (OffscreenBuffer* layer = node->getLayer()) {
250        layer->renderState.layerPool().putOrDelete(layer);
251        node->setLayer(nullptr);
252    }
253}
254
255void OpenGLPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
256    if (Caches::hasInstance() && thread.eglManager().hasEglContext()) {
257        ATRACE_NAME("Bitmap#prepareToDraw task");
258        Caches::getInstance().textureCache.prefetch(bitmap);
259    }
260}
261
262void OpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
263    DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
264    if (thread.eglManager().hasEglContext()) {
265        mode = DrawGlInfo::kModeProcess;
266    }
267    thread.renderState().invokeFunctor(functor, mode, nullptr);
268}
269
270} /* namespace renderthread */
271} /* namespace uirenderer */
272} /* namespace android */
273