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