有buffer但不latch

frameIsEarly - buffer生成后不立即latch

正常情况下latchBuffers时会对每个有buffer的layer进行latchBuffer

但7707397 没有latchBuffer操作

latchBuffers

bool SurfaceFlinger::latchBuffers() {
    ATRACE_CALL();
    //...
        if (layer->hasReadyFrame()) {
            frameQueued = true;
            if (layer->shouldPresentNow(expectedPresentTime)) {
                // 准备好的layer会在下面进行latchBuffer操作
                mLayersWithQueuedFrames.emplace(layer);
            } else {
                ATRACE_NAME("!layer->shouldPresentNow()");
                layer->useEmptyDamage();
            }
        } else {
            layer->useEmptyDamage();
        }
    });
    // ...
    
    if (!mLayersWithQueuedFrames.empty()) {
        // mStateLock is needed for latchBuffer as LayerRejecter::reject()
        // writes to Layer current state. See also b/119481871
        Mutex::Autolock lock(mStateLock);
 
        for (const auto& layer : mLayersWithQueuedFrames) {
        // 对每个layer的latchBuffer操作
            if (layer->latchBuffer(visibleRegions, latchTime, expectedPresentTime)) {
                mLayersPendingRefresh.push_back(layer);
                newDataLatched = true;
            }
            layer->useSurfaceDamage();
        }
    }
    //...

hasReadyFrame

bool BufferLayer::hasReadyFrame() const {
    return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh();
}

正常有buffer情况第一个hasFrameUpdate()为true,其他为false

hasFrameUpdate

bool BufferStateLayer::hasFrameUpdate() const {
    const State& c(getDrawingState());
    return (mDrawingStateModified || mDrawingState.modified) && (c.buffer != nullptr || c.bgColorLayer != nullptr);
}

c.bufferBufferStateLayer::setBuffer 中设置

没有进行setBuffer

导致上面的hasFrameUpdatec.buffer = nullptr

acquireNextBuffer侧

acquireNextBufferLocked中有frame=且没有releaseBuffer的trace说明调用了tsetBuffer

既然acquire时buffer != null所以SurfaceComposerClient::Transaction::setBuffer也会将

swhat |= layer_state_t::eBufferChanged;

那就可能是SurfaceFlinger处的setBuffer前面的栈没有调用上

flushTransactionQueues

其调用栈上会调用transactionIsReadyToBeApplied

有如下关键trace

frameIsEarly

frameIsEarly

bool SurfaceFlinger::frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const {
    // The amount of time SF can delay a frame if it is considered early based
    // on the VsyncModulator::VsyncConfig::appWorkDuration
    constexpr static std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
    // vsync周期时间
    const auto currentVsyncPeriod = mScheduler->getDisplayStatInfo(systemTime()).vsyncPeriod;
    const auto earlyLatchVsyncThreshold = currentVsyncPeriod / 2;
    // 这里vsyncid对应app token
    const auto prediction = mFrameTimeline->getTokenManager()->getPredictionsForToken(vsyncId);
    if (!prediction.has_value()) {
        return false;
    }
 
    if (std::abs(prediction->presentTime - expectedPresentTime) >=
        kEarlyLatchMaxThreshold.count()) {
        return false;
    }
    // True则isEarly,不setBuffer
    return prediction->presentTime >= expectedPresentTime &&
            prediction->presentTime - expectedPresentTime >= earlyLatchVsyncThreshold;
}

expectedPresentTime 是由 SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) 传入的 expectedVsyncTime 这个是计算出来的真实vsync时间,系统根据这个计算的sf.vsyncTime和app.vsyncTime

  • expectedPresentTime

    可以理解为上显的vsyncTime

  • prediction

    presentTime

    对应app的预测上显时间

  • prediction

    presentTime

    >=

    expectedPresentTime

    • app. prediction.endTime + sfWork >= sf. prediction.startTime + sfwork
    • app. prediction.endTime >= sf. prediction.startTime
  • prediction

    presentTime

    -

    expectedPresentTime

    >=

    earlyLatchVsyncThreshold

    • app. prediction.endTime >= sf. prediction.startTime + vsyncdur/2

7707391的prediction.endTime

当前app-vsync期望的sf合成时间在app-vsync+appwork,7707391并非期望的sf合成时间,则判定为来早了,

(这个时间段应该合成上一帧的buffer,而上一帧由于之前sf合成慢的原因,app-vsync晚了而没有触发绘制)

作用

  1. frameIsEarly对下面这种场景特别重要,app隔一帧绘制,一般出现在非全屏播放视频的场景,系统fps和视频fps不一致的情况下
  • 正常情况下,app-1 sf-1;app-3 sf-3;app-5 sf-5,对应vsync的合成
  • 如果没有frameIsEarly,app第1帧被sf-0提前latch了,而第二帧如果慢了一点没赶上sf-2,而是在sf-3latch,这种情况下就会出现两次帧更新间隔不一致的情况,而此时app并未发生掉帧。

http://minio.898311.xyz:8900/blogimg/16946102804723.png

  1. 另一种还可以防止有多个layer时,此layer提前更新的问题,(同一个app-vsync下所有layer的buffer能够给到sf同时合成)

app不绘制

导致上述原因的问题主要是TimerDispatch 的 vsyncCallback 来了或没有触发app-vsync,既没有触发app绘制

参考了下这篇文章:https://wiki.n.miui.com/display/~xuyingfeng/Timer.cpp

TimerDispatch 采用 Epoll+Timerfd 的方式触发,设置的唤醒时间如下

void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t /*now*/) {
    mIntendedWakeupTime = targetTime;
    mTimeKeeper->alarmAt(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
                         mIntendedWakeupTime);
    mLastTimerSchedule = mTimeKeeper->now();
}
void Timer::alarmAt(std::function<void()> callback, nsecs_t time) {
    // ..
    if (timerfd_settime(mTimerFd, TFD_TIMER_ABSTIME, &new_timer, &old_timer)) {
        ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno);
    }
}
// setTimer位置
void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
        nsecs_t now, CallbackMap::iterator const& skipUpdateIt) {
    //... now是当前时间:systemTime(SYSTEM_TIME_MONOTONIC);
        auto const wakeupTime = *callback->wakeupTime();
        if (!min || *min > wakeupTime) {
            nextWakeupName = callback->name();
            // 设置下次唤醒时间
            min = wakeupTime;
            targetVsync = callback->targetVsync();
        }
    }
 
    if (min && min < mIntendedWakeupTime) {
#ifdef MI_FEATURE_ENABLE
        if (MiSurfaceFlingerStub::isSceneExist(VSYNC_OPTIMIZE_SCENE)) {
            if (*min < now + mTimerSlack)
                min = now + mTimerSlack;
        }
#endif
        if (ATRACE_ENABLED() && nextWakeupName && targetVsync) {
            ftl::Concat trace(ftl::truncated<5>(*nextWakeupName), " alarm in ", ns2us(*min - now),
                              "us; VSYNC in ", ns2us(*targetVsync - now), "us");
            ATRACE_NAME(trace.c_str());
        }
        setTimer(*min, now); //*
    } else {
        // 当前没有此trace,所有setTimer正常设置
        ATRACE_NAME("cancel timer");
        cancelTimer();
    }
}

从未下app-vsync的前一个rearmTimerSkippingUpdateFor来看,有alarm in的trace,所以这里设置了setTimer

alarm in 看设置到下次唤醒时间距离当前now:2.78ms

但是2.78ms后并没有唤醒

其他地方并不会在调用setTimer,那说明可能被cancelTimer

但周期内并未发生cancel timer事件

只有这两个地方发生了timerfd_settime,似乎只剩下Epoll+Timerfd出问题的可能…