C++程序  |  162行  |  5.04 KB

/*
 * Copyright 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "FrameStatistics.h"

#define LOG_TAG "FrameStatistics"

#include <cmath>
#include <inttypes.h>
#include <string>

#include "EGL.h"

#include "Log.h"


namespace swappy {

// NB This is only needed for C++14
constexpr std::chrono::nanoseconds FrameStatistics::LOG_EVERY_N_NS;

void FrameStatistics::updateFrames(EGLnsecsANDROID start, EGLnsecsANDROID end, uint64_t stat[]) {
    const uint64_t deltaTimeNano = end - start;

    uint32_t numFrames = deltaTimeNano / mRefreshPeriod.count();
    numFrames = std::min(numFrames, static_cast<uint32_t>(MAX_FRAME_BUCKETS));
    stat[numFrames]++;
}

void FrameStatistics::updateIdleFrames(EGL::FrameTimestamps& frameStats) {
    updateFrames(frameStats.renderingCompleted,
                 frameStats.compositionLatched,
                 mStats.idleFrames);
}

void FrameStatistics::updateLatencyFrames(swappy::EGL::FrameTimestamps &frameStats,
                                                                        TimePoint frameStartTime) {
    updateFrames(frameStartTime.time_since_epoch().count(),
                 frameStats.presented,
                 mStats.latencyFrames);
}

void FrameStatistics::updateLateFrames(EGL::FrameTimestamps& frameStats) {
    updateFrames(frameStats.requested,
                 frameStats.presented,
                 mStats.lateFrames);
}

void FrameStatistics::updateOffsetFromPreviousFrame(swappy::EGL::FrameTimestamps &frameStats) {
    if (mPrevFrameTime != 0) {
        updateFrames(mPrevFrameTime,
                     frameStats.presented,
                     mStats.offsetFromPreviousFrame);
    }
    mPrevFrameTime = frameStats.presented;
}

// called once per swap
void FrameStatistics::capture(EGLDisplay dpy, EGLSurface surface) {
    const TimePoint frameStartTime = std::chrono::steady_clock::now();

    // first get the next frame id
    std::pair<bool,EGLuint64KHR> nextFrameId = mEgl->getNextFrameId(dpy, surface);
    if (nextFrameId.first) {
        mPendingFrames.push_back({dpy, surface, nextFrameId.second, frameStartTime});
    }

    if (mPendingFrames.empty()) {
        return;
    }


    EGLFrame frame = mPendingFrames.front();
    // make sure we don't lag behind the stats too much
    if (nextFrameId.first && nextFrameId.second - frame.id > MAX_FRAME_LAG) {
        while (mPendingFrames.size() > 1)
            mPendingFrames.erase(mPendingFrames.begin());
        mPrevFrameTime = 0;
        frame = mPendingFrames.front();
    }

    std::unique_ptr<EGL::FrameTimestamps> frameStats =
            mEgl->getFrameTimestamps(frame.dpy, frame.surface, frame.id);

    if (!frameStats) {
        return;
    }

    mPendingFrames.erase(mPendingFrames.begin());

    std::lock_guard<std::mutex> lock(mMutex);
    mStats.totalFrames++;
    updateIdleFrames(*frameStats);
    updateLateFrames(*frameStats);
    updateOffsetFromPreviousFrame(*frameStats);
    updateLatencyFrames(*frameStats, frame.startFrameTime);

    logFrames();
}

void FrameStatistics::logFrames() {
    static auto previousLogTime = std::chrono::steady_clock::now();

    if (std::chrono::steady_clock::now() - previousLogTime < LOG_EVERY_N_NS) {
        return;
    }

    std::string message;
    ALOGI("== Frame statistics ==");
    ALOGI("total frames: %" PRIu64, mStats.totalFrames);
    message += "Buckets:                    ";
    for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
        message += "\t[" + swappy::to_string(i) + "]";
    ALOGI("%s", message.c_str());

    message = "";
    message += "idle frames:                ";
    for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
        message += "\t " + swappy::to_string(mStats.idleFrames[i]);
    ALOGI("%s", message.c_str());

    message = "";
    message += "late frames:                ";
    for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
        message += "\t " + swappy::to_string(mStats.lateFrames[i]);
    ALOGI("%s", message.c_str());

    message = "";
    message += "offset from previous frame: ";
    for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
        message += "\t " + swappy::to_string(mStats.offsetFromPreviousFrame[i]);
    ALOGI("%s", message.c_str());

    message = "";
    message += "frame latency:              ";
    for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
        message += "\t " + swappy::to_string(mStats.latencyFrames[i]);
    ALOGI("%s", message.c_str());

    previousLogTime = std::chrono::steady_clock::now();
}

Swappy_Stats FrameStatistics::getStats() {
    std::lock_guard<std::mutex> lock(mMutex);
    return mStats;
}

} // namespace swappy