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