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