CanvasContext.cpp revision 0ddfbc46c434e8ce26919ee74526cda0d8d199af
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 <GpuMemoryTracker.h> 18#include "CanvasContext.h" 19 20#include "AnimationContext.h" 21#include "Caches.h" 22#include "DeferredLayerUpdater.h" 23#include "EglManager.h" 24#include "LayerUpdateQueue.h" 25#include "Properties.h" 26#include "Readback.h" 27#include "RenderThread.h" 28#include "hwui/Canvas.h" 29#include "renderstate/RenderState.h" 30#include "renderstate/Stencil.h" 31#include "protos/hwui.pb.h" 32#include "OpenGLPipeline.h" 33#include "utils/GLUtils.h" 34#include "utils/TimeUtils.h" 35 36#include <cutils/properties.h> 37#include <google/protobuf/io/zero_copy_stream_impl.h> 38#include <private/hwui/DrawGlInfo.h> 39#include <strings.h> 40 41#include <algorithm> 42#include <fcntl.h> 43#include <sys/stat.h> 44 45#include <cstdlib> 46 47#define TRIM_MEMORY_COMPLETE 80 48#define TRIM_MEMORY_UI_HIDDEN 20 49 50#define ENABLE_RENDERNODE_SERIALIZATION false 51 52#define LOG_FRAMETIME_MMA 0 53 54#if LOG_FRAMETIME_MMA 55static float sBenchMma = 0; 56static int sFrameCount = 0; 57static const float NANOS_PER_MILLIS_F = 1000000.0f; 58#endif 59 60namespace android { 61namespace uirenderer { 62namespace renderthread { 63 64CanvasContext* CanvasContext::create(RenderThread& thread, 65 bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) { 66 67 auto renderType = Properties::getRenderPipelineType(); 68 69 switch (renderType) { 70 case RenderPipelineType::OpenGL: 71 return new CanvasContext(thread, translucent, rootRenderNode, contextFactory, 72 std::make_unique<OpenGLPipeline>(thread)); 73 case RenderPipelineType::SkiaGL: 74 //TODO: implement SKIA GL 75 LOG_ALWAYS_FATAL("skiaGL canvas type not implemented."); 76 break; 77 case RenderPipelineType::SkiaVulkan: 78 //TODO: implement Vulkan 79 LOG_ALWAYS_FATAL("Vulkan canvas type not implemented."); 80 break; 81 default: 82 LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType); 83 break; 84 } 85 return nullptr; 86} 87 88CanvasContext::CanvasContext(RenderThread& thread, bool translucent, 89 RenderNode* rootRenderNode, IContextFactory* contextFactory, 90 std::unique_ptr<IRenderPipeline> renderPipeline) 91 : mRenderThread(thread) 92 , mOpaque(!translucent) 93 , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord())) 94 , mJankTracker(thread.mainDisplayInfo()) 95 , mProfiler(mFrames) 96 , mContentDrawBounds(0, 0, 0, 0) 97 , mRenderPipeline(std::move(renderPipeline)) { 98 mRenderNodes.emplace_back(rootRenderNode); 99 mRenderThread.renderState().registerCanvasContext(this); 100 mProfiler.setDensity(mRenderThread.mainDisplayInfo().density); 101} 102 103CanvasContext::~CanvasContext() { 104 destroy(nullptr); 105 mRenderThread.renderState().unregisterCanvasContext(this); 106} 107 108void CanvasContext::destroy(TreeObserver* observer) { 109 stopDrawing(); 110 setSurface(nullptr); 111 freePrefetchedLayers(observer); 112 destroyHardwareResources(observer); 113 mAnimationContext->destroy(); 114} 115 116void CanvasContext::setSurface(Surface* surface) { 117 ATRACE_CALL(); 118 119 mNativeSurface = surface; 120 121 bool hasSurface = mRenderPipeline->setSurface(surface, mSwapBehavior); 122 123 mFrameNumber = -1; 124 125 if (hasSurface) { 126 mHaveNewSurface = true; 127 mSwapHistory.clear(); 128 } else { 129 mRenderThread.removeFrameCallback(this); 130 } 131} 132 133void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) { 134 mSwapBehavior = swapBehavior; 135} 136 137void CanvasContext::initialize(Surface* surface) { 138 setSurface(surface); 139} 140 141void CanvasContext::updateSurface(Surface* surface) { 142 setSurface(surface); 143} 144 145bool CanvasContext::pauseSurface(Surface* surface) { 146 return mRenderThread.removeFrameCallback(this); 147} 148 149void CanvasContext::setStopped(bool stopped) { 150 if (mStopped != stopped) { 151 mStopped = stopped; 152 if (mStopped) { 153 mRenderThread.removeFrameCallback(this); 154 mRenderPipeline->onStop(); 155 } else if (mIsDirty && hasSurface()) { 156 mRenderThread.postFrameCallback(this); 157 } 158 } 159} 160 161void CanvasContext::setup(float lightRadius, 162 uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { 163 mLightGeometry.radius = lightRadius; 164 mLightInfo.ambientShadowAlpha = ambientShadowAlpha; 165 mLightInfo.spotShadowAlpha = spotShadowAlpha; 166} 167 168void CanvasContext::setLightCenter(const Vector3& lightCenter) { 169 mLightGeometry.center = lightCenter; 170} 171 172void CanvasContext::setOpaque(bool opaque) { 173 mOpaque = opaque; 174} 175 176bool CanvasContext::makeCurrent() { 177 if (mStopped) return false; 178 179 auto result = mRenderPipeline->makeCurrent(); 180 switch (result) { 181 case MakeCurrentResult::AlreadyCurrent: 182 return true; 183 case MakeCurrentResult::Failed: 184 mHaveNewSurface = true; 185 setSurface(nullptr); 186 return false; 187 case MakeCurrentResult::Succeeded: 188 mHaveNewSurface = true; 189 return true; 190 default: 191 LOG_ALWAYS_FATAL("unexpected result %d from IRenderPipeline::makeCurrent", 192 (int32_t) result); 193 } 194 195 return true; 196} 197 198static bool wasSkipped(FrameInfo* info) { 199 return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame); 200} 201 202bool CanvasContext::isSwapChainStuffed() { 203 static const auto SLOW_THRESHOLD = 6_ms; 204 205 if (mSwapHistory.size() != mSwapHistory.capacity()) { 206 // We want at least 3 frames of history before attempting to 207 // guess if the queue is stuffed 208 return false; 209 } 210 nsecs_t frameInterval = mRenderThread.timeLord().frameIntervalNanos(); 211 auto& swapA = mSwapHistory[0]; 212 213 // Was there a happy queue & dequeue time? If so, don't 214 // consider it stuffed 215 if (swapA.dequeueDuration < SLOW_THRESHOLD 216 && swapA.queueDuration < SLOW_THRESHOLD) { 217 return false; 218 } 219 220 for (size_t i = 1; i < mSwapHistory.size(); i++) { 221 auto& swapB = mSwapHistory[i]; 222 223 // If there's a multi-frameInterval gap we effectively already dropped a frame, 224 // so consider the queue healthy. 225 if (swapA.swapCompletedTime - swapB.swapCompletedTime > frameInterval * 3) { 226 return false; 227 } 228 229 // Was there a happy queue & dequeue time? If so, don't 230 // consider it stuffed 231 if (swapB.dequeueDuration < SLOW_THRESHOLD 232 && swapB.queueDuration < SLOW_THRESHOLD) { 233 return false; 234 } 235 236 swapA = swapB; 237 } 238 239 // All signs point to a stuffed swap chain 240 ATRACE_NAME("swap chain stuffed"); 241 return true; 242} 243 244void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, 245 int64_t syncQueued, RenderNode* target) { 246 mRenderThread.removeFrameCallback(this); 247 248 // If the previous frame was dropped we don't need to hold onto it, so 249 // just keep using the previous frame's structure instead 250 if (!wasSkipped(mCurrentFrameInfo)) { 251 mCurrentFrameInfo = &mFrames.next(); 252 } 253 mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo); 254 mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued; 255 mCurrentFrameInfo->markSyncStart(); 256 257 info.damageAccumulator = &mDamageAccumulator; 258 info.layerUpdateQueue = &mLayerUpdateQueue; 259 260 mAnimationContext->startFrame(info.mode); 261 for (const sp<RenderNode>& node : mRenderNodes) { 262 // Only the primary target node will be drawn full - all other nodes would get drawn in 263 // real time mode. In case of a window, the primary node is the window content and the other 264 // node(s) are non client / filler nodes. 265 info.mode = (node.get() == target ? TreeInfo::MODE_FULL : TreeInfo::MODE_RT_ONLY); 266 node->prepareTree(info); 267 GL_CHECKPOINT(MODERATE); 268 } 269 mAnimationContext->runRemainingAnimations(info); 270 GL_CHECKPOINT(MODERATE); 271 272 freePrefetchedLayers(info.observer); 273 GL_CHECKPOINT(MODERATE); 274 275 mIsDirty = true; 276 277 if (CC_UNLIKELY(!mNativeSurface.get())) { 278 mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); 279 info.out.canDrawThisFrame = false; 280 return; 281 } 282 283 if (CC_LIKELY(mSwapHistory.size() && !Properties::forceDrawFrame)) { 284 nsecs_t latestVsync = mRenderThread.timeLord().latestVsync(); 285 SwapHistory& lastSwap = mSwapHistory.back(); 286 nsecs_t vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync); 287 // The slight fudge-factor is to deal with cases where 288 // the vsync was estimated due to being slow handling the signal. 289 // See the logic in TimeLord#computeFrameTimeNanos or in 290 // Choreographer.java for details on when this happens 291 if (vsyncDelta < 2_ms) { 292 // Already drew for this vsync pulse, UI draw request missed 293 // the deadline for RT animations 294 info.out.canDrawThisFrame = false; 295 } else if (vsyncDelta >= mRenderThread.timeLord().frameIntervalNanos() * 3 296 || (latestVsync - mLastDropVsync) < 500_ms) { 297 // It's been several frame intervals, assume the buffer queue is fine 298 // or the last drop was too recent 299 info.out.canDrawThisFrame = true; 300 } else { 301 info.out.canDrawThisFrame = !isSwapChainStuffed(); 302 if (!info.out.canDrawThisFrame) { 303 // dropping frame 304 mLastDropVsync = mRenderThread.timeLord().latestVsync(); 305 } 306 } 307 } else { 308 info.out.canDrawThisFrame = true; 309 } 310 311 if (!info.out.canDrawThisFrame) { 312 mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); 313 } 314 315 if (info.out.hasAnimations || !info.out.canDrawThisFrame) { 316 if (!info.out.requiresUiRedraw) { 317 // If animationsNeedsRedraw is set don't bother posting for an RT anim 318 // as we will just end up fighting the UI thread. 319 mRenderThread.postFrameCallback(this); 320 } 321 } 322} 323 324void CanvasContext::stopDrawing() { 325 mRenderThread.removeFrameCallback(this); 326 mAnimationContext->pauseAnimators(); 327} 328 329void CanvasContext::notifyFramePending() { 330 ATRACE_CALL(); 331 mRenderThread.pushBackFrameCallback(this); 332} 333 334void CanvasContext::draw() { 335 SkRect dirty; 336 mDamageAccumulator.finish(&dirty); 337 338 // TODO: Re-enable after figuring out cause of b/22592975 339// if (dirty.isEmpty() && Properties::skipEmptyFrames) { 340// mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); 341// return; 342// } 343 344 mCurrentFrameInfo->markIssueDrawCommandsStart(); 345 346 Frame frame = mRenderPipeline->getFrame(); 347 348 SkRect windowDirty = computeDirtyRect(frame, &dirty); 349 350 bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue, 351 mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes, &(profiler())); 352 353 waitOnFences(); 354 355 bool requireSwap = false; 356 bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, 357 &requireSwap); 358 359 mIsDirty = false; 360 361 if (requireSwap) { 362 if (!didSwap) { //some error happened 363 setSurface(nullptr); 364 } 365 SwapHistory& swap = mSwapHistory.next(); 366 swap.damage = windowDirty; 367 swap.swapCompletedTime = systemTime(CLOCK_MONOTONIC); 368 swap.vsyncTime = mRenderThread.timeLord().latestVsync(); 369 if (mNativeSurface.get()) { 370 int durationUs; 371 mNativeSurface->query(NATIVE_WINDOW_LAST_DEQUEUE_DURATION, &durationUs); 372 swap.dequeueDuration = us2ns(durationUs); 373 mNativeSurface->query(NATIVE_WINDOW_LAST_QUEUE_DURATION, &durationUs); 374 swap.queueDuration = us2ns(durationUs); 375 } else { 376 swap.dequeueDuration = 0; 377 swap.queueDuration = 0; 378 } 379 mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) 380 = swap.dequeueDuration; 381 mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) 382 = swap.queueDuration; 383 mHaveNewSurface = false; 384 mFrameNumber = -1; 385 } else { 386 mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = 0; 387 mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = 0; 388 } 389 390 // TODO: Use a fence for real completion? 391 mCurrentFrameInfo->markFrameCompleted(); 392 393#if LOG_FRAMETIME_MMA 394 float thisFrame = mCurrentFrameInfo->duration( 395 FrameInfoIndex::IssueDrawCommandsStart, 396 FrameInfoIndex::FrameCompleted) / NANOS_PER_MILLIS_F; 397 if (sFrameCount) { 398 sBenchMma = ((9 * sBenchMma) + thisFrame) / 10; 399 } else { 400 sBenchMma = thisFrame; 401 } 402 if (++sFrameCount == 10) { 403 sFrameCount = 1; 404 ALOGD("Average frame time: %.4f", sBenchMma); 405 } 406#endif 407 408 mJankTracker.addFrame(*mCurrentFrameInfo); 409 mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo); 410 if (CC_UNLIKELY(mFrameMetricsReporter.get() != nullptr)) { 411 mFrameMetricsReporter->reportFrameMetrics(mCurrentFrameInfo->data()); 412 } 413 414 GpuMemoryTracker::onFrameCompleted(); 415} 416 417// Called by choreographer to do an RT-driven animation 418void CanvasContext::doFrame() { 419 if (!mRenderPipeline->isSurfaceReady()) return; 420 prepareAndDraw(nullptr); 421} 422 423void CanvasContext::prepareAndDraw(RenderNode* node) { 424 ATRACE_CALL(); 425 426 nsecs_t vsync = mRenderThread.timeLord().computeFrameTimeNanos(); 427 int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE]; 428 UiFrameInfoBuilder(frameInfo) 429 .addFlag(FrameInfoFlags::RTAnimation) 430 .setVsync(vsync, vsync); 431 432 TreeInfo info(TreeInfo::MODE_RT_ONLY, *this); 433 prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC), node); 434 if (info.out.canDrawThisFrame) { 435 draw(); 436 } 437} 438 439void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) { 440 ATRACE_CALL(); 441 DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; 442 if (thread.eglManager().hasEglContext()) { 443 mode = DrawGlInfo::kModeProcess; 444 } 445 446 thread.renderState().invokeFunctor(functor, mode, nullptr); 447} 448 449void CanvasContext::markLayerInUse(RenderNode* node) { 450 if (mPrefetchedLayers.erase(node)) { 451 node->decStrong(nullptr); 452 } 453} 454 455void CanvasContext::freePrefetchedLayers(TreeObserver* observer) { 456 if (mPrefetchedLayers.size()) { 457 for (auto& node : mPrefetchedLayers) { 458 ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...", 459 node->getName()); 460 node->destroyHardwareResources(observer); 461 node->decStrong(observer); 462 } 463 mPrefetchedLayers.clear(); 464 } 465} 466 467void CanvasContext::buildLayer(RenderNode* node, TreeObserver* observer) { 468 ATRACE_CALL(); 469 if (!mRenderPipeline->isContextReady()) return; 470 471 // buildLayer() will leave the tree in an unknown state, so we must stop drawing 472 stopDrawing(); 473 474 TreeInfo info(TreeInfo::MODE_FULL, *this); 475 info.damageAccumulator = &mDamageAccumulator; 476 info.observer = observer; 477 info.layerUpdateQueue = &mLayerUpdateQueue; 478 info.runAnimations = false; 479 node->prepareTree(info); 480 SkRect ignore; 481 mDamageAccumulator.finish(&ignore); 482 // Tickle the GENERIC property on node to mark it as dirty for damaging 483 // purposes when the frame is actually drawn 484 node->setPropertyFieldsDirty(RenderNode::GENERIC); 485 486 mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue, mOpaque, mLightInfo); 487 488 node->incStrong(nullptr); 489 mPrefetchedLayers.insert(node); 490} 491 492bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { 493 return mRenderPipeline->copyLayerInto(layer, bitmap); 494} 495 496void CanvasContext::destroyHardwareResources(TreeObserver* observer) { 497 stopDrawing(); 498 if (mRenderPipeline->isContextReady()) { 499 freePrefetchedLayers(observer); 500 for (const sp<RenderNode>& node : mRenderNodes) { 501 node->destroyHardwareResources(observer); 502 } 503 mRenderPipeline->onDestroyHardwareResources(); 504 } 505} 506 507void CanvasContext::trimMemory(RenderThread& thread, int level) { 508 // No context means nothing to free 509 if (!thread.eglManager().hasEglContext()) return; 510 511 ATRACE_CALL(); 512 if (level >= TRIM_MEMORY_COMPLETE) { 513 thread.renderState().flush(Caches::FlushMode::Full); 514 thread.eglManager().destroy(); 515 } else if (level >= TRIM_MEMORY_UI_HIDDEN) { 516 thread.renderState().flush(Caches::FlushMode::Moderate); 517 } 518} 519 520DeferredLayerUpdater* CanvasContext::createTextureLayer() { 521 return mRenderPipeline->createTextureLayer(); 522} 523 524void CanvasContext::setTextureAtlas(RenderThread& thread, 525 const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) { 526 thread.eglManager().setTextureAtlas(buffer, map, mapSize); 527} 528 529void CanvasContext::dumpFrames(int fd) { 530 FILE* file = fdopen(fd, "a"); 531 fprintf(file, "\n\n---PROFILEDATA---\n"); 532 for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) { 533 fprintf(file, "%s", FrameInfoNames[i].c_str()); 534 fprintf(file, ","); 535 } 536 for (size_t i = 0; i < mFrames.size(); i++) { 537 FrameInfo& frame = mFrames[i]; 538 if (frame[FrameInfoIndex::SyncStart] == 0) { 539 continue; 540 } 541 fprintf(file, "\n"); 542 for (int i = 0; i < static_cast<int>(FrameInfoIndex::NumIndexes); i++) { 543 fprintf(file, "%" PRId64 ",", frame[i]); 544 } 545 } 546 fprintf(file, "\n---PROFILEDATA---\n\n"); 547 fflush(file); 548} 549 550void CanvasContext::resetFrameStats() { 551 mFrames.clear(); 552 mRenderThread.jankTracker().reset(); 553} 554 555void CanvasContext::serializeDisplayListTree() { 556#if ENABLE_RENDERNODE_SERIALIZATION 557 using namespace google::protobuf::io; 558 char package[128]; 559 // Check whether tracing is enabled for this process. 560 FILE * file = fopen("/proc/self/cmdline", "r"); 561 if (file) { 562 if (!fgets(package, 128, file)) { 563 ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno); 564 fclose(file); 565 return; 566 } 567 fclose(file); 568 } else { 569 ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno), 570 errno); 571 return; 572 } 573 char path[1024]; 574 snprintf(path, 1024, "/data/data/%s/cache/rendertree_dump", package); 575 int fd = open(path, O_CREAT | O_WRONLY, S_IRWXU | S_IRGRP | S_IROTH); 576 if (fd == -1) { 577 ALOGD("Failed to open '%s'", path); 578 return; 579 } 580 proto::RenderNode tree; 581 // TODO: Streaming writes? 582 mRootRenderNode->copyTo(&tree); 583 std::string data = tree.SerializeAsString(); 584 write(fd, data.c_str(), data.length()); 585 close(fd); 586#endif 587} 588 589void CanvasContext::waitOnFences() { 590 if (mFrameFences.size()) { 591 ATRACE_CALL(); 592 for (auto& fence : mFrameFences) { 593 fence->getResult(); 594 } 595 mFrameFences.clear(); 596 } 597} 598 599class CanvasContext::FuncTaskProcessor : public TaskProcessor<bool> { 600public: 601 explicit FuncTaskProcessor(TaskManager* taskManager) 602 : TaskProcessor<bool>(taskManager) {} 603 604 virtual void onProcess(const sp<Task<bool> >& task) override { 605 FuncTask* t = static_cast<FuncTask*>(task.get()); 606 t->func(); 607 task->setResult(true); 608 } 609}; 610 611void CanvasContext::enqueueFrameWork(std::function<void()>&& func) { 612 if (!mFrameWorkProcessor.get()) { 613 mFrameWorkProcessor = new FuncTaskProcessor(mRenderPipeline->getTaskManager()); 614 } 615 sp<FuncTask> task(new FuncTask()); 616 task->func = func; 617 mFrameFences.push_back(task); 618 mFrameWorkProcessor->add(task); 619} 620 621int64_t CanvasContext::getFrameNumber() { 622 // mFrameNumber is reset to -1 when the surface changes or we swap buffers 623 if (mFrameNumber == -1 && mNativeSurface.get()) { 624 mFrameNumber = static_cast<int64_t>(mNativeSurface->getNextFrameNumber()); 625 } 626 return mFrameNumber; 627} 628 629SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { 630 if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) { 631 // can't rely on prior content of window if viewport size changes 632 dirty->setEmpty(); 633 mLastFrameWidth = frame.width(); 634 mLastFrameHeight = frame.height(); 635 } else if (mHaveNewSurface || frame.bufferAge() == 0) { 636 // New surface needs a full draw 637 dirty->setEmpty(); 638 } else { 639 if (!dirty->isEmpty() && !dirty->intersect(0, 0, frame.width(), frame.height())) { 640 ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", 641 SK_RECT_ARGS(*dirty), frame.width(), frame.height()); 642 dirty->setEmpty(); 643 } 644 profiler().unionDirty(dirty); 645 } 646 647 if (dirty->isEmpty()) { 648 dirty->set(0, 0, frame.width(), frame.height()); 649 } 650 651 // At this point dirty is the area of the window to update. However, 652 // the area of the frame we need to repaint is potentially different, so 653 // stash the screen area for later 654 SkRect windowDirty(*dirty); 655 656 // If the buffer age is 0 we do a full-screen repaint (handled above) 657 // If the buffer age is 1 the buffer contents are the same as they were 658 // last frame so there's nothing to union() against 659 // Therefore we only care about the > 1 case. 660 if (frame.bufferAge() > 1) { 661 if (frame.bufferAge() > (int) mSwapHistory.size()) { 662 // We don't have enough history to handle this old of a buffer 663 // Just do a full-draw 664 dirty->set(0, 0, frame.width(), frame.height()); 665 } else { 666 // At this point we haven't yet added the latest frame 667 // to the damage history (happens below) 668 // So we need to damage 669 for (int i = mSwapHistory.size() - 1; 670 i > ((int) mSwapHistory.size()) - frame.bufferAge(); i--) { 671 dirty->join(mSwapHistory[i].damage); 672 } 673 } 674 } 675 676 return windowDirty; 677} 678 679} /* namespace renderthread */ 680} /* namespace uirenderer */ 681} /* namespace android */ 682