CanvasContext.cpp revision 64e445bf74bee2098781d608cedfd723d8cc88d3
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 "CanvasContext.h" 18 19#include "AnimationContext.h" 20#include "Caches.h" 21#include "DeferredLayerUpdater.h" 22#include "EglManager.h" 23#include "LayerRenderer.h" 24#include "OpenGLRenderer.h" 25#include "Properties.h" 26#include "RenderThread.h" 27#include "renderstate/RenderState.h" 28#include "renderstate/Stencil.h" 29#include "protos/hwui.pb.h" 30 31#include <cutils/properties.h> 32#include <google/protobuf/io/zero_copy_stream_impl.h> 33#include <private/hwui/DrawGlInfo.h> 34#include <strings.h> 35 36#include <algorithm> 37#include <fcntl.h> 38#include <sys/stat.h> 39 40#define TRIM_MEMORY_COMPLETE 80 41#define TRIM_MEMORY_UI_HIDDEN 20 42 43#define ENABLE_RENDERNODE_SERIALIZATION false 44 45#define LOG_FRAMETIME_MMA 0 46 47#if LOG_FRAMETIME_MMA 48static float sBenchMma = 0; 49static int sFrameCount = 0; 50static const float NANOS_PER_MILLIS_F = 1000000.0f; 51#endif 52 53namespace android { 54namespace uirenderer { 55namespace renderthread { 56 57CanvasContext::CanvasContext(RenderThread& thread, bool translucent, 58 RenderNode* rootRenderNode, IContextFactory* contextFactory) 59 : mRenderThread(thread) 60 , mEglManager(thread.eglManager()) 61 , mOpaque(!translucent) 62 , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord())) 63 , mRootRenderNode(rootRenderNode) 64 , mJankTracker(thread.timeLord().frameIntervalNanos()) 65 , mProfiler(mFrames) { 66 mRenderThread.renderState().registerCanvasContext(this); 67 mProfiler.setDensity(mRenderThread.mainDisplayInfo().density); 68} 69 70CanvasContext::~CanvasContext() { 71 destroy(); 72 mRenderThread.renderState().unregisterCanvasContext(this); 73} 74 75void CanvasContext::destroy() { 76 stopDrawing(); 77 setSurface(nullptr); 78 freePrefetechedLayers(); 79 destroyHardwareResources(); 80 mAnimationContext->destroy(); 81 if (mCanvas) { 82 delete mCanvas; 83 mCanvas = nullptr; 84 } 85} 86 87void CanvasContext::setSurface(ANativeWindow* window) { 88 ATRACE_CALL(); 89 90 mNativeWindow = window; 91 92 if (mEglSurface != EGL_NO_SURFACE) { 93 mEglManager.destroySurface(mEglSurface); 94 mEglSurface = EGL_NO_SURFACE; 95 } 96 97 if (window) { 98 mEglSurface = mEglManager.createSurface(window); 99 } 100 101 if (mEglSurface != EGL_NO_SURFACE) { 102 const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer); 103 mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); 104 mHaveNewSurface = true; 105 makeCurrent(); 106 } else { 107 mRenderThread.removeFrameCallback(this); 108 } 109} 110 111void CanvasContext::requireSurface() { 112 LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, 113 "requireSurface() called but no surface set!"); 114 makeCurrent(); 115} 116 117void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) { 118 mSwapBehavior = swapBehavior; 119} 120 121bool CanvasContext::initialize(ANativeWindow* window) { 122 setSurface(window); 123 if (mCanvas) return false; 124 mCanvas = new OpenGLRenderer(mRenderThread.renderState()); 125 mCanvas->initProperties(); 126 return true; 127} 128 129void CanvasContext::updateSurface(ANativeWindow* window) { 130 setSurface(window); 131} 132 133bool CanvasContext::pauseSurface(ANativeWindow* window) { 134 return mRenderThread.removeFrameCallback(this); 135} 136 137// TODO: don't pass viewport size, it's automatic via EGL 138void CanvasContext::setup(int width, int height, float lightRadius, 139 uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { 140 if (!mCanvas) return; 141 mCanvas->initLight(lightRadius, ambientShadowAlpha, spotShadowAlpha); 142} 143 144void CanvasContext::setLightCenter(const Vector3& lightCenter) { 145 if (!mCanvas) return; 146 mCanvas->setLightCenter(lightCenter); 147} 148 149void CanvasContext::setOpaque(bool opaque) { 150 mOpaque = opaque; 151} 152 153void CanvasContext::makeCurrent() { 154 // TODO: Figure out why this workaround is needed, see b/13913604 155 // In the meantime this matches the behavior of GLRenderer, so it is not a regression 156 EGLint error = 0; 157 mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface, &error); 158 if (error) { 159 setSurface(nullptr); 160 } 161} 162 163void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) { 164 bool success = layerUpdater->apply(); 165 LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!"); 166 if (layerUpdater->backingLayer()->deferredUpdateScheduled) { 167 mCanvas->pushLayerUpdate(layerUpdater->backingLayer()); 168 } 169} 170 171static bool wasSkipped(FrameInfo* info) { 172 return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame); 173} 174 175void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued) { 176 mRenderThread.removeFrameCallback(this); 177 178 // If the previous frame was dropped we don't need to hold onto it, so 179 // just keep using the previous frame's structure instead 180 if (!wasSkipped(mCurrentFrameInfo)) { 181 mCurrentFrameInfo = &mFrames.next(); 182 } 183 mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo); 184 mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued; 185 mCurrentFrameInfo->markSyncStart(); 186 187 info.damageAccumulator = &mDamageAccumulator; 188 info.renderer = mCanvas; 189 info.canvasContext = this; 190 191 mAnimationContext->startFrame(info.mode); 192 mRootRenderNode->prepareTree(info); 193 mAnimationContext->runRemainingAnimations(info); 194 195 freePrefetechedLayers(); 196 197 if (CC_UNLIKELY(!mNativeWindow.get())) { 198 mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); 199 info.out.canDrawThisFrame = false; 200 return; 201 } 202 203 int runningBehind = 0; 204 // TODO: This query is moderately expensive, investigate adding some sort 205 // of fast-path based off when we last called eglSwapBuffers() as well as 206 // last vsync time. Or something. 207 mNativeWindow->query(mNativeWindow.get(), 208 NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind); 209 info.out.canDrawThisFrame = !runningBehind; 210 211 if (!info.out.canDrawThisFrame) { 212 mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); 213 } 214 215 if (info.out.hasAnimations || !info.out.canDrawThisFrame) { 216 if (!info.out.requiresUiRedraw) { 217 // If animationsNeedsRedraw is set don't bother posting for an RT anim 218 // as we will just end up fighting the UI thread. 219 mRenderThread.postFrameCallback(this); 220 } 221 } 222} 223 224void CanvasContext::stopDrawing() { 225 mRenderThread.removeFrameCallback(this); 226} 227 228void CanvasContext::notifyFramePending() { 229 ATRACE_CALL(); 230 mRenderThread.pushBackFrameCallback(this); 231} 232 233void CanvasContext::draw() { 234 LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, 235 "drawRenderNode called on a context with no canvas or surface!"); 236 237 SkRect dirty; 238 mDamageAccumulator.finish(&dirty); 239 240 // TODO: Re-enable after figuring out cause of b/22592975 241// if (dirty.isEmpty() && Properties::skipEmptyFrames) { 242// mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); 243// return; 244// } 245 246 mCurrentFrameInfo->markIssueDrawCommandsStart(); 247 248 Frame frame = mEglManager.beginFrame(mEglSurface); 249 if (frame.width() != mCanvas->getViewportWidth() 250 || frame.height() != mCanvas->getViewportHeight()) { 251 // can't rely on prior content of window if viewport size changes 252 dirty.setEmpty(); 253 } else if (mHaveNewSurface || frame.bufferAge() == 0) { 254 // New surface needs a full draw 255 dirty.setEmpty(); 256 } else { 257 if (!dirty.isEmpty() && !dirty.intersect(0, 0, frame.width(), frame.height())) { 258 ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", 259 SK_RECT_ARGS(dirty), frame.width(), frame.height()); 260 dirty.setEmpty(); 261 } 262 profiler().unionDirty(&dirty); 263 } 264 265 if (dirty.isEmpty()) { 266 dirty.set(0, 0, frame.width(), frame.height()); 267 } 268 269 // At this point dirty is the area of the screen to update. However, 270 // the area of the frame we need to repaint is potentially different, so 271 // stash the screen area for later 272 SkRect screenDirty(dirty); 273 274 // If the buffer age is 0 we do a full-screen repaint (handled above) 275 // If the buffer age is 1 the buffer contents are the same as they were 276 // last frame so there's nothing to union() against 277 // Therefore we only care about the > 1 case. 278 if (frame.bufferAge() > 1) { 279 if (frame.bufferAge() > (int) mDamageHistory.size()) { 280 // We don't have enough history to handle this old of a buffer 281 // Just do a full-draw 282 dirty.set(0, 0, frame.width(), frame.height()); 283 } else { 284 // At this point we haven't yet added the latest frame 285 // to the damage history (happens below) 286 // So we need to damage 287 for (int i = mDamageHistory.size() - 1; 288 i > ((int) mDamageHistory.size()) - frame.bufferAge(); i--) { 289 dirty.join(mDamageHistory[i]); 290 } 291 } 292 } 293 294 // Add the screen damage to the ring buffer. 295 mDamageHistory.next() = screenDirty; 296 297 mEglManager.damageFrame(frame, dirty); 298 mCanvas->prepareDirty(frame.width(), frame.height(), 299 dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque); 300 301 Rect outBounds; 302 mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds); 303 304 profiler().draw(mCanvas); 305 306 bool drew = mCanvas->finish(); 307 308 // Even if we decided to cancel the frame, from the perspective of jank 309 // metrics the frame was swapped at this point 310 mCurrentFrameInfo->markSwapBuffers(); 311 312 if (drew) { 313 if (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty))) { 314 setSurface(nullptr); 315 } 316 mHaveNewSurface = false; 317 } 318 319 // TODO: Use a fence for real completion? 320 mCurrentFrameInfo->markFrameCompleted(); 321 322#if LOG_FRAMETIME_MMA 323 float thisFrame = mCurrentFrameInfo->duration( 324 FrameInfoIndex::IssueDrawCommandsStart, 325 FrameInfoIndex::FrameCompleted) / NANOS_PER_MILLIS_F; 326 if (sFrameCount) { 327 sBenchMma = ((9 * sBenchMma) + thisFrame) / 10; 328 } else { 329 sBenchMma = thisFrame; 330 } 331 if (++sFrameCount == 10) { 332 sFrameCount = 1; 333 ALOGD("Average frame time: %.4f", sBenchMma); 334 } 335#endif 336 337 mJankTracker.addFrame(*mCurrentFrameInfo); 338 mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo); 339} 340 341// Called by choreographer to do an RT-driven animation 342void CanvasContext::doFrame() { 343 if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) { 344 return; 345 } 346 347 ATRACE_CALL(); 348 349 int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE]; 350 UiFrameInfoBuilder(frameInfo) 351 .addFlag(FrameInfoFlags::RTAnimation) 352 .setVsync(mRenderThread.timeLord().computeFrameTimeNanos(), 353 mRenderThread.timeLord().latestVsync()); 354 355 TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState()); 356 prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC)); 357 if (info.out.canDrawThisFrame) { 358 draw(); 359 } 360} 361 362void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) { 363 ATRACE_CALL(); 364 DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; 365 if (thread.eglManager().hasEglContext()) { 366 mode = DrawGlInfo::kModeProcess; 367 } 368 369 thread.renderState().invokeFunctor(functor, mode, nullptr); 370} 371 372void CanvasContext::markLayerInUse(RenderNode* node) { 373 if (mPrefetechedLayers.erase(node)) { 374 node->decStrong(nullptr); 375 } 376} 377 378static void destroyPrefetechedNode(RenderNode* node) { 379 ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...", node->getName()); 380 node->destroyHardwareResources(); 381 node->decStrong(nullptr); 382} 383 384void CanvasContext::freePrefetechedLayers() { 385 if (mPrefetechedLayers.size()) { 386 std::for_each(mPrefetechedLayers.begin(), mPrefetechedLayers.end(), destroyPrefetechedNode); 387 mPrefetechedLayers.clear(); 388 } 389} 390 391void CanvasContext::buildLayer(RenderNode* node) { 392 ATRACE_CALL(); 393 if (!mEglManager.hasEglContext() || !mCanvas) { 394 return; 395 } 396 // buildLayer() will leave the tree in an unknown state, so we must stop drawing 397 stopDrawing(); 398 399 TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState()); 400 info.damageAccumulator = &mDamageAccumulator; 401 info.renderer = mCanvas; 402 info.runAnimations = false; 403 node->prepareTree(info); 404 SkRect ignore; 405 mDamageAccumulator.finish(&ignore); 406 // Tickle the GENERIC property on node to mark it as dirty for damaging 407 // purposes when the frame is actually drawn 408 node->setPropertyFieldsDirty(RenderNode::GENERIC); 409 410 mCanvas->markLayersAsBuildLayers(); 411 mCanvas->flushLayerUpdates(); 412 413 node->incStrong(nullptr); 414 mPrefetechedLayers.insert(node); 415} 416 417bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { 418 layer->apply(); 419 return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap); 420} 421 422void CanvasContext::destroyHardwareResources() { 423 stopDrawing(); 424 if (mEglManager.hasEglContext()) { 425 freePrefetechedLayers(); 426 mRootRenderNode->destroyHardwareResources(); 427 Caches& caches = Caches::getInstance(); 428 // Make sure to release all the textures we were owning as there won't 429 // be another draw 430 caches.textureCache.resetMarkInUse(this); 431 caches.flush(Caches::FlushMode::Layers); 432 } 433} 434 435void CanvasContext::trimMemory(RenderThread& thread, int level) { 436 // No context means nothing to free 437 if (!thread.eglManager().hasEglContext()) return; 438 439 ATRACE_CALL(); 440 if (level >= TRIM_MEMORY_COMPLETE) { 441 Caches::getInstance().flush(Caches::FlushMode::Full); 442 thread.eglManager().destroy(); 443 } else if (level >= TRIM_MEMORY_UI_HIDDEN) { 444 Caches::getInstance().flush(Caches::FlushMode::Moderate); 445 } 446} 447 448void CanvasContext::runWithGlContext(RenderTask* task) { 449 LOG_ALWAYS_FATAL_IF(!mEglManager.hasEglContext(), 450 "GL context not initialized!"); 451 task->run(); 452} 453 454Layer* CanvasContext::createTextureLayer() { 455 requireSurface(); 456 return LayerRenderer::createTextureLayer(mRenderThread.renderState()); 457} 458 459void CanvasContext::setTextureAtlas(RenderThread& thread, 460 const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) { 461 thread.eglManager().setTextureAtlas(buffer, map, mapSize); 462} 463 464void CanvasContext::dumpFrames(int fd) { 465 FILE* file = fdopen(fd, "a"); 466 fprintf(file, "\n\n---PROFILEDATA---\n"); 467 for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) { 468 fprintf(file, "%s", FrameInfoNames[i].c_str()); 469 fprintf(file, ","); 470 } 471 for (size_t i = 0; i < mFrames.size(); i++) { 472 FrameInfo& frame = mFrames[i]; 473 if (frame[FrameInfoIndex::SyncStart] == 0) { 474 continue; 475 } 476 fprintf(file, "\n"); 477 for (int i = 0; i < static_cast<int>(FrameInfoIndex::NumIndexes); i++) { 478 fprintf(file, "%" PRId64 ",", frame[i]); 479 } 480 } 481 fprintf(file, "\n---PROFILEDATA---\n\n"); 482 fflush(file); 483} 484 485void CanvasContext::resetFrameStats() { 486 mFrames.clear(); 487 mRenderThread.jankTracker().reset(); 488} 489 490void CanvasContext::serializeDisplayListTree() { 491#if ENABLE_RENDERNODE_SERIALIZATION 492 using namespace google::protobuf::io; 493 char package[128]; 494 // Check whether tracing is enabled for this process. 495 FILE * file = fopen("/proc/self/cmdline", "r"); 496 if (file) { 497 if (!fgets(package, 128, file)) { 498 ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno); 499 fclose(file); 500 return; 501 } 502 fclose(file); 503 } else { 504 ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno), 505 errno); 506 return; 507 } 508 char path[1024]; 509 snprintf(path, 1024, "/data/data/%s/cache/rendertree_dump", package); 510 int fd = open(path, O_CREAT | O_WRONLY, S_IRWXU | S_IRGRP | S_IROTH); 511 if (fd == -1) { 512 ALOGD("Failed to open '%s'", path); 513 return; 514 } 515 proto::RenderNode tree; 516 // TODO: Streaming writes? 517 mRootRenderNode->copyTo(&tree); 518 std::string data = tree.SerializeAsString(); 519 write(fd, data.c_str(), data.length()); 520 close(fd); 521#endif 522} 523 524} /* namespace renderthread */ 525} /* namespace uirenderer */ 526} /* namespace android */ 527