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

Commit f9a7d410 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes I078a2055,Ifa7f2705

* changes:
  SF: Stop collecting animation FrameStats
  SF: Extract PresentLatencyTracker
parents a7dfc93a 66296b2b
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ cc_defaults {
        "libbase",
        "libcutils",
        "liblog",
        "libui",
        "libutils",
    ],
}
@@ -39,6 +40,7 @@ cc_library_static {
    name: "libscheduler",
    defaults: ["libscheduler_defaults"],
    srcs: [
        "src/PresentLatencyTracker.cpp",
        "src/Timer.cpp",
    ],
    local_include_dirs: ["include"],
@@ -50,6 +52,7 @@ cc_test {
    test_suites: ["device-tests"],
    defaults: ["libscheduler_defaults"],
    srcs: [
        "tests/PresentLatencyTrackerTest.cpp",
        "tests/TimerTest.cpp",
    ],
    static_libs: [
+54 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 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.
 */

#pragma once

#include <memory>
#include <queue>
#include <utility>

#include <scheduler/Time.h>

namespace android {

class FenceTime;

namespace scheduler {

// Computes composite-to-present latency by tracking recently composited frames pending to present.
class PresentLatencyTracker {
public:
    // For tests.
    static constexpr size_t kMaxPendingFrames = 4;

    // Returns the present latency of the latest frame.
    Duration trackPendingFrame(TimePoint compositeTime,
                               std::shared_ptr<FenceTime> presentFenceTime);

private:
    struct PendingFrame {
        PendingFrame(TimePoint compositeTime, std::shared_ptr<FenceTime> presentFenceTime)
              : compositeTime(compositeTime), presentFenceTime(std::move(presentFenceTime)) {}

        const TimePoint compositeTime;
        const std::shared_ptr<FenceTime> presentFenceTime;
    };

    std::queue<PendingFrame> mPendingFrames;
};

} // namespace scheduler
} // namespace android
+5 −5
Original line number Diff line number Diff line
@@ -32,13 +32,13 @@ static_assert(SchedulerClock::is_steady);
struct Duration;

struct TimePoint : scheduler::SchedulerClock::time_point {
    explicit TimePoint(const Duration&);
    explicit constexpr TimePoint(const Duration&);

    // Implicit conversion from std::chrono counterpart.
    constexpr TimePoint(scheduler::SchedulerClock::time_point p)
          : scheduler::SchedulerClock::time_point(p) {}

    static TimePoint fromNs(nsecs_t);
    static constexpr TimePoint fromNs(nsecs_t);

    nsecs_t ns() const;
};
@@ -47,16 +47,16 @@ struct Duration : TimePoint::duration {
    // Implicit conversion from std::chrono counterpart.
    constexpr Duration(TimePoint::duration d) : TimePoint::duration(d) {}

    static Duration fromNs(nsecs_t ns) { return {std::chrono::nanoseconds(ns)}; }
    static constexpr Duration fromNs(nsecs_t ns) { return {std::chrono::nanoseconds(ns)}; }

    nsecs_t ns() const { return std::chrono::nanoseconds(*this).count(); }
};

using Period = Duration;

inline TimePoint::TimePoint(const Duration& d) : scheduler::SchedulerClock::time_point(d) {}
constexpr TimePoint::TimePoint(const Duration& d) : scheduler::SchedulerClock::time_point(d) {}

inline TimePoint TimePoint::fromNs(nsecs_t ns) {
constexpr TimePoint TimePoint::fromNs(nsecs_t ns) {
    return TimePoint(Duration::fromNs(ns));
}

+56 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 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 <scheduler/PresentLatencyTracker.h>

#include <cutils/compiler.h>
#include <log/log.h>
#include <ui/FenceTime.h>

namespace android::scheduler {

Duration PresentLatencyTracker::trackPendingFrame(TimePoint compositeTime,
                                                  std::shared_ptr<FenceTime> presentFenceTime) {
    Duration presentLatency = Duration::zero();
    while (!mPendingFrames.empty()) {
        const auto& pendingFrame = mPendingFrames.front();
        const auto presentTime =
                TimePoint::fromNs(pendingFrame.presentFenceTime->getCachedSignalTime());

        if (presentTime == TimePoint::fromNs(Fence::SIGNAL_TIME_PENDING)) {
            break;
        }

        if (presentTime == TimePoint::fromNs(Fence::SIGNAL_TIME_INVALID)) {
            ALOGE("%s: Invalid present fence", __func__);
        } else {
            presentLatency = presentTime - pendingFrame.compositeTime;
        }

        mPendingFrames.pop();
    }

    mPendingFrames.emplace(compositeTime, std::move(presentFenceTime));

    if (CC_UNLIKELY(mPendingFrames.size() > kMaxPendingFrames)) {
        ALOGE("%s: Too many pending frames", __func__);
        mPendingFrames.pop();
    }

    return presentLatency;
}

} // namespace android::scheduler
+81 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 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 <gtest/gtest.h>

#include <algorithm>
#include <array>

#include <scheduler/PresentLatencyTracker.h>
#include <ui/FenceTime.h>

namespace android::scheduler {
namespace {

using FencePair = std::pair<sp<Fence>, std::shared_ptr<FenceTime>>;

FencePair makePendingFence(FenceToFenceTimeMap& fenceMap) {
    const auto fence = sp<Fence>::make();
    return {fence, fenceMap.createFenceTimeForTest(fence)};
}

} // namespace

TEST(PresentLatencyTrackerTest, skipsInvalidFences) {
    PresentLatencyTracker tracker;

    const TimePoint kCompositeTime = TimePoint::fromNs(999);
    EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, FenceTime::NO_FENCE), Duration::zero());
    EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, FenceTime::NO_FENCE), Duration::zero());
    EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, FenceTime::NO_FENCE), Duration::zero());

    FenceToFenceTimeMap fenceMap;
    const auto [fence, fenceTime] = makePendingFence(fenceMap);
    EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, fenceTime), Duration::zero());

    fenceTime->signalForTest(9999);

    EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, FenceTime::NO_FENCE),
              Duration::fromNs(9000));
}

TEST(PresentLatencyTrackerTest, tracksPendingFrames) {
    PresentLatencyTracker tracker;

    FenceToFenceTimeMap fenceMap;
    std::array<FencePair, PresentLatencyTracker::kMaxPendingFrames> fences;
    std::generate(fences.begin(), fences.end(), [&fenceMap] { return makePendingFence(fenceMap); });

    // The present latency is 0 if all fences are pending.
    const TimePoint kCompositeTime = TimePoint::fromNs(1234);
    for (const auto& [fence, fenceTime] : fences) {
        EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, fenceTime), Duration::zero());
    }

    // If multiple frames have been presented...
    constexpr size_t kPresentCount = fences.size() / 2;
    for (size_t i = 0; i < kPresentCount; i++) {
        fences[i].second->signalForTest(kCompositeTime.ns() + static_cast<nsecs_t>(i));
    }

    const auto fence = makePendingFence(fenceMap);

    // ...then the present latency is measured using the latest frame.
    constexpr Duration kPresentLatency = Duration::fromNs(static_cast<nsecs_t>(kPresentCount) - 1);
    EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, fence.second), kPresentLatency);
}

} // namespace android::scheduler
Loading