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