CanvasContext.cpp revision 471a63ee35f0be76cd7c2bc9a38889b9d2062f41
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 } else { 437 // wait on fences so tasks don't overlap next frame 438 waitOnFences(); 439 } 440} 441 442void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) { 443 ATRACE_CALL(); 444 DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; 445 if (thread.eglManager().hasEglContext()) { 446 mode = DrawGlInfo::kModeProcess; 447 } 448 449 thread.renderState().invokeFunctor(functor, mode, nullptr); 450} 451 452void CanvasContext::markLayerInUse(RenderNode* node) { 453 if (mPrefetchedLayers.erase(node)) { 454 node->decStrong(nullptr); 455 } 456} 457 458void CanvasContext::freePrefetchedLayers(TreeObserver* observer) { 459 if (mPrefetchedLayers.size()) { 460 for (auto& node : mPrefetchedLayers) { 461 ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...", 462 node->getName()); 463 node->destroyHardwareResources(observer); 464 node->decStrong(observer); 465 } 466 mPrefetchedLayers.clear(); 467 } 468} 469 470void CanvasContext::buildLayer(RenderNode* node, TreeObserver* observer) { 471 ATRACE_CALL(); 472 if (!mRenderPipeline->isContextReady()) return; 473 474 // buildLayer() will leave the tree in an unknown state, so we must stop drawing 475 stopDrawing(); 476 477 TreeInfo info(TreeInfo::MODE_FULL, *this); 478 info.damageAccumulator = &mDamageAccumulator; 479 info.observer = observer; 480 info.layerUpdateQueue = &mLayerUpdateQueue; 481 info.runAnimations = false; 482 node->prepareTree(info); 483 SkRect ignore; 484 mDamageAccumulator.finish(&ignore); 485 // Tickle the GENERIC property on node to mark it as dirty for damaging 486 // purposes when the frame is actually drawn 487 node->setPropertyFieldsDirty(RenderNode::GENERIC); 488 489 mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue, mOpaque, mLightInfo); 490 491 node->incStrong(nullptr); 492 mPrefetchedLayers.insert(node); 493} 494 495bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { 496 return mRenderPipeline->copyLayerInto(layer, bitmap); 497} 498 499void CanvasContext::destroyHardwareResources(TreeObserver* observer) { 500 stopDrawing(); 501 if (mRenderPipeline->isContextReady()) { 502 freePrefetchedLayers(observer); 503 for (const sp<RenderNode>& node : mRenderNodes) { 504 node->destroyHardwareResources(observer); 505 } 506 Caches& caches = Caches::getInstance(); 507 // Make sure to release all the textures we were owning as there won't 508 // be another draw 509 caches.textureCache.resetMarkInUse(this); 510 mRenderPipeline->onDestroyHardwareResources(); 511 } 512} 513 514void CanvasContext::trimMemory(RenderThread& thread, int level) { 515 // No context means nothing to free 516 if (!thread.eglManager().hasEglContext()) return; 517 518 ATRACE_CALL(); 519 if (level >= TRIM_MEMORY_COMPLETE) { 520 thread.renderState().flush(Caches::FlushMode::Full); 521 thread.eglManager().destroy(); 522 } else if (level >= TRIM_MEMORY_UI_HIDDEN) { 523 thread.renderState().flush(Caches::FlushMode::Moderate); 524 } 525} 526 527DeferredLayerUpdater* CanvasContext::createTextureLayer() { 528 return mRenderPipeline->createTextureLayer(); 529} 530 531void CanvasContext::setTextureAtlas(RenderThread& thread, 532 const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) { 533 thread.eglManager().setTextureAtlas(buffer, map, mapSize); 534} 535 536void CanvasContext::dumpFrames(int fd) { 537 FILE* file = fdopen(fd, "a"); 538 fprintf(file, "\n\n---PROFILEDATA---\n"); 539 for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) { 540 fprintf(file, "%s", FrameInfoNames[i].c_str()); 541 fprintf(file, ","); 542 } 543 for (size_t i = 0; i < mFrames.size(); i++) { 544 FrameInfo& frame = mFrames[i]; 545 if (frame[FrameInfoIndex::SyncStart] == 0) { 546 continue; 547 } 548 fprintf(file, "\n"); 549 for (int i = 0; i < static_cast<int>(FrameInfoIndex::NumIndexes); i++) { 550 fprintf(file, "%" PRId64 ",", frame[i]); 551 } 552 } 553 fprintf(file, "\n---PROFILEDATA---\n\n"); 554 fflush(file); 555} 556 557void CanvasContext::resetFrameStats() { 558 mFrames.clear(); 559 mRenderThread.jankTracker().reset(); 560} 561 562void CanvasContext::serializeDisplayListTree() { 563#if ENABLE_RENDERNODE_SERIALIZATION 564 using namespace google::protobuf::io; 565 char package[128]; 566 // Check whether tracing is enabled for this process. 567 FILE * file = fopen("/proc/self/cmdline", "r"); 568 if (file) { 569 if (!fgets(package, 128, file)) { 570 ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno); 571 fclose(file); 572 return; 573 } 574 fclose(file); 575 } else { 576 ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno), 577 errno); 578 return; 579 } 580 char path[1024]; 581 snprintf(path, 1024, "/data/data/%s/cache/rendertree_dump", package); 582 int fd = open(path, O_CREAT | O_WRONLY, S_IRWXU | S_IRGRP | S_IROTH); 583 if (fd == -1) { 584 ALOGD("Failed to open '%s'", path); 585 return; 586 } 587 proto::RenderNode tree; 588 // TODO: Streaming writes? 589 mRootRenderNode->copyTo(&tree); 590 std::string data = tree.SerializeAsString(); 591 write(fd, data.c_str(), data.length()); 592 close(fd); 593#endif 594} 595 596void CanvasContext::waitOnFences() { 597 if (mFrameFences.size()) { 598 ATRACE_CALL(); 599 for (auto& fence : mFrameFences) { 600 fence->getResult(); 601 } 602 mFrameFences.clear(); 603 } 604} 605 606class CanvasContext::FuncTaskProcessor : public TaskProcessor<bool> { 607public: 608 explicit FuncTaskProcessor(TaskManager* taskManager) 609 : TaskProcessor<bool>(taskManager) {} 610 611 virtual void onProcess(const sp<Task<bool> >& task) override { 612 FuncTask* t = static_cast<FuncTask*>(task.get()); 613 t->func(); 614 task->setResult(true); 615 } 616}; 617 618void CanvasContext::enqueueFrameWork(std::function<void()>&& func) { 619 if (!mFrameWorkProcessor.get()) { 620 mFrameWorkProcessor = new FuncTaskProcessor(mRenderPipeline->getTaskManager()); 621 } 622 sp<FuncTask> task(new FuncTask()); 623 task->func = func; 624 mFrameFences.push_back(task); 625 mFrameWorkProcessor->add(task); 626} 627 628int64_t CanvasContext::getFrameNumber() { 629 // mFrameNumber is reset to -1 when the surface changes or we swap buffers 630 if (mFrameNumber == -1 && mNativeSurface.get()) { 631 mFrameNumber = static_cast<int64_t>(mNativeSurface->getNextFrameNumber()); 632 } 633 return mFrameNumber; 634} 635 636SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { 637 if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) { 638 // can't rely on prior content of window if viewport size changes 639 dirty->setEmpty(); 640 mLastFrameWidth = frame.width(); 641 mLastFrameHeight = frame.height(); 642 } else if (mHaveNewSurface || frame.bufferAge() == 0) { 643 // New surface needs a full draw 644 dirty->setEmpty(); 645 } else { 646 if (!dirty->isEmpty() && !dirty->intersect(0, 0, frame.width(), frame.height())) { 647 ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", 648 SK_RECT_ARGS(*dirty), frame.width(), frame.height()); 649 dirty->setEmpty(); 650 } 651 profiler().unionDirty(dirty); 652 } 653 654 if (dirty->isEmpty()) { 655 dirty->set(0, 0, frame.width(), frame.height()); 656 } 657 658 // At this point dirty is the area of the window to update. However, 659 // the area of the frame we need to repaint is potentially different, so 660 // stash the screen area for later 661 SkRect windowDirty(*dirty); 662 663 // If the buffer age is 0 we do a full-screen repaint (handled above) 664 // If the buffer age is 1 the buffer contents are the same as they were 665 // last frame so there's nothing to union() against 666 // Therefore we only care about the > 1 case. 667 if (frame.bufferAge() > 1) { 668 if (frame.bufferAge() > (int) mSwapHistory.size()) { 669 // We don't have enough history to handle this old of a buffer 670 // Just do a full-draw 671 dirty->set(0, 0, frame.width(), frame.height()); 672 } else { 673 // At this point we haven't yet added the latest frame 674 // to the damage history (happens below) 675 // So we need to damage 676 for (int i = mSwapHistory.size() - 1; 677 i > ((int) mSwapHistory.size()) - frame.bufferAge(); i--) { 678 dirty->join(mSwapHistory[i].damage); 679 } 680 } 681 } 682 683 return windowDirty; 684} 685 686} /* namespace renderthread */ 687} /* namespace uirenderer */ 688} /* namespace android */ 689