CanvasContext.cpp revision ec845a215e343cdb3b2e4c7b6aff7b24beb0236b
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#include "CanvasContext.h" 18 19#include <algorithm> 20#include <private/hwui/DrawGlInfo.h> 21#include <strings.h> 22 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" 32 33#define TRIM_MEMORY_COMPLETE 80 34#define TRIM_MEMORY_UI_HIDDEN 20 35 36namespace android { 37namespace uirenderer { 38namespace renderthread { 39 40CanvasContext::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) 47 , mCanvas(NULL) 48 , mHaveNewSurface(false) 49 , mRootRenderNode(rootRenderNode) { 50 mAnimationContext = contextFactory->createAnimationContext(mRenderThread.timeLord()); 51 mRenderThread.renderState().registerCanvasContext(this); 52} 53 54CanvasContext::~CanvasContext() { 55 destroy(); 56 delete mAnimationContext; 57 mRenderThread.renderState().unregisterCanvasContext(this); 58} 59 60void CanvasContext::destroy() { 61 stopDrawing(); 62 freePrefetechedLayers(); 63 destroyHardwareResources(); 64 mAnimationContext->destroy(); 65 if (mCanvas) { 66 delete mCanvas; 67 mCanvas = 0; 68 } 69 setSurface(NULL); 70} 71 72void CanvasContext::setSurface(ANativeWindow* window) { 73 mNativeWindow = window; 74 75 if (mEglSurface != EGL_NO_SURFACE) { 76 mEglManager.destroySurface(mEglSurface); 77 mEglSurface = EGL_NO_SURFACE; 78 } 79 80 if (window) { 81 mEglSurface = mEglManager.createSurface(window); 82 } 83 84 if (mEglSurface != EGL_NO_SURFACE) { 85 mDirtyRegionsEnabled = mEglManager.enableDirtyRegions(mEglSurface); 86 mHaveNewSurface = true; 87 makeCurrent(); 88 } else { 89 mRenderThread.removeFrameCallback(this); 90 } 91} 92 93void CanvasContext::swapBuffers() { 94 mEglManager.swapBuffers(mEglSurface); 95 mHaveNewSurface = false; 96} 97 98void CanvasContext::requireSurface() { 99 LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, 100 "requireSurface() called but no surface set!"); 101 makeCurrent(); 102} 103 104bool CanvasContext::initialize(ANativeWindow* window) { 105 if (mCanvas) return false; 106 setSurface(window); 107 mCanvas = new OpenGLRenderer(mRenderThread.renderState()); 108 mCanvas->initProperties(); 109 return true; 110} 111 112void CanvasContext::updateSurface(ANativeWindow* window) { 113 setSurface(window); 114} 115 116void CanvasContext::pauseSurface(ANativeWindow* window) { 117 stopDrawing(); 118} 119 120void CanvasContext::setup(int width, int height, const Vector3& lightCenter, float lightRadius, 121 uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { 122 if (!mCanvas) return; 123 mCanvas->setViewport(width, height); 124 mCanvas->initLight(lightCenter, lightRadius, ambientShadowAlpha, spotShadowAlpha); 125} 126 127void CanvasContext::setOpaque(bool opaque) { 128 mOpaque = opaque; 129} 130 131void CanvasContext::makeCurrent() { 132 // TODO: Figure out why this workaround is needed, see b/13913604 133 // In the meantime this matches the behavior of GLRenderer, so it is not a regression 134 mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface); 135} 136 137void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) { 138 bool success = layerUpdater->apply(); 139 LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!"); 140 if (layerUpdater->backingLayer()->deferredUpdateScheduled) { 141 mCanvas->pushLayerUpdate(layerUpdater->backingLayer()); 142 } 143} 144 145void CanvasContext::prepareTree(TreeInfo& info) { 146 mRenderThread.removeFrameCallback(this); 147 148 info.damageAccumulator = &mDamageAccumulator; 149 info.renderer = mCanvas; 150 if (mPrefetechedLayers.size() && info.mode == TreeInfo::MODE_FULL) { 151 info.canvasContext = this; 152 } 153 mAnimationContext->startFrame(info.mode); 154 mRootRenderNode->prepareTree(info); 155 mAnimationContext->runRemainingAnimations(info); 156 157 if (info.canvasContext) { 158 freePrefetechedLayers(); 159 } 160 161 int runningBehind = 0; 162 // TODO: This query is moderately expensive, investigate adding some sort 163 // of fast-path based off when we last called eglSwapBuffers() as well as 164 // last vsync time. Or something. 165 mNativeWindow->query(mNativeWindow.get(), 166 NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind); 167 info.out.canDrawThisFrame = !runningBehind; 168 169 if (info.out.hasAnimations || !info.out.canDrawThisFrame) { 170 if (!info.out.requiresUiRedraw) { 171 // If animationsNeedsRedraw is set don't bother posting for an RT anim 172 // as we will just end up fighting the UI thread. 173 mRenderThread.postFrameCallback(this); 174 } 175 } 176} 177 178void CanvasContext::stopDrawing() { 179 mRenderThread.removeFrameCallback(this); 180} 181 182void CanvasContext::notifyFramePending() { 183 ATRACE_CALL(); 184 mRenderThread.pushBackFrameCallback(this); 185} 186 187void CanvasContext::draw() { 188 LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, 189 "drawRenderNode called on a context with no canvas or surface!"); 190 191 profiler().markPlaybackStart(); 192 193 SkRect dirty; 194 mDamageAccumulator.finish(&dirty); 195 196 EGLint width, height; 197 mEglManager.beginFrame(mEglSurface, &width, &height); 198 if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { 199 mCanvas->setViewport(width, height); 200 dirty.setEmpty(); 201 } else if (!mDirtyRegionsEnabled || mHaveNewSurface) { 202 dirty.setEmpty(); 203 } else { 204 if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) { 205 ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", 206 SK_RECT_ARGS(dirty), width, height); 207 dirty.setEmpty(); 208 } 209 profiler().unionDirty(&dirty); 210 } 211 212 status_t status; 213 if (!dirty.isEmpty()) { 214 status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop, 215 dirty.fRight, dirty.fBottom, mOpaque); 216 } else { 217 status = mCanvas->prepare(mOpaque); 218 } 219 220 Rect outBounds; 221 status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds); 222 223 profiler().draw(mCanvas); 224 225 mCanvas->finish(); 226 227 profiler().markPlaybackEnd(); 228 229 if (status & DrawGlInfo::kStatusDrew) { 230 swapBuffers(); 231 } 232 233 profiler().finishFrame(); 234} 235 236// Called by choreographer to do an RT-driven animation 237void CanvasContext::doFrame() { 238 if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) { 239 return; 240 } 241 242 ATRACE_CALL(); 243 244 profiler().startFrame(); 245 246 TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState()); 247 prepareTree(info); 248 if (info.out.canDrawThisFrame) { 249 draw(); 250 } 251} 252 253void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) { 254 ATRACE_CALL(); 255 DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; 256 if (thread.eglManager().hasEglContext()) { 257 thread.eglManager().requireGlContext(); 258 mode = DrawGlInfo::kModeProcess; 259 } 260 261 thread.renderState().invokeFunctor(functor, mode, NULL); 262} 263 264void CanvasContext::markLayerInUse(RenderNode* node) { 265 if (mPrefetechedLayers.erase(node)) { 266 node->decStrong(0); 267 } 268} 269 270static void destroyPrefetechedNode(RenderNode* node) { 271 ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...", node->getName()); 272 node->destroyHardwareResources(); 273 node->decStrong(0); 274} 275 276void CanvasContext::freePrefetechedLayers() { 277 if (mPrefetechedLayers.size()) { 278 requireGlContext(); 279 std::for_each(mPrefetechedLayers.begin(), mPrefetechedLayers.end(), destroyPrefetechedNode); 280 mPrefetechedLayers.clear(); 281 } 282} 283 284void CanvasContext::buildLayer(RenderNode* node) { 285 ATRACE_CALL(); 286 if (!mEglManager.hasEglContext() || !mCanvas) { 287 return; 288 } 289 requireGlContext(); 290 // buildLayer() will leave the tree in an unknown state, so we must stop drawing 291 stopDrawing(); 292 293 TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState()); 294 info.damageAccumulator = &mDamageAccumulator; 295 info.renderer = mCanvas; 296 info.runAnimations = false; 297 node->prepareTree(info); 298 SkRect ignore; 299 mDamageAccumulator.finish(&ignore); 300 // Tickle the GENERIC property on node to mark it as dirty for damaging 301 // purposes when the frame is actually drawn 302 node->setPropertyFieldsDirty(RenderNode::GENERIC); 303 304 mCanvas->markLayersAsBuildLayers(); 305 mCanvas->flushLayerUpdates(); 306 307 node->incStrong(0); 308 mPrefetechedLayers.insert(node); 309} 310 311bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { 312 requireGlContext(); 313 layer->apply(); 314 return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap); 315} 316 317void CanvasContext::destroyHardwareResources() { 318 stopDrawing(); 319 if (mEglManager.hasEglContext()) { 320 requireGlContext(); 321 freePrefetechedLayers(); 322 mRootRenderNode->destroyHardwareResources(); 323 Caches::getInstance().flush(Caches::kFlushMode_Layers); 324 } 325} 326 327void CanvasContext::trimMemory(RenderThread& thread, int level) { 328 // No context means nothing to free 329 if (!thread.eglManager().hasEglContext()) return; 330 331 thread.eglManager().requireGlContext(); 332 if (level >= TRIM_MEMORY_COMPLETE) { 333 Caches::getInstance().flush(Caches::kFlushMode_Full); 334 thread.eglManager().destroy(); 335 } else if (level >= TRIM_MEMORY_UI_HIDDEN) { 336 Caches::getInstance().flush(Caches::kFlushMode_Moderate); 337 } 338} 339 340void CanvasContext::runWithGlContext(RenderTask* task) { 341 requireGlContext(); 342 task->run(); 343} 344 345Layer* CanvasContext::createRenderLayer(int width, int height) { 346 requireSurface(); 347 return LayerRenderer::createRenderLayer(mRenderThread.renderState(), width, height); 348} 349 350Layer* CanvasContext::createTextureLayer() { 351 requireSurface(); 352 return LayerRenderer::createTextureLayer(mRenderThread.renderState()); 353} 354 355void CanvasContext::requireGlContext() { 356 mEglManager.requireGlContext(); 357} 358 359void CanvasContext::setTextureAtlas(RenderThread& thread, 360 const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) { 361 thread.eglManager().setTextureAtlas(buffer, map, mapSize); 362} 363 364} /* namespace renderthread */ 365} /* namespace uirenderer */ 366} /* namespace android */ 367