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