Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0e486472 authored by John Reck's avatar John Reck
Browse files

Revert "Revert "Break down jank between frame drops vs. triple buffered""

This reverts commit a6d8fbf4.

Fixes an out-of-bounds read in COMPARISONS by switching up
how comparisons works. Instead of requiring all jank types
to have an associated COMPARISON's entry, which kHighInputLatency
and kMissedDeadline don't, instead have each
COMPARISON indicate which JankType it applies to so it can
be independently sized from JankTypes.

Bug: 70220906
Bug: 75566601
Test: launching & using maps works
Change-Id: I7fd90daeb320b4627e42c3418c89726d860998c1
parent dba47fcd
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@ message GraphicsStatsJankSummaryProto {
    // Number of "missed vsync" events.
    optional int32 missed_vsync_count = 3;

    // Number of "high input latency" events.
    // Number of frames in triple-buffering scenario (high input latency)
    optional int32 high_input_latency_count = 4;

    // Number of "slow UI thread" events.
@@ -67,6 +67,9 @@ message GraphicsStatsJankSummaryProto {

    // Number of "slow draw" events.
    optional int32 slow_draw_count = 7;

    // Number of frames that missed their deadline (aka, visibly janked)
    optional int32 missed_deadline_count = 8;
}

message GraphicsStatsHistogramBucketProto {
+52 −35
Original line number Diff line number Diff line
@@ -38,16 +38,27 @@ namespace android {
namespace uirenderer {

struct Comparison {
    JankType type;
    std::function<int64_t(nsecs_t)> computeThreadshold;
    FrameInfoIndex start;
    FrameInfoIndex end;
};

static const Comparison COMPARISONS[] = {
        {FrameInfoIndex::IntendedVsync, FrameInfoIndex::Vsync},
        {FrameInfoIndex::OldestInputEvent, FrameInfoIndex::Vsync},
        {FrameInfoIndex::Vsync, FrameInfoIndex::SyncStart},
        {FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart},
        {FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::FrameCompleted},
static const std::array<Comparison, 4> COMPARISONS{
        Comparison{JankType::kMissedVsync, [](nsecs_t) { return 1; }, FrameInfoIndex::IntendedVsync,
                   FrameInfoIndex::Vsync},

        Comparison{JankType::kSlowUI,
                   [](nsecs_t frameInterval) { return static_cast<int64_t>(.5 * frameInterval); },
                   FrameInfoIndex::Vsync, FrameInfoIndex::SyncStart},

        Comparison{JankType::kSlowSync,
                   [](nsecs_t frameInterval) { return static_cast<int64_t>(.2 * frameInterval); },
                   FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart},

        Comparison{JankType::kSlowRT,
                   [](nsecs_t frameInterval) { return static_cast<int64_t>(.75 * frameInterval); },
                   FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::FrameCompleted},
};

// If the event exceeds 10 seconds throw it away, this isn't a jank event
@@ -91,24 +102,10 @@ JankTracker::JankTracker(ProfileDataContainer* globalData, const DisplayInfo& di

void JankTracker::setFrameInterval(nsecs_t frameInterval) {
    mFrameInterval = frameInterval;
    mThresholds[kMissedVsync] = 1;
    /*
     * Due to interpolation and sample rate differences between the touch
     * panel and the display (example, 85hz touch panel driving a 60hz display)
     * we call high latency 1.5 * frameinterval
     *
     * NOTE: Be careful when tuning this! A theoretical 1,000hz touch panel
     * on a 60hz display will show kOldestInputEvent - kIntendedVsync of being 15ms
     * Thus this must always be larger than frameInterval, or it will fail
     */
    mThresholds[kHighInputLatency] = static_cast<int64_t>(1.5 * frameInterval);

    // Note that these do not add up to 1. This is intentional. It's to deal
    // with variance in values, and should be sort of an upper-bound on what
    // is reasonable to expect.
    mThresholds[kSlowUI] = static_cast<int64_t>(.5 * frameInterval);
    mThresholds[kSlowSync] = static_cast<int64_t>(.2 * frameInterval);
    mThresholds[kSlowRT] = static_cast<int64_t>(.75 * frameInterval);
    for (auto& comparison : COMPARISONS) {
        mThresholds[comparison.type] = comparison.computeThreadshold(frameInterval);
    }
}

void JankTracker::finishFrame(const FrameInfo& frame) {
@@ -129,28 +126,48 @@ void JankTracker::finishFrame(const FrameInfo& frame) {
            totalDuration -= forgiveAmount;
        }
    }

    LOG_ALWAYS_FATAL_IF(totalDuration <= 0, "Impossible totalDuration %" PRId64, totalDuration);
    mData->reportFrame(totalDuration);
    (*mGlobalData)->reportFrame(totalDuration);

    // Keep the fast path as fast as possible.
    if (CC_LIKELY(totalDuration < mFrameInterval)) {
        return;
    }

    // Only things like Surface.lockHardwareCanvas() are exempt from tracking
    if (frame[FrameInfoIndex::Flags] & EXEMPT_FRAMES_FLAGS) {
    if (CC_UNLIKELY(frame[FrameInfoIndex::Flags] & EXEMPT_FRAMES_FLAGS)) {
        return;
    }

    if (totalDuration > mFrameInterval) {
        mData->reportJank();
        (*mGlobalData)->reportJank();
    }

    bool isTripleBuffered = mSwapDeadline > frame[FrameInfoIndex::IntendedVsync];

    mSwapDeadline = std::max(mSwapDeadline + mFrameInterval,
                             frame[FrameInfoIndex::IntendedVsync] + mFrameInterval);

    // If we hit the deadline, cool!
    if (frame[FrameInfoIndex::FrameCompleted] < mSwapDeadline) {
        if (isTripleBuffered) {
            mData->reportJankType(JankType::kHighInputLatency);
            (*mGlobalData)->reportJankType(JankType::kHighInputLatency);
        }
        return;
    }

    mData->reportJankType(JankType::kMissedDeadline);
    (*mGlobalData)->reportJankType(JankType::kMissedDeadline);

    // Janked, reset the swap deadline
    nsecs_t jitterNanos = frame[FrameInfoIndex::FrameCompleted] - frame[FrameInfoIndex::Vsync];
    nsecs_t lastFrameOffset = jitterNanos % mFrameInterval;
    mSwapDeadline = frame[FrameInfoIndex::FrameCompleted] - lastFrameOffset + mFrameInterval;

    for (int i = 0; i < NUM_BUCKETS; i++) {
        int64_t delta = frame.duration(COMPARISONS[i].start, COMPARISONS[i].end);
        if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) {
            mData->reportJankType((JankType)i);
            (*mGlobalData)->reportJankType((JankType)i);
    for (auto& comparison : COMPARISONS) {
        int64_t delta = frame.duration(comparison.start, comparison.end);
        if (delta >= mThresholds[comparison.type] && delta < IGNORE_EXCEEDING) {
            mData->reportJankType(comparison.type);
            (*mGlobalData)->reportJankType(comparison.type);
        }
    }

+1 −0
Original line number Diff line number Diff line
@@ -75,6 +75,7 @@ private:

    std::array<int64_t, NUM_BUCKETS> mThresholds;
    int64_t mFrameInterval;
    nsecs_t mSwapDeadline;
    // The amount of time we will erase from the total duration to account
    // for SF vsync offsets with HWC2 blocking dequeueBuffers.
    // (Vsync + mDequeueBlockTolerance) is the point at which we expect
+1 −2
Original line number Diff line number Diff line
@@ -23,8 +23,7 @@ namespace uirenderer {

static const char* JANK_TYPE_NAMES[] = {
        "Missed Vsync",        "High input latency",       "Slow UI thread",
        "Slow bitmap uploads", "Slow issue draw commands",
};
        "Slow bitmap uploads", "Slow issue draw commands", "Frame deadline missed"};

// The bucketing algorithm controls so to speak
// If a frame is <= to this it goes in bucket 0
+1 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ enum JankType {
    kSlowUI,
    kSlowSync,
    kSlowRT,
    kMissedDeadline,

    // must be last
    NUM_BUCKETS,
Loading