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