blob: 2afff8d313a247b2fec46df0baa7bd1b7c76cd4e [file] [log] [blame]
/*
* Copyright (C) 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 <timestatsproto/TimeStatsProtoHeader.h>
#include <utils/Timers.h>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
namespace android {
namespace surfaceflinger {
class TimeStatsHelper {
public:
class Histogram {
public:
// Key is the delta time between timestamps
// Value is the number of appearances of that delta
std::unordered_map<int32_t, int32_t> hist;
void insert(int32_t delta);
int64_t totalTime() const;
float averageTime() const;
std::string toString() const;
};
struct JankPayload {
// note that transactions are counted for these frames.
int32_t totalFrames = 0;
int32_t totalJankyFrames = 0;
int32_t totalSFLongCpu = 0;
int32_t totalSFLongGpu = 0;
int32_t totalSFUnattributed = 0;
int32_t totalAppUnattributed = 0;
int32_t totalSFScheduling = 0;
int32_t totalSFPredictionError = 0;
int32_t totalAppBufferStuffing = 0;
std::string toString() const;
};
struct SetFrameRateVote {
float frameRate = 0;
// Needs to be in sync with atoms.proto
enum class FrameRateCompatibility {
Undefined = 0,
Default = 1,
ExactOrMultiple = 2,
} frameRateCompatibility = FrameRateCompatibility::Undefined;
// Needs to be in sync with atoms.proto
enum class Seamlessness {
Undefined = 0,
ShouldBeSeamless = 1,
NotRequired = 2,
} seamlessness = Seamlessness::Undefined;
static std::string toString(FrameRateCompatibility);
static std::string toString(Seamlessness);
std::string toString() const;
};
/**
* GameMode of the layer. GameModes are set by SysUI through WMShell.
* Actual game mode definitions are managed by GameManager.java
* The values defined here should always be in sync with the ones in GameManager.
*/
enum GameMode {
GameModeUnsupported = 0,
GameModeStandard = 1,
GameModePerformance = 2,
GameModeBattery = 3,
};
class TimeStatsLayer {
public:
uid_t uid;
std::string layerName;
std::string packageName;
int32_t displayRefreshRateBucket = 0;
int32_t renderRateBucket = 0;
int32_t gameMode = 0;
int32_t totalFrames = 0;
int32_t droppedFrames = 0;
int32_t lateAcquireFrames = 0;
int32_t badDesiredPresentFrames = 0;
JankPayload jankPayload;
SetFrameRateVote setFrameRateVote;
std::unordered_map<std::string, Histogram> deltas;
std::string toString() const;
std::string toString(int32_t gameMode) const;
SFTimeStatsLayerProto toProto() const;
};
// Lifted from SkiaGLRenderEngine's LinearEffect class.
// Which in turn was inspired by art/runtime/class_linker.cc
// Also this is what boost:hash_combine does so this is a pretty good hash.
static size_t HashCombine(size_t seed, size_t val) {
return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
}
struct TimelineStatsKey {
int32_t displayRefreshRateBucket = 0;
int32_t renderRateBucket = 0;
struct Hasher {
size_t operator()(const TimelineStatsKey& key) const {
size_t result = std::hash<int32_t>{}(key.displayRefreshRateBucket);
return HashCombine(result, std::hash<int32_t>{}(key.renderRateBucket));
}
};
bool operator==(const TimelineStatsKey& o) const {
return displayRefreshRateBucket == o.displayRefreshRateBucket &&
renderRateBucket == o.renderRateBucket;
}
};
struct LayerStatsKey {
uid_t uid = 0;
std::string layerName;
int32_t gameMode = 0;
struct Hasher {
size_t operator()(const LayerStatsKey& key) const {
size_t uidHash = std::hash<uid_t>{}(key.uid);
size_t layerNameHash = std::hash<std::string>{}(key.layerName);
size_t gameModeHash = std::hash<int32_t>{}(key.gameMode);
return HashCombine(uidHash, HashCombine(layerNameHash, gameModeHash));
}
};
bool operator==(const LayerStatsKey& o) const {
return uid == o.uid && layerName == o.layerName && gameMode == o.gameMode;
}
};
struct TimelineStats {
TimelineStatsKey key;
JankPayload jankPayload;
Histogram displayDeadlineDeltas;
Histogram displayPresentDeltas;
std::unordered_map<LayerStatsKey, TimeStatsLayer, LayerStatsKey::Hasher> stats;
void clearGlobals() {
jankPayload = {};
displayDeadlineDeltas = {};
displayPresentDeltas = {};
}
};
class TimeStatsGlobal {
public:
// Note: these are all legacy statistics, we're keeping these around because a variety of
// systems and form-factors find these useful when comparing with older releases. However,
// the current recommendation is that the new timeline-based metrics are used, and the old
// ones are deprecated.
int64_t statsStartLegacy = 0;
int64_t statsEndLegacy = 0;
int32_t totalFramesLegacy = 0;
int32_t missedFramesLegacy = 0;
int32_t clientCompositionFramesLegacy = 0;
int32_t clientCompositionReusedFramesLegacy = 0;
int32_t refreshRateSwitchesLegacy = 0;
int32_t compositionStrategyChangesLegacy = 0;
int32_t displayEventConnectionsCountLegacy = 0;
int64_t displayOnTimeLegacy = 0;
Histogram presentToPresentLegacy;
Histogram frameDurationLegacy;
Histogram renderEngineTimingLegacy;
std::unordered_map<uint32_t, nsecs_t> refreshRateStatsLegacy;
std::unordered_map<TimelineStatsKey, TimelineStats, TimelineStatsKey::Hasher> stats;
std::string toString(std::optional<uint32_t> maxLayers) const;
SFTimeStatsGlobalProto toProto(std::optional<uint32_t> maxLayers) const;
private:
std::vector<TimeStatsLayer const*> generateDumpStats(
std::optional<uint32_t> maxLayers) const;
};
};
} // namespace surfaceflinger
} // namespace android