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