CanvasContext.cpp revision 3b20251a355c88193c439f928a84ae69483fb488
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.hasFunctors) {
152            info.out.requiresUiRedraw = true;
153        } else if (!info.out.requiresUiRedraw) {
154            // If animationsNeedsRedraw is set don't bother posting for an RT anim
155            // as we will just end up fighting the UI thread.
156            mRenderThread.postFrameCallback(this);
157        }
158    }
159}
160
161void CanvasContext::notifyFramePending() {
162    ATRACE_CALL();
163    mRenderThread.pushBackFrameCallback(this);
164}
165
166void CanvasContext::draw() {
167    LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
168            "drawRenderNode called on a context with no canvas or surface!");
169
170    profiler().markPlaybackStart();
171
172    SkRect dirty;
173    mDamageAccumulator.finish(&dirty);
174
175    EGLint width, height;
176    mEglManager.beginFrame(mEglSurface, &width, &height);
177    if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
178        mCanvas->setViewport(width, height);
179        dirty.setEmpty();
180    } else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
181        dirty.setEmpty();
182    } else {
183        profiler().unionDirty(&dirty);
184    }
185
186    status_t status;
187    if (!dirty.isEmpty()) {
188        status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
189                dirty.fRight, dirty.fBottom, mOpaque);
190    } else {
191        status = mCanvas->prepare(mOpaque);
192    }
193
194    Rect outBounds;
195    status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);
196
197    profiler().draw(mCanvas);
198
199    mCanvas->finish();
200
201    profiler().markPlaybackEnd();
202
203    if (status & DrawGlInfo::kStatusDrew) {
204        swapBuffers();
205    }
206
207    profiler().finishFrame();
208}
209
210// Called by choreographer to do an RT-driven animation
211void CanvasContext::doFrame() {
212    if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) {
213        return;
214    }
215
216    ATRACE_CALL();
217
218    profiler().startFrame();
219
220    TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState());
221    info.prepareTextures = false;
222
223    prepareTree(info);
224    if (info.out.canDrawThisFrame) {
225        draw();
226    }
227}
228
229void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) {
230    ATRACE_CALL();
231    DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
232    if (thread.eglManager().hasEglContext()) {
233        thread.eglManager().requireGlContext();
234        mode = DrawGlInfo::kModeProcess;
235    }
236
237    thread.renderState().invokeFunctor(functor, mode, NULL);
238}
239
240bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
241    requireGlContext();
242    TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState());
243    layer->apply(info);
244    return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap);
245}
246
247void CanvasContext::flushCaches(Caches::FlushMode flushMode) {
248    if (mEglManager.hasEglContext()) {
249        requireGlContext();
250        Caches::getInstance().flush(flushMode);
251    }
252}
253
254void CanvasContext::runWithGlContext(RenderTask* task) {
255    requireGlContext();
256    task->run();
257}
258
259Layer* CanvasContext::createRenderLayer(int width, int height) {
260    requireSurface();
261    return LayerRenderer::createRenderLayer(mRenderThread.renderState(), width, height);
262}
263
264Layer* CanvasContext::createTextureLayer() {
265    requireSurface();
266    return LayerRenderer::createTextureLayer(mRenderThread.renderState());
267}
268
269void CanvasContext::requireGlContext() {
270    if (mEglSurface != EGL_NO_SURFACE) {
271        makeCurrent();
272    } else {
273        mEglManager.usePBufferSurface();
274    }
275}
276
277void CanvasContext::setTextureAtlas(RenderThread& thread,
278        const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) {
279    thread.eglManager().setTextureAtlas(buffer, map, mapSize);
280}
281
282} /* namespace renderthread */
283} /* namespace uirenderer */
284} /* namespace android */
285