2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "CanvasContext.h"
20 #include <private/hwui/DrawGlInfo.h>
23 #include "EglManager.h"
24 #include "RenderThread.h"
25 #include "../AnimationContext.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"
33 #define TRIM_MEMORY_COMPLETE 80
34 #define TRIM_MEMORY_UI_HIDDEN 20
37 namespace uirenderer {
38 namespace renderthread {
40 CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
41 RenderNode* rootRenderNode, IContextFactory* contextFactory)
42 : mRenderThread(thread)
43 , mEglManager(thread.eglManager())
44 , mEglSurface(EGL_NO_SURFACE)
45 , mDirtyRegionsEnabled(false)
46 , mOpaque(!translucent)
48 , mHaveNewSurface(false)
49 , mRootRenderNode(rootRenderNode) {
50 mAnimationContext = contextFactory->createAnimationContext(mRenderThread.timeLord());
51 mRenderThread.renderState().registerCanvasContext(this);
54 CanvasContext::~CanvasContext() {
56 delete mAnimationContext;
57 mRenderThread.renderState().unregisterCanvasContext(this);
60 void CanvasContext::destroy() {
62 freePrefetechedLayers();
63 destroyHardwareResources();
64 mAnimationContext->destroy();
72 void CanvasContext::setSurface(ANativeWindow* window) {
73 mNativeWindow = window;
75 if (mEglSurface != EGL_NO_SURFACE) {
76 mEglManager.destroySurface(mEglSurface);
77 mEglSurface = EGL_NO_SURFACE;
81 mEglSurface = mEglManager.createSurface(window);
84 if (mEglSurface != EGL_NO_SURFACE) {
85 mDirtyRegionsEnabled = mEglManager.enableDirtyRegions(mEglSurface);
86 mHaveNewSurface = true;
89 mRenderThread.removeFrameCallback(this);
93 void CanvasContext::swapBuffers() {
94 if (CC_UNLIKELY(!mEglManager.swapBuffers(mEglSurface))) {
97 mHaveNewSurface = false;
100 void CanvasContext::requireSurface() {
101 LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
102 "requireSurface() called but no surface set!");
106 bool CanvasContext::initialize(ANativeWindow* window) {
108 if (mCanvas) return false;
109 mCanvas = new OpenGLRenderer(mRenderThread.renderState());
110 mCanvas->initProperties();
114 void CanvasContext::updateSurface(ANativeWindow* window) {
118 void CanvasContext::pauseSurface(ANativeWindow* window) {
122 // TODO: don't pass viewport size, it's automatic via EGL
123 void CanvasContext::setup(int width, int height, const Vector3& lightCenter, float lightRadius,
124 uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
125 if (!mCanvas) return;
126 mCanvas->initLight(lightCenter, lightRadius, ambientShadowAlpha, spotShadowAlpha);
129 void CanvasContext::setOpaque(bool opaque) {
133 void CanvasContext::makeCurrent() {
134 // TODO: Figure out why this workaround is needed, see b/13913604
135 // In the meantime this matches the behavior of GLRenderer, so it is not a regression
136 mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface);
139 void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) {
140 bool success = layerUpdater->apply();
141 LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!");
142 if (layerUpdater->backingLayer()->deferredUpdateScheduled) {
143 mCanvas->pushLayerUpdate(layerUpdater->backingLayer());
147 void CanvasContext::prepareTree(TreeInfo& info) {
148 mRenderThread.removeFrameCallback(this);
150 info.damageAccumulator = &mDamageAccumulator;
151 info.renderer = mCanvas;
152 if (mPrefetechedLayers.size() && info.mode == TreeInfo::MODE_FULL) {
153 info.canvasContext = this;
155 mAnimationContext->startFrame(info.mode);
156 mRootRenderNode->prepareTree(info);
157 mAnimationContext->runRemainingAnimations(info);
159 if (info.canvasContext) {
160 freePrefetechedLayers();
163 int runningBehind = 0;
164 // TODO: This query is moderately expensive, investigate adding some sort
165 // of fast-path based off when we last called eglSwapBuffers() as well as
166 // last vsync time. Or something.
167 mNativeWindow->query(mNativeWindow.get(),
168 NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
169 info.out.canDrawThisFrame = !runningBehind;
171 if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
172 if (!info.out.requiresUiRedraw) {
173 // If animationsNeedsRedraw is set don't bother posting for an RT anim
174 // as we will just end up fighting the UI thread.
175 mRenderThread.postFrameCallback(this);
180 void CanvasContext::stopDrawing() {
181 mRenderThread.removeFrameCallback(this);
184 void CanvasContext::notifyFramePending() {
186 mRenderThread.pushBackFrameCallback(this);
189 void CanvasContext::draw() {
190 LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
191 "drawRenderNode called on a context with no canvas or surface!");
193 profiler().markPlaybackStart();
196 mDamageAccumulator.finish(&dirty);
198 EGLint width, height;
199 mEglManager.beginFrame(mEglSurface, &width, &height);
200 if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
201 mCanvas->setViewport(width, height);
203 } else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
206 if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) {
207 ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?",
208 SK_RECT_ARGS(dirty), width, height);
211 profiler().unionDirty(&dirty);
215 if (!dirty.isEmpty()) {
216 status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
217 dirty.fRight, dirty.fBottom, mOpaque);
219 status = mCanvas->prepare(mOpaque);
223 status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);
225 profiler().draw(mCanvas);
229 profiler().markPlaybackEnd();
231 if (status & DrawGlInfo::kStatusDrew) {
235 profiler().finishFrame();
238 // Called by choreographer to do an RT-driven animation
239 void CanvasContext::doFrame() {
240 if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) {
246 profiler().startFrame();
248 TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState());
250 if (info.out.canDrawThisFrame) {
255 void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) {
257 DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
258 if (thread.eglManager().hasEglContext()) {
259 thread.eglManager().requireGlContext();
260 mode = DrawGlInfo::kModeProcess;
263 thread.renderState().invokeFunctor(functor, mode, NULL);
266 void CanvasContext::markLayerInUse(RenderNode* node) {
267 if (mPrefetechedLayers.erase(node)) {
272 static void destroyPrefetechedNode(RenderNode* node) {
273 ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...", node->getName());
274 node->destroyHardwareResources();
278 void CanvasContext::freePrefetechedLayers() {
279 if (mPrefetechedLayers.size()) {
281 std::for_each(mPrefetechedLayers.begin(), mPrefetechedLayers.end(), destroyPrefetechedNode);
282 mPrefetechedLayers.clear();
286 void CanvasContext::buildLayer(RenderNode* node) {
288 if (!mEglManager.hasEglContext() || !mCanvas) {
292 // buildLayer() will leave the tree in an unknown state, so we must stop drawing
295 TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState());
296 info.damageAccumulator = &mDamageAccumulator;
297 info.renderer = mCanvas;
298 info.runAnimations = false;
299 node->prepareTree(info);
301 mDamageAccumulator.finish(&ignore);
302 // Tickle the GENERIC property on node to mark it as dirty for damaging
303 // purposes when the frame is actually drawn
304 node->setPropertyFieldsDirty(RenderNode::GENERIC);
306 mCanvas->markLayersAsBuildLayers();
307 mCanvas->flushLayerUpdates();
310 mPrefetechedLayers.insert(node);
313 bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
316 return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap);
319 void CanvasContext::destroyHardwareResources() {
321 if (mEglManager.hasEglContext()) {
323 freePrefetechedLayers();
324 mRootRenderNode->destroyHardwareResources();
325 Caches::getInstance().flush(Caches::kFlushMode_Layers);
329 void CanvasContext::trimMemory(RenderThread& thread, int level) {
330 // No context means nothing to free
331 if (!thread.eglManager().hasEglContext()) return;
333 thread.eglManager().requireGlContext();
334 if (level >= TRIM_MEMORY_COMPLETE) {
335 Caches::getInstance().flush(Caches::kFlushMode_Full);
336 thread.eglManager().destroy();
337 } else if (level >= TRIM_MEMORY_UI_HIDDEN) {
338 Caches::getInstance().flush(Caches::kFlushMode_Moderate);
342 void CanvasContext::runWithGlContext(RenderTask* task) {
347 Layer* CanvasContext::createTextureLayer() {
349 return LayerRenderer::createTextureLayer(mRenderThread.renderState());
352 void CanvasContext::requireGlContext() {
353 mEglManager.requireGlContext();
356 void CanvasContext::setTextureAtlas(RenderThread& thread,
357 const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) {
358 thread.eglManager().setTextureAtlas(buffer, map, mapSize);
361 } /* namespace renderthread */
362 } /* namespace uirenderer */
363 } /* namespace android */