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 "DrawFrameTask.h"
     18 
     19 #include <utils/Log.h>
     20 #include <utils/Trace.h>
     21 
     22 #include "../DeferredLayerUpdater.h"
     23 #include "../DisplayList.h"
     24 #include "../RenderNode.h"
     25 #include "CanvasContext.h"
     26 #include "RenderThread.h"
     27 
     28 namespace android {
     29 namespace uirenderer {
     30 namespace renderthread {
     31 
     32 DrawFrameTask::DrawFrameTask()
     33         : mRenderThread(nullptr)
     34         , mContext(nullptr)
     35         , mContentDrawBounds(0, 0, 0, 0)
     36         , mSyncResult(SyncResult::OK) {}
     37 
     38 DrawFrameTask::~DrawFrameTask() {}
     39 
     40 void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context,
     41                                RenderNode* targetNode) {
     42     mRenderThread = thread;
     43     mContext = context;
     44     mTargetNode = targetNode;
     45 }
     46 
     47 void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
     48     LOG_ALWAYS_FATAL_IF(!mContext,
     49                         "Lifecycle violation, there's no context to pushLayerUpdate with!");
     50 
     51     for (size_t i = 0; i < mLayers.size(); i++) {
     52         if (mLayers[i].get() == layer) {
     53             return;
     54         }
     55     }
     56     mLayers.push_back(layer);
     57 }
     58 
     59 void DrawFrameTask::removeLayerUpdate(DeferredLayerUpdater* layer) {
     60     for (size_t i = 0; i < mLayers.size(); i++) {
     61         if (mLayers[i].get() == layer) {
     62             mLayers.erase(mLayers.begin() + i);
     63             return;
     64         }
     65     }
     66 }
     67 
     68 int DrawFrameTask::drawFrame() {
     69     LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
     70 
     71     mSyncResult = SyncResult::OK;
     72     mSyncQueued = systemTime(CLOCK_MONOTONIC);
     73     postAndWait();
     74 
     75     return mSyncResult;
     76 }
     77 
     78 void DrawFrameTask::postAndWait() {
     79     AutoMutex _lock(mLock);
     80     mRenderThread->queue().post([this]() { run(); });
     81     mSignal.wait(mLock);
     82 }
     83 
     84 void DrawFrameTask::run() {
     85     ATRACE_NAME("DrawFrame");
     86 
     87     bool canUnblockUiThread;
     88     bool canDrawThisFrame;
     89     {
     90         TreeInfo info(TreeInfo::MODE_FULL, *mContext);
     91         canUnblockUiThread = syncFrameState(info);
     92         canDrawThisFrame = info.out.canDrawThisFrame;
     93 
     94         if (mFrameCompleteCallback) {
     95             mContext->addFrameCompleteListener(std::move(mFrameCompleteCallback));
     96             mFrameCompleteCallback = nullptr;
     97         }
     98     }
     99 
    100     // Grab a copy of everything we need
    101     CanvasContext* context = mContext;
    102     std::function<void(int64_t)> callback = std::move(mFrameCallback);
    103     mFrameCallback = nullptr;
    104 
    105     // From this point on anything in "this" is *UNSAFE TO ACCESS*
    106     if (canUnblockUiThread) {
    107         unblockUiThread();
    108     }
    109 
    110     // Even if we aren't drawing this vsync pulse the next frame number will still be accurate
    111     if (CC_UNLIKELY(callback)) {
    112         context->enqueueFrameWork([callback, frameNr = context->getFrameNumber()]() {
    113             callback(frameNr);
    114         });
    115     }
    116 
    117     if (CC_LIKELY(canDrawThisFrame)) {
    118         context->draw();
    119     } else {
    120         // wait on fences so tasks don't overlap next frame
    121         context->waitOnFences();
    122     }
    123 
    124     if (!canUnblockUiThread) {
    125         unblockUiThread();
    126     }
    127 }
    128 
    129 bool DrawFrameTask::syncFrameState(TreeInfo& info) {
    130     ATRACE_CALL();
    131     int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
    132     mRenderThread->timeLord().vsyncReceived(vsync);
    133     bool canDraw = mContext->makeCurrent();
    134     mContext->unpinImages();
    135 
    136     for (size_t i = 0; i < mLayers.size(); i++) {
    137         mLayers[i]->apply();
    138     }
    139     mLayers.clear();
    140     mContext->setContentDrawBounds(mContentDrawBounds);
    141     mContext->prepareTree(info, mFrameInfo, mSyncQueued, mTargetNode);
    142 
    143     // This is after the prepareTree so that any pending operations
    144     // (RenderNode tree state, prefetched layers, etc...) will be flushed.
    145     if (CC_UNLIKELY(!mContext->hasSurface() || !canDraw)) {
    146         if (!mContext->hasSurface()) {
    147             mSyncResult |= SyncResult::LostSurfaceRewardIfFound;
    148         } else {
    149             // If we have a surface but can't draw we must be stopped
    150             mSyncResult |= SyncResult::ContextIsStopped;
    151         }
    152         info.out.canDrawThisFrame = false;
    153     }
    154 
    155     if (info.out.hasAnimations) {
    156         if (info.out.requiresUiRedraw) {
    157             mSyncResult |= SyncResult::UIRedrawRequired;
    158         }
    159     }
    160     if (!info.out.canDrawThisFrame) {
    161         mSyncResult |= SyncResult::FrameDropped;
    162     }
    163     // If prepareTextures is false, we ran out of texture cache space
    164     return info.prepareTextures;
    165 }
    166 
    167 void DrawFrameTask::unblockUiThread() {
    168     AutoMutex _lock(mLock);
    169     mSignal.signal();
    170 }
    171 
    172 } /* namespace renderthread */
    173 } /* namespace uirenderer */
    174 } /* namespace android */
    175