CanvasContext.cpp revision 240ff6246a29602539fd0295274e1c769e743a2e
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 info.out.canDrawThisFrame = false; 189 return; 190 } 191 192 int runningBehind = 0; 193 // TODO: This query is moderately expensive, investigate adding some sort 194 // of fast-path based off when we last called eglSwapBuffers() as well as 195 // last vsync time. Or something. 196 mNativeWindow->query(mNativeWindow.get(), 197 NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind); 198 info.out.canDrawThisFrame = !runningBehind; 199 200 if (info.out.hasAnimations || !info.out.canDrawThisFrame) { 201 if (!info.out.requiresUiRedraw) { 202 // If animationsNeedsRedraw is set don't bother posting for an RT anim 203 // as we will just end up fighting the UI thread. 204 mRenderThread.postFrameCallback(this); 205 } 206 } 207} 208 209void CanvasContext::stopDrawing() { 210 mRenderThread.removeFrameCallback(this); 211} 212 213void CanvasContext::notifyFramePending() { 214 ATRACE_CALL(); 215 mRenderThread.pushBackFrameCallback(this); 216} 217 218void CanvasContext::draw() { 219 LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, 220 "drawRenderNode called on a context with no canvas or surface!"); 221 222 SkRect dirty; 223 mDamageAccumulator.finish(&dirty); 224 225 if (dirty.isEmpty() && sSkipEmptyDamage) { 226 mCurrentFrameInfo->addFlag(FrameInfoFlags::kSkippedFrame); 227 return; 228 } 229 230 profiler().markPlaybackStart(); 231 mCurrentFrameInfo->markIssueDrawCommandsStart(); 232 233 EGLint width, height; 234 mEglManager.beginFrame(mEglSurface, &width, &height); 235 if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { 236 mCanvas->setViewport(width, height); 237 dirty.setEmpty(); 238 } else if (!mBufferPreserved || mHaveNewSurface) { 239 dirty.setEmpty(); 240 } else { 241 if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) { 242 ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", 243 SK_RECT_ARGS(dirty), width, height); 244 dirty.setEmpty(); 245 } 246 profiler().unionDirty(&dirty); 247 } 248 249 if (!dirty.isEmpty()) { 250 mCanvas->prepareDirty(dirty.fLeft, dirty.fTop, 251 dirty.fRight, dirty.fBottom, mOpaque); 252 } else { 253 mCanvas->prepare(mOpaque); 254 } 255 256 Rect outBounds; 257 mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds); 258 259 profiler().draw(mCanvas); 260 261 bool drew = mCanvas->finish(); 262 263 profiler().markPlaybackEnd(); 264 265 // Even if we decided to cancel the frame, from the perspective of jank 266 // metrics the frame was swapped at this point 267 mCurrentFrameInfo->markSwapBuffers(); 268 269 if (drew) { 270 swapBuffers(); 271 } else { 272 mEglManager.cancelFrame(); 273 } 274 275 // TODO: Use a fence for real completion? 276 mCurrentFrameInfo->markFrameCompleted(); 277 mJankTracker.addFrame(*mCurrentFrameInfo); 278 mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo); 279 profiler().finishFrame(); 280} 281 282// Called by choreographer to do an RT-driven animation 283void CanvasContext::doFrame() { 284 if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) { 285 return; 286 } 287 288 ATRACE_CALL(); 289 290 profiler().startFrame(); 291 int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE]; 292 UiFrameInfoBuilder(frameInfo) 293 .addFlag(FrameInfoFlags::kRTAnimation) 294 .setVsync(mRenderThread.timeLord().computeFrameTimeNanos(), 295 mRenderThread.timeLord().latestVsync()); 296 297 TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState()); 298 prepareTree(info, frameInfo); 299 if (info.out.canDrawThisFrame) { 300 draw(); 301 } else { 302 mCurrentFrameInfo->addFlag(FrameInfoFlags::kSkippedFrame); 303 } 304} 305 306void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) { 307 ATRACE_CALL(); 308 DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; 309 if (thread.eglManager().hasEglContext()) { 310 thread.eglManager().requireGlContext(); 311 mode = DrawGlInfo::kModeProcess; 312 } 313 314 thread.renderState().invokeFunctor(functor, mode, nullptr); 315} 316 317void CanvasContext::markLayerInUse(RenderNode* node) { 318 if (mPrefetechedLayers.erase(node)) { 319 node->decStrong(nullptr); 320 } 321} 322 323static void destroyPrefetechedNode(RenderNode* node) { 324 ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...", node->getName()); 325 node->destroyHardwareResources(); 326 node->decStrong(nullptr); 327} 328 329void CanvasContext::freePrefetechedLayers() { 330 if (mPrefetechedLayers.size()) { 331 requireGlContext(); 332 std::for_each(mPrefetechedLayers.begin(), mPrefetechedLayers.end(), destroyPrefetechedNode); 333 mPrefetechedLayers.clear(); 334 } 335} 336 337void CanvasContext::buildLayer(RenderNode* node) { 338 ATRACE_CALL(); 339 if (!mEglManager.hasEglContext() || !mCanvas) { 340 return; 341 } 342 requireGlContext(); 343 // buildLayer() will leave the tree in an unknown state, so we must stop drawing 344 stopDrawing(); 345 346 TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState()); 347 info.damageAccumulator = &mDamageAccumulator; 348 info.renderer = mCanvas; 349 info.runAnimations = false; 350 node->prepareTree(info); 351 SkRect ignore; 352 mDamageAccumulator.finish(&ignore); 353 // Tickle the GENERIC property on node to mark it as dirty for damaging 354 // purposes when the frame is actually drawn 355 node->setPropertyFieldsDirty(RenderNode::GENERIC); 356 357 mCanvas->markLayersAsBuildLayers(); 358 mCanvas->flushLayerUpdates(); 359 360 node->incStrong(nullptr); 361 mPrefetechedLayers.insert(node); 362} 363 364bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { 365 requireGlContext(); 366 layer->apply(); 367 return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap); 368} 369 370void CanvasContext::destroyHardwareResources() { 371 stopDrawing(); 372 if (mEglManager.hasEglContext()) { 373 requireGlContext(); 374 freePrefetechedLayers(); 375 mRootRenderNode->destroyHardwareResources(); 376 Caches::getInstance().flush(Caches::kFlushMode_Layers); 377 } 378} 379 380void CanvasContext::trimMemory(RenderThread& thread, int level) { 381 // No context means nothing to free 382 if (!thread.eglManager().hasEglContext()) return; 383 384 ATRACE_CALL(); 385 thread.eglManager().requireGlContext(); 386 if (level >= TRIM_MEMORY_COMPLETE) { 387 Caches::getInstance().flush(Caches::kFlushMode_Full); 388 thread.eglManager().destroy(); 389 } else if (level >= TRIM_MEMORY_UI_HIDDEN) { 390 Caches::getInstance().flush(Caches::kFlushMode_Moderate); 391 } 392} 393 394void CanvasContext::runWithGlContext(RenderTask* task) { 395 requireGlContext(); 396 task->run(); 397} 398 399Layer* CanvasContext::createTextureLayer() { 400 requireSurface(); 401 return LayerRenderer::createTextureLayer(mRenderThread.renderState()); 402} 403 404void CanvasContext::requireGlContext() { 405 mEglManager.requireGlContext(); 406} 407 408void CanvasContext::setTextureAtlas(RenderThread& thread, 409 const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) { 410 thread.eglManager().setTextureAtlas(buffer, map, mapSize); 411} 412 413void CanvasContext::dumpFrames(int fd) { 414 FILE* file = fdopen(fd, "a"); 415 fprintf(file, "\n\n---PROFILEDATA---"); 416 for (size_t i = 0; i < mFrames.size(); i++) { 417 FrameInfo& frame = mFrames[i]; 418 if (frame[FrameInfoIndex::kSyncStart] == 0) { 419 continue; 420 } 421 fprintf(file, "\n"); 422 for (int i = 0; i < static_cast<int>(FrameInfoIndex::kNumIndexes); i++) { 423 fprintf(file, "%" PRId64 ",", frame[i]); 424 } 425 } 426 fprintf(file, "\n---PROFILEDATA---\n\n"); 427 fflush(file); 428} 429 430void CanvasContext::resetFrameStats() { 431 mFrames.clear(); 432 mRenderThread.jankTracker().reset(); 433} 434 435} /* namespace renderthread */ 436} /* namespace uirenderer */ 437} /* namespace android */ 438