CanvasContext.cpp revision cd028f336e36b22dbe8cf623eb5bd2361314495c
1/*
2 * Copyright (C) 2014 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#define LOG_TAG "CanvasContext"
18
19#include "CanvasContext.h"
20
21#include <private/hwui/DrawGlInfo.h>
22#include <strings.h>
23
24#include "EglManager.h"
25#include "RenderThread.h"
26#include "../Caches.h"
27#include "../DeferredLayerUpdater.h"
28#include "../RenderState.h"
29#include "../LayerRenderer.h"
30#include "../OpenGLRenderer.h"
31#include "../Stencil.h"
32
33namespace android {
34namespace uirenderer {
35namespace renderthread {
36
37CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode)
38        : mRenderThread(thread)
39        , mEglManager(thread.eglManager())
40        , mEglSurface(EGL_NO_SURFACE)
41        , mDirtyRegionsEnabled(false)
42        , mOpaque(!translucent)
43        , mCanvas(NULL)
44        , mHaveNewSurface(false)
45        , mRootRenderNode(rootRenderNode) {
46}
47
48CanvasContext::~CanvasContext() {
49    destroyCanvasAndSurface();
50    mRenderThread.removeFrameCallback(this);
51}
52
53void CanvasContext::destroyCanvasAndSurface() {
54    if (mCanvas) {
55        delete mCanvas;
56        mCanvas = 0;
57    }
58    setSurface(NULL);
59}
60
61void CanvasContext::setSurface(ANativeWindow* window) {
62    mNativeWindow = window;
63
64    if (mEglSurface != EGL_NO_SURFACE) {
65        mEglManager.destroySurface(mEglSurface);
66        mEglSurface = EGL_NO_SURFACE;
67    }
68
69    if (window) {
70        mEglSurface = mEglManager.createSurface(window);
71    }
72
73    if (mEglSurface != EGL_NO_SURFACE) {
74        mDirtyRegionsEnabled = mEglManager.enableDirtyRegions(mEglSurface);
75        mHaveNewSurface = true;
76        makeCurrent();
77    } else {
78        mRenderThread.removeFrameCallback(this);
79    }
80}
81
82void CanvasContext::swapBuffers() {
83    mEglManager.swapBuffers(mEglSurface);
84    mHaveNewSurface = false;
85}
86
87void CanvasContext::requireSurface() {
88    LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
89            "requireSurface() called but no surface set!");
90    makeCurrent();
91}
92
93bool CanvasContext::initialize(ANativeWindow* window) {
94    if (mCanvas) return false;
95    setSurface(window);
96    mCanvas = new OpenGLRenderer(mRenderThread.renderState());
97    mCanvas->initProperties();
98    return true;
99}
100
101void CanvasContext::updateSurface(ANativeWindow* window) {
102    setSurface(window);
103}
104
105void CanvasContext::pauseSurface(ANativeWindow* window) {
106    // TODO: For now we just need a fence, in the future suspend any animations
107    // and such to prevent from trying to render into this surface
108}
109
110void CanvasContext::setup(int width, int height, const Vector3& lightCenter, float lightRadius) {
111    if (!mCanvas) return;
112    mCanvas->setViewport(width, height);
113    mCanvas->initializeLight(lightCenter, lightRadius);
114}
115
116void CanvasContext::setOpaque(bool opaque) {
117    mOpaque = opaque;
118}
119
120void CanvasContext::makeCurrent() {
121    // TODO: Figure out why this workaround is needed, see b/13913604
122    // In the meantime this matches the behavior of GLRenderer, so it is not a regression
123    mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface);
124}
125
126void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater, TreeInfo& info) {
127    bool success = layerUpdater->apply(info);
128    LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!");
129    if (layerUpdater->backingLayer()->deferredUpdateScheduled) {
130        mCanvas->pushLayerUpdate(layerUpdater->backingLayer());
131    }
132}
133
134void CanvasContext::prepareTree(TreeInfo& info) {
135    mRenderThread.removeFrameCallback(this);
136
137    info.frameTimeMs = mRenderThread.timeLord().frameTimeMs();
138    info.damageAccumulator = &mDamageAccumulator;
139    info.renderer = mCanvas;
140    mRootRenderNode->prepareTree(info);
141
142    int runningBehind = 0;
143    // TODO: This query is moderately expensive, investigate adding some sort
144    // of fast-path based off when we last called eglSwapBuffers() as well as
145    // last vsync time. Or something.
146    mNativeWindow->query(mNativeWindow.get(),
147            NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
148    info.out.canDrawThisFrame = !runningBehind;
149
150    if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
151        if (!info.out.requiresUiRedraw) {
152            // If animationsNeedsRedraw is set don't bother posting for an RT anim
153            // as we will just end up fighting the UI thread.
154            mRenderThread.postFrameCallback(this);
155        }
156    }
157}
158
159void CanvasContext::notifyFramePending() {
160    ATRACE_CALL();
161    mRenderThread.pushBackFrameCallback(this);
162}
163
164void CanvasContext::draw() {
165    LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
166            "drawRenderNode called on a context with no canvas or surface!");
167
168    profiler().markPlaybackStart();
169
170    SkRect dirty;
171    mDamageAccumulator.finish(&dirty);
172
173    EGLint width, height;
174    mEglManager.beginFrame(mEglSurface, &width, &height);
175    if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
176        mCanvas->setViewport(width, height);
177        dirty.setEmpty();
178    } else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
179        dirty.setEmpty();
180    } else {
181        profiler().unionDirty(&dirty);
182    }
183
184    status_t status;
185    if (!dirty.isEmpty()) {
186        status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
187                dirty.fRight, dirty.fBottom, mOpaque);
188    } else {
189        status = mCanvas->prepare(mOpaque);
190    }
191
192    Rect outBounds;
193    status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);
194
195    profiler().draw(mCanvas);
196
197    mCanvas->finish();
198
199    profiler().markPlaybackEnd();
200
201    if (status & DrawGlInfo::kStatusDrew) {
202        swapBuffers();
203    }
204
205    profiler().finishFrame();
206}
207
208// Called by choreographer to do an RT-driven animation
209void CanvasContext::doFrame() {
210    if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) {
211        return;
212    }
213
214    ATRACE_CALL();
215
216    profiler().startFrame();
217
218    TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState());
219    info.prepareTextures = false;
220
221    prepareTree(info);
222    if (info.out.canDrawThisFrame) {
223        draw();
224    }
225}
226
227void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) {
228    ATRACE_CALL();
229    DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
230    if (thread.eglManager().hasEglContext()) {
231        thread.eglManager().requireGlContext();
232        mode = DrawGlInfo::kModeProcess;
233    }
234
235    thread.renderState().invokeFunctor(functor, mode, NULL);
236}
237
238bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
239    requireGlContext();
240    TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState());
241    layer->apply(info);
242    return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap);
243}
244
245void CanvasContext::flushCaches(Caches::FlushMode flushMode) {
246    if (mEglManager.hasEglContext()) {
247        requireGlContext();
248        Caches::getInstance().flush(flushMode);
249    }
250}
251
252void CanvasContext::runWithGlContext(RenderTask* task) {
253    requireGlContext();
254    task->run();
255}
256
257Layer* CanvasContext::createRenderLayer(int width, int height) {
258    requireSurface();
259    return LayerRenderer::createRenderLayer(mRenderThread.renderState(), width, height);
260}
261
262Layer* CanvasContext::createTextureLayer() {
263    requireSurface();
264    return LayerRenderer::createTextureLayer(mRenderThread.renderState());
265}
266
267void CanvasContext::requireGlContext() {
268    if (mEglSurface != EGL_NO_SURFACE) {
269        makeCurrent();
270    } else {
271        mEglManager.usePBufferSurface();
272    }
273}
274
275void CanvasContext::setTextureAtlas(RenderThread& thread,
276        const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) {
277    thread.eglManager().setTextureAtlas(buffer, map, mapSize);
278}
279
280} /* namespace renderthread */
281} /* namespace uirenderer */
282} /* namespace android */
283