CanvasContext.cpp revision edc524c90506d80e0fc5fb67e8de7b8f3ef53439
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 "EglManager.h" 20#include "RenderThread.h" 21#include "../AnimationContext.h" 22#include "../Caches.h" 23#include "../DeferredLayerUpdater.h" 24#include "../renderstate/RenderState.h" 25#include "../renderstate/Stencil.h" 26#include "../LayerRenderer.h" 27#include "../OpenGLRenderer.h" 28 29#include <algorithm> 30#include <private/hwui/DrawGlInfo.h> 31#include <strings.h> 32 33#define TRIM_MEMORY_COMPLETE 80 34#define TRIM_MEMORY_UI_HIDDEN 20 35 36namespace android { 37namespace uirenderer { 38namespace renderthread { 39 40CanvasContext::CanvasContext(RenderThread& thread, bool translucent, 41 RenderNode* rootRenderNode, IContextFactory* contextFactory) 42 : mRenderThread(thread) 43 , mEglManager(thread.eglManager()) 44 , mOpaque(!translucent) 45 , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord())) 46 , mRootRenderNode(rootRenderNode) 47 , mJankTracker(thread.timeLord().frameIntervalNanos()) { 48 mRenderThread.renderState().registerCanvasContext(this); 49 mProfiler.setDensity(mRenderThread.mainDisplayInfo().density); 50} 51 52CanvasContext::~CanvasContext() { 53 destroy(); 54 mRenderThread.renderState().unregisterCanvasContext(this); 55} 56 57void CanvasContext::destroy() { 58 stopDrawing(); 59 setSurface(nullptr); 60 freePrefetechedLayers(); 61 destroyHardwareResources(); 62 mAnimationContext->destroy(); 63 if (mCanvas) { 64 delete mCanvas; 65 mCanvas = nullptr; 66 } 67} 68 69void CanvasContext::setSurface(ANativeWindow* window) { 70 ATRACE_CALL(); 71 72 mNativeWindow = window; 73 74 if (mEglSurface != EGL_NO_SURFACE) { 75 mEglManager.destroySurface(mEglSurface); 76 mEglSurface = EGL_NO_SURFACE; 77 } 78 79 if (window) { 80 mEglSurface = mEglManager.createSurface(window); 81 } 82 83 if (mEglSurface != EGL_NO_SURFACE) { 84 const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer); 85 mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); 86 mHaveNewSurface = true; 87 makeCurrent(); 88 } else { 89 mRenderThread.removeFrameCallback(this); 90 } 91} 92 93void CanvasContext::swapBuffers() { 94 if (CC_UNLIKELY(!mEglManager.swapBuffers(mEglSurface))) { 95 setSurface(nullptr); 96 } 97 mHaveNewSurface = false; 98} 99 100void CanvasContext::requireSurface() { 101 LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, 102 "requireSurface() called but no surface set!"); 103 makeCurrent(); 104} 105 106void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) { 107 mSwapBehavior = swapBehavior; 108} 109 110bool CanvasContext::initialize(ANativeWindow* window) { 111 setSurface(window); 112 if (mCanvas) return false; 113 mCanvas = new OpenGLRenderer(mRenderThread.renderState()); 114 mCanvas->initProperties(); 115 return true; 116} 117 118void CanvasContext::updateSurface(ANativeWindow* window) { 119 setSurface(window); 120} 121 122bool CanvasContext::pauseSurface(ANativeWindow* window) { 123 return mRenderThread.removeFrameCallback(this); 124} 125 126// TODO: don't pass viewport size, it's automatic via EGL 127void CanvasContext::setup(int width, int height, const Vector3& lightCenter, float lightRadius, 128 uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { 129 if (!mCanvas) return; 130 mCanvas->initLight(lightCenter, lightRadius, ambientShadowAlpha, spotShadowAlpha); 131} 132 133void CanvasContext::setOpaque(bool opaque) { 134 mOpaque = opaque; 135} 136 137void CanvasContext::makeCurrent() { 138 // TODO: Figure out why this workaround is needed, see b/13913604 139 // In the meantime this matches the behavior of GLRenderer, so it is not a regression 140 mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface); 141} 142 143void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) { 144 bool success = layerUpdater->apply(); 145 LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!"); 146 if (layerUpdater->backingLayer()->deferredUpdateScheduled) { 147 mCanvas->pushLayerUpdate(layerUpdater->backingLayer()); 148 } 149} 150 151void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo) { 152 mRenderThread.removeFrameCallback(this); 153 154 mCurrentFrameInfo = &mFrames.next(); 155 mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo); 156 mCurrentFrameInfo->markSyncStart(); 157 158 info.damageAccumulator = &mDamageAccumulator; 159 info.renderer = mCanvas; 160 if (mPrefetechedLayers.size() && info.mode == TreeInfo::MODE_FULL) { 161 info.canvasContext = this; 162 } 163 mAnimationContext->startFrame(info.mode); 164 mRootRenderNode->prepareTree(info); 165 mAnimationContext->runRemainingAnimations(info); 166 167 if (info.canvasContext) { 168 freePrefetechedLayers(); 169 } 170 171 if (CC_UNLIKELY(!mNativeWindow.get())) { 172 info.out.canDrawThisFrame = false; 173 return; 174 } 175 176 int runningBehind = 0; 177 // TODO: This query is moderately expensive, investigate adding some sort 178 // of fast-path based off when we last called eglSwapBuffers() as well as 179 // last vsync time. Or something. 180 mNativeWindow->query(mNativeWindow.get(), 181 NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind); 182 info.out.canDrawThisFrame = !runningBehind; 183 184 if (info.out.hasAnimations || !info.out.canDrawThisFrame) { 185 if (!info.out.requiresUiRedraw) { 186 // If animationsNeedsRedraw is set don't bother posting for an RT anim 187 // as we will just end up fighting the UI thread. 188 mRenderThread.postFrameCallback(this); 189 } 190 } 191} 192 193void CanvasContext::stopDrawing() { 194 mRenderThread.removeFrameCallback(this); 195} 196 197void CanvasContext::notifyFramePending() { 198 ATRACE_CALL(); 199 mRenderThread.pushBackFrameCallback(this); 200} 201 202void CanvasContext::draw() { 203 LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, 204 "drawRenderNode called on a context with no canvas or surface!"); 205 206 profiler().markPlaybackStart(); 207 mCurrentFrameInfo->markIssueDrawCommandsStart(); 208 209 SkRect dirty; 210 mDamageAccumulator.finish(&dirty); 211 212 EGLint width, height; 213 mEglManager.beginFrame(mEglSurface, &width, &height); 214 if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { 215 mCanvas->setViewport(width, height); 216 dirty.setEmpty(); 217 } else if (!mBufferPreserved || mHaveNewSurface) { 218 dirty.setEmpty(); 219 } else { 220 if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) { 221 ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", 222 SK_RECT_ARGS(dirty), width, height); 223 dirty.setEmpty(); 224 } 225 profiler().unionDirty(&dirty); 226 } 227 228 if (!dirty.isEmpty()) { 229 mCanvas->prepareDirty(dirty.fLeft, dirty.fTop, 230 dirty.fRight, dirty.fBottom, mOpaque); 231 } else { 232 mCanvas->prepare(mOpaque); 233 } 234 235 Rect outBounds; 236 mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds); 237 238 profiler().draw(mCanvas); 239 240 bool drew = mCanvas->finish(); 241 242 profiler().markPlaybackEnd(); 243 244 // Even if we decided to cancel the frame, from the perspective of jank 245 // metrics the frame was swapped at this point 246 mCurrentFrameInfo->markSwapBuffers(); 247 248 if (drew) { 249 swapBuffers(); 250 } else { 251 mEglManager.cancelFrame(); 252 } 253 254 // TODO: Use a fence for real completion? 255 mCurrentFrameInfo->markFrameCompleted(); 256 mJankTracker.addFrame(*mCurrentFrameInfo); 257 mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo); 258 profiler().finishFrame(); 259} 260 261// Called by choreographer to do an RT-driven animation 262void CanvasContext::doFrame() { 263 if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) { 264 return; 265 } 266 267 ATRACE_CALL(); 268 269 profiler().startFrame(); 270 int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE]; 271 UiFrameInfoBuilder(frameInfo) 272 .addFlag(FrameInfoFlags::kRTAnimation) 273 .setVsync(mRenderThread.timeLord().computeFrameTimeNanos(), 274 mRenderThread.timeLord().latestVsync()); 275 276 TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState()); 277 prepareTree(info, frameInfo); 278 if (info.out.canDrawThisFrame) { 279 draw(); 280 } 281} 282 283void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) { 284 ATRACE_CALL(); 285 DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; 286 if (thread.eglManager().hasEglContext()) { 287 thread.eglManager().requireGlContext(); 288 mode = DrawGlInfo::kModeProcess; 289 } 290 291 thread.renderState().invokeFunctor(functor, mode, nullptr); 292} 293 294void CanvasContext::markLayerInUse(RenderNode* node) { 295 if (mPrefetechedLayers.erase(node)) { 296 node->decStrong(nullptr); 297 } 298} 299 300static void destroyPrefetechedNode(RenderNode* node) { 301 ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...", node->getName()); 302 node->destroyHardwareResources(); 303 node->decStrong(nullptr); 304} 305 306void CanvasContext::freePrefetechedLayers() { 307 if (mPrefetechedLayers.size()) { 308 requireGlContext(); 309 std::for_each(mPrefetechedLayers.begin(), mPrefetechedLayers.end(), destroyPrefetechedNode); 310 mPrefetechedLayers.clear(); 311 } 312} 313 314void CanvasContext::buildLayer(RenderNode* node) { 315 ATRACE_CALL(); 316 if (!mEglManager.hasEglContext() || !mCanvas) { 317 return; 318 } 319 requireGlContext(); 320 // buildLayer() will leave the tree in an unknown state, so we must stop drawing 321 stopDrawing(); 322 323 TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState()); 324 info.damageAccumulator = &mDamageAccumulator; 325 info.renderer = mCanvas; 326 info.runAnimations = false; 327 node->prepareTree(info); 328 SkRect ignore; 329 mDamageAccumulator.finish(&ignore); 330 // Tickle the GENERIC property on node to mark it as dirty for damaging 331 // purposes when the frame is actually drawn 332 node->setPropertyFieldsDirty(RenderNode::GENERIC); 333 334 mCanvas->markLayersAsBuildLayers(); 335 mCanvas->flushLayerUpdates(); 336 337 node->incStrong(nullptr); 338 mPrefetechedLayers.insert(node); 339} 340 341bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { 342 requireGlContext(); 343 layer->apply(); 344 return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap); 345} 346 347void CanvasContext::destroyHardwareResources() { 348 stopDrawing(); 349 if (mEglManager.hasEglContext()) { 350 requireGlContext(); 351 freePrefetechedLayers(); 352 mRootRenderNode->destroyHardwareResources(); 353 Caches::getInstance().flush(Caches::kFlushMode_Layers); 354 } 355} 356 357void CanvasContext::trimMemory(RenderThread& thread, int level) { 358 // No context means nothing to free 359 if (!thread.eglManager().hasEglContext()) return; 360 361 ATRACE_CALL(); 362 thread.eglManager().requireGlContext(); 363 if (level >= TRIM_MEMORY_COMPLETE) { 364 Caches::getInstance().flush(Caches::kFlushMode_Full); 365 thread.eglManager().destroy(); 366 } else if (level >= TRIM_MEMORY_UI_HIDDEN) { 367 Caches::getInstance().flush(Caches::kFlushMode_Moderate); 368 } 369} 370 371void CanvasContext::runWithGlContext(RenderTask* task) { 372 requireGlContext(); 373 task->run(); 374} 375 376Layer* CanvasContext::createTextureLayer() { 377 requireSurface(); 378 return LayerRenderer::createTextureLayer(mRenderThread.renderState()); 379} 380 381void CanvasContext::requireGlContext() { 382 mEglManager.requireGlContext(); 383} 384 385void CanvasContext::setTextureAtlas(RenderThread& thread, 386 const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) { 387 thread.eglManager().setTextureAtlas(buffer, map, mapSize); 388} 389 390void CanvasContext::dumpFrames(int fd) { 391 FILE* file = fdopen(fd, "a"); 392 fprintf(file, "\n\n---PROFILEDATA---"); 393 for (size_t i = 0; i < mFrames.size(); i++) { 394 FrameInfo& frame = mFrames[i]; 395 if (frame[FrameInfoIndex::kSyncStart] == 0) { 396 continue; 397 } 398 fprintf(file, "\n"); 399 for (int i = 0; i < static_cast<int>(FrameInfoIndex::kNumIndexes); i++) { 400 fprintf(file, "%" PRId64 ",", frame[i]); 401 } 402 } 403 fprintf(file, "\n---PROFILEDATA---\n\n"); 404 fflush(file); 405} 406 407void CanvasContext::resetFrameStats() { 408 mFrames.clear(); 409 mRenderThread.jankTracker().reset(); 410} 411 412} /* namespace renderthread */ 413} /* namespace uirenderer */ 414} /* namespace android */ 415