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