1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
/*
* Copyright 2018 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 <chrono>
#include <mutex>
#include <optional>
#include <unordered_set>
#include <android-base/thread_annotations.h>
#include <binder/IBinder.h>
#include <utils/Timers.h>
#include "../WpHash.h"
namespace android::scheduler {
// State machine controlled by transaction flags. VsyncModulator switches to early phase offsets
// when a transaction is flagged EarlyStart or Early, lasting until an EarlyEnd transaction or a
// fixed number of frames, respectively.
enum class TransactionSchedule {
Late, // Default.
EarlyStart,
EarlyEnd
};
// Modulates VSYNC phase depending on transaction schedule and refresh rate changes.
class VsyncModulator : public IBinder::DeathRecipient {
public:
// Number of frames to keep early offsets after an early transaction or GPU composition.
// This acts as a low-pass filter in case subsequent transactions are delayed, or if the
// composition strategy alternates on subsequent frames.
static constexpr int MIN_EARLY_TRANSACTION_FRAMES = 2;
static constexpr int MIN_EARLY_GPU_FRAMES = 2;
// Duration to delay the MIN_EARLY_TRANSACTION_FRAMES countdown after an early transaction.
// This may keep early offsets for an extra frame, but avoids a race with transaction commit.
static const std::chrono::nanoseconds MIN_EARLY_TRANSACTION_TIME;
// Phase offsets and work durations for SF and app deadlines from VSYNC.
struct VsyncConfig {
nsecs_t sfOffset;
nsecs_t appOffset;
std::chrono::nanoseconds sfWorkDuration;
std::chrono::nanoseconds appWorkDuration;
bool operator==(const VsyncConfig& other) const {
return sfOffset == other.sfOffset && appOffset == other.appOffset &&
sfWorkDuration == other.sfWorkDuration &&
appWorkDuration == other.appWorkDuration;
}
bool operator!=(const VsyncConfig& other) const { return !(*this == other); }
};
using VsyncConfigOpt = std::optional<VsyncConfig>;
struct VsyncConfigSet {
VsyncConfig early; // Used for early transactions, and during refresh rate change.
VsyncConfig earlyGpu; // Used during GPU composition.
VsyncConfig late; // Default.
std::chrono::nanoseconds hwcMinWorkDuration; // Used for calculating the
// earliest present time
bool operator==(const VsyncConfigSet& other) const {
return early == other.early && earlyGpu == other.earlyGpu && late == other.late &&
hwcMinWorkDuration == other.hwcMinWorkDuration;
}
bool operator!=(const VsyncConfigSet& other) const { return !(*this == other); }
};
using Clock = std::chrono::steady_clock;
using TimePoint = Clock::time_point;
using Now = TimePoint (*)();
explicit VsyncModulator(const VsyncConfigSet&, Now = Clock::now);
VsyncConfig getVsyncConfig() const EXCLUDES(mMutex);
[[nodiscard]] VsyncConfig setVsyncConfigSet(const VsyncConfigSet&) EXCLUDES(mMutex);
// Changes offsets in response to transaction flags or commit.
[[nodiscard]] VsyncConfigOpt setTransactionSchedule(TransactionSchedule,
const sp<IBinder>& = {}) EXCLUDES(mMutex);
[[nodiscard]] VsyncConfigOpt onTransactionCommit();
// Called when we send a refresh rate change to hardware composer, so that
// we can move into early offsets.
[[nodiscard]] VsyncConfigOpt onRefreshRateChangeInitiated();
// Called when we detect from VSYNC signals that the refresh rate changed.
// This way we can move out of early offsets if no longer necessary.
[[nodiscard]] VsyncConfigOpt onRefreshRateChangeCompleted();
[[nodiscard]] VsyncConfigOpt onDisplayRefresh(bool usedGpuComposition);
[[nodiscard]] bool isVsyncConfigDefault() const;
protected:
// Called from unit tests as well
void binderDied(const wp<IBinder>&) override EXCLUDES(mMutex);
private:
enum class VsyncConfigType { Early, EarlyGpu, Late };
VsyncConfigType getNextVsyncConfigType() const REQUIRES(mMutex);
const VsyncConfig& getNextVsyncConfig() const REQUIRES(mMutex);
[[nodiscard]] VsyncConfig updateVsyncConfig() EXCLUDES(mMutex);
[[nodiscard]] VsyncConfig updateVsyncConfigLocked() REQUIRES(mMutex);
mutable std::mutex mMutex;
VsyncConfigSet mVsyncConfigSet GUARDED_BY(mMutex);
VsyncConfig mVsyncConfig GUARDED_BY(mMutex){mVsyncConfigSet.late};
using Schedule = TransactionSchedule;
std::atomic<Schedule> mTransactionSchedule = Schedule::Late;
std::unordered_set<wp<IBinder>, WpHash> mEarlyWakeupRequests GUARDED_BY(mMutex);
std::atomic<bool> mRefreshRateChangePending = false;
std::atomic<int> mEarlyTransactionFrames = 0;
std::atomic<int> mEarlyGpuFrames = 0;
std::atomic<TimePoint> mEarlyTransactionStartTime = TimePoint();
std::atomic<TimePoint> mLastTransactionCommitTime = TimePoint();
const Now mNow;
const bool mTraceDetailedInfo;
};
} // namespace android::scheduler
|