blob: 0c227d41684b9fd17b6c9a6c1189ee42efd7df35 [file] [log] [blame]
/*
* 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 <cstdint>
#include <deque>
#include <mutex>
#include <optional>
#include <unordered_map>
#include <variant>
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
#include <gui/JankInfo.h>
#include <gui/LayerMetadata.h>
#include <timestatsproto/TimeStatsHelper.h>
#include <timestatsproto/TimeStatsProtoHeader.h>
#include <ui/FenceTime.h>
#include <utils/String16.h>
#include <utils/Vector.h>
#include <scheduler/Fps.h>
using android::gui::GameMode;
using android::gui::LayerMetadata;
using namespace android::surfaceflinger;
namespace android {
class TimeStats {
public:
using SetFrameRateVote = TimeStatsHelper::SetFrameRateVote;
virtual ~TimeStats() = default;
// Process a pull request from statsd.
virtual bool onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) = 0;
virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
virtual bool isEnabled() = 0;
virtual std::string miniDump() = 0;
virtual void incrementTotalFrames() = 0;
virtual void incrementMissedFrames() = 0;
// Increments the number of times the display refresh rate changed.
virtual void incrementRefreshRateSwitches() = 0;
// Records the start and end times for a frame.
// The start time is the same as the beginning of a SurfaceFlinger
// invalidate message.
// The end time corresponds to when SurfaceFlinger finishes submitting the
// request to HWC to present a frame.
virtual void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) = 0;
// Records the start time and end times for when RenderEngine begins work.
// The start time corresponds to the beginning of RenderEngine::drawLayers.
// The end time corresponds to when RenderEngine finishes rendering.
virtual void recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) = 0;
// Same as above, but passes in a fence representing the end time.
virtual void recordRenderEngineDuration(nsecs_t startTime,
const std::shared_ptr<FenceTime>& readyFence) = 0;
virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
uid_t uid, nsecs_t postTime, GameMode) = 0;
virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
// Reasons why latching a particular buffer may be skipped
enum class LatchSkipReason {
// If the acquire fence did not fire on some devices we skip latching
// the buffer until the fence fires.
LateAcquire,
};
// Increments the counter of skipped latch buffers.
virtual void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) = 0;
// Increments the counter of bad desired present times for this layer.
// Bad desired present times are "implausible" and cause SurfaceFlinger to
// latch a buffer immediately to avoid stalling.
virtual void incrementBadDesiredPresent(int32_t layerId) = 0;
virtual void setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) = 0;
virtual void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) = 0;
virtual void setAcquireFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& acquireFence) = 0;
// SetPresent{Time, Fence} are not expected to be called in the critical
// rendering path, as they flush prior fences if those fences have fired.
virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
Fps displayRefreshRate, std::optional<Fps> renderRate,
SetFrameRateVote frameRateVote, GameMode) = 0;
virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence,
Fps displayRefreshRate, std::optional<Fps> renderRate,
SetFrameRateVote frameRateVote, GameMode) = 0;
// Increments janky frames, blamed to the provided {refreshRate, renderRate, uid, layerName}
// key, with JankMetadata as supplementary reasons for the jank. Because FrameTimeline is the
// infrastructure responsible for computing jank in the system, this is expected to be called
// from FrameTimeline, rather than directly from SurfaceFlinger or individual layers. If there
// are no jank reasons, then total frames are incremented but jank is not, for accurate
// accounting of janky frames.
// displayDeadlineDelta, displayPresentJitter, and appDeadlineDelta are also provided in order
// to provide contextual information about a janky frame. These values may only be uploaded if
// there was an associated valid jank reason, and they must be positive. When these frame counts
// are incremented, these are also aggregated into a global reporting packet to help with data
// validation and assessing of overall device health.
struct JankyFramesInfo {
Fps refreshRate;
std::optional<Fps> renderRate;
uid_t uid = 0;
std::string layerName;
GameMode gameMode = GameMode::Unsupported;
int32_t reasons = 0;
nsecs_t displayDeadlineDelta = 0;
nsecs_t displayPresentJitter = 0;
nsecs_t appDeadlineDelta = 0;
static bool isOptApproxEqual(std::optional<Fps> lhs, std::optional<Fps> rhs) {
return (!lhs && !rhs) || (lhs && rhs && isApproxEqual(*lhs, *rhs));
}
bool operator==(const JankyFramesInfo& o) const {
return isApproxEqual(refreshRate, o.refreshRate) &&
isOptApproxEqual(renderRate, o.renderRate) && uid == o.uid &&
layerName == o.layerName && gameMode == o.gameMode && reasons == o.reasons &&
displayDeadlineDelta == o.displayDeadlineDelta &&
displayPresentJitter == o.displayPresentJitter &&
appDeadlineDelta == o.appDeadlineDelta;
}
friend std::ostream& operator<<(std::ostream& os, const JankyFramesInfo& info) {
os << "JankyFramesInfo {";
os << "\n .refreshRate = " << info.refreshRate;
os << "\n .renderRate = "
<< (info.renderRate ? to_string(*info.renderRate) : "nullopt");
os << "\n .uid = " << info.uid;
os << "\n .layerName = " << info.layerName;
os << "\n .reasons = " << info.reasons;
os << "\n .displayDeadlineDelta = " << info.displayDeadlineDelta;
os << "\n .displayPresentJitter = " << info.displayPresentJitter;
os << "\n .appDeadlineDelta = " << info.appDeadlineDelta;
return os << "\n}";
}
};
struct ClientCompositionRecord {
// Frame had client composition or mixed composition
bool hadClientComposition = false;
// Composition changed between hw composition and mixed/client composition
bool changed = false;
// Frame reused the client composition result from a previous frame
bool reused = false;
// Composition strategy predicted for frame
bool predicted = false;
// Composition strategy prediction succeeded
bool predictionSucceeded = false;
// Whether there is data we want to record.
bool hasInterestingData() const {
return hadClientComposition || changed || reused || predicted;
}
};
virtual void incrementJankyFrames(const JankyFramesInfo& info) = 0;
// Clean up the layer record
virtual void onDestroy(int32_t layerId) = 0;
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
virtual void removeTimeRecord(int32_t layerId, uint64_t frameNumber) = 0;
virtual void setPowerMode(
hardware::graphics::composer::V2_4::IComposerClient::PowerMode powerMode) = 0;
// Source of truth is RefrehRateStats.
virtual void recordRefreshRate(uint32_t fps, nsecs_t duration) = 0;
virtual void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) = 0;
virtual void pushCompositionStrategyState(const ClientCompositionRecord&) = 0;
};
namespace impl {
class TimeStats : public android::TimeStats {
using PowerMode = android::hardware::graphics::composer::V2_4::IComposerClient::PowerMode;
struct FrameTime {
uint64_t frameNumber = 0;
nsecs_t postTime = 0;
nsecs_t latchTime = 0;
nsecs_t acquireTime = 0;
nsecs_t desiredTime = 0;
nsecs_t presentTime = 0;
};
struct TimeRecord {
bool ready = false;
FrameTime frameTime;
std::shared_ptr<FenceTime> acquireFence;
std::shared_ptr<FenceTime> presentFence;
};
struct LayerRecord {
uid_t uid;
std::string layerName;
GameMode gameMode = GameMode::Unsupported;
// This is the index in timeRecords, at which the timestamps for that
// specific frame are still not fully received. This is not waiting for
// fences to signal, but rather waiting to receive those fences/timestamps.
int32_t waitData = -1;
uint32_t droppedFrames = 0;
uint32_t lateAcquireFrames = 0;
uint32_t badDesiredPresentFrames = 0;
TimeRecord prevTimeRecord;
std::optional<int32_t> prevPresentToPresentMs;
std::deque<TimeRecord> timeRecords;
};
struct PowerTime {
PowerMode powerMode = PowerMode::OFF;
nsecs_t prevTime = 0;
};
struct RenderEngineDuration {
nsecs_t startTime;
std::variant<nsecs_t, std::shared_ptr<FenceTime>> endTime;
};
struct GlobalRecord {
nsecs_t prevPresentTime = 0;
std::deque<std::shared_ptr<FenceTime>> presentFences;
std::deque<RenderEngineDuration> renderEngineDurations;
};
public:
TimeStats();
// For testing only for injecting custom dependencies.
TimeStats(std::optional<size_t> maxPulledLayers,
std::optional<size_t> maxPulledHistogramBuckets);
bool onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) override;
void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
bool isEnabled() override;
std::string miniDump() override;
void incrementTotalFrames() override;
void incrementMissedFrames() override;
void incrementRefreshRateSwitches() override;
void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) override;
void recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) override;
void recordRenderEngineDuration(nsecs_t startTime,
const std::shared_ptr<FenceTime>& readyFence) override;
void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, uid_t uid,
nsecs_t postTime, GameMode) override;
void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override;
void incrementBadDesiredPresent(int32_t layerId) override;
void setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) override;
void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) override;
void setAcquireFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& acquireFence) override;
void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
Fps displayRefreshRate, std::optional<Fps> renderRate, SetFrameRateVote,
GameMode) override;
void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate,
std::optional<Fps> renderRate, SetFrameRateVote, GameMode) override;
void incrementJankyFrames(const JankyFramesInfo& info) override;
// Clean up the layer record
void onDestroy(int32_t layerId) override;
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
void removeTimeRecord(int32_t layerId, uint64_t frameNumber) override;
void setPowerMode(
hardware::graphics::composer::V2_4::IComposerClient::PowerMode powerMode) override;
// Source of truth is RefrehRateStats.
void recordRefreshRate(uint32_t fps, nsecs_t duration) override;
void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) override;
void pushCompositionStrategyState(const ClientCompositionRecord&) override;
static const size_t MAX_NUM_TIME_RECORDS = 64;
private:
bool populateGlobalAtom(std::vector<uint8_t>* pulledData);
bool populateLayerAtom(std::vector<uint8_t>* pulledData);
bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
std::optional<Fps> renderRate, SetFrameRateVote,
GameMode);
void flushPowerTimeLocked();
void flushAvailableGlobalRecordsToStatsLocked();
bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName, GameMode);
void enable();
void disable();
void clearAll();
void clearGlobalLocked();
void clearLayersLocked();
void dump(bool asProto, std::optional<uint32_t> maxLayers, std::string& result);
std::atomic<bool> mEnabled = false;
std::mutex mMutex;
TimeStatsHelper::TimeStatsGlobal mTimeStats;
// Hashmap for LayerRecord with layerId as the hash key
std::unordered_map<int32_t, LayerRecord> mTimeStatsTracker;
PowerTime mPowerTime;
GlobalRecord mGlobalRecord;
static const size_t MAX_NUM_LAYER_RECORDS = 200;
static const size_t REFRESH_RATE_BUCKET_WIDTH = 30;
static const size_t RENDER_RATE_BUCKET_WIDTH = REFRESH_RATE_BUCKET_WIDTH;
static const size_t MAX_NUM_LAYER_STATS = 200;
static const size_t MAX_NUM_PULLED_LAYERS = MAX_NUM_LAYER_STATS;
size_t mMaxPulledLayers = MAX_NUM_PULLED_LAYERS;
size_t mMaxPulledHistogramBuckets = 6;
};
} // namespace impl
} // namespace android