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