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