blob: 2ceb0e7cb76f87e6f49aa0c7d5a164fe19e9950d [file] [log] [blame]
/*
* Copyright 2024 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 <android-base/thread_annotations.h>
#include <utils/Timers.h>
#include "InputEventTimeline.h"
namespace android::inputdispatcher {
static constexpr size_t NUM_BINS = 20;
static constexpr size_t NUM_INPUT_EVENT_TYPES = 6;
enum class LatencyStageIndex : size_t {
EVENT_TO_READ = 0,
READ_TO_DELIVER = 1,
DELIVER_TO_CONSUME = 2,
CONSUME_TO_FINISH = 3,
CONSUME_TO_GPU_COMPLETE = 4,
GPU_COMPLETE_TO_PRESENT = 5,
END_TO_END = 6,
SIZE = 7, // must be last
};
// Let's create a full timeline here:
// eventTime
// readTime
// <---- after this point, the data becomes per-connection
// deliveryTime // time at which the event was sent to the receiver
// consumeTime // time at which the receiver read the event
// finishTime // time at which the dispatcher reads the response from the receiver that the event
// was processed
// GraphicsTimeline::GPU_COMPLETED_TIME
// GraphicsTimeline::PRESENT_TIME
/**
* Keep histograms with latencies of the provided events
*/
class LatencyAggregatorWithHistograms final : public InputEventTimelineProcessor {
public:
/**
* Record a complete event timeline
*/
void processTimeline(const InputEventTimeline& timeline) override;
void pushLatencyStatistics() override;
std::string dump(const char* prefix) const override;
private:
// ---------- Slow event handling ----------
void processSlowEvent(const InputEventTimeline& timeline);
nsecs_t mLastSlowEventTime = 0;
// How many slow events have been skipped due to rate limiting
size_t mNumSkippedSlowEvents = 0;
// How many events have been received since the last time we reported a slow event
size_t mNumEventsSinceLastSlowEventReport = 0;
// ---------- Statistics handling ----------
/**
* Data structure to gather time samples into NUM_BINS buckets
*/
class Histogram {
public:
Histogram(const std::array<int, NUM_BINS - 1>& binSizes) : mBinSizes(binSizes) {
mBinCounts.fill(0);
}
// Increments binCounts of the appropriate bin when adding a new sample
void addSample(int64_t sample) {
size_t binIndex = getSampleBinIndex(sample);
mBinCounts[binIndex]++;
}
const std::array<int32_t, NUM_BINS>& getBinCounts() const { return mBinCounts; }
private:
// reference to an array that represents the range of values each bin holds.
// in bin i+1 live samples such that *mBinSizes[i] <= sample < *mBinSizes[i+1]
const std::array<int, NUM_BINS - 1>& mBinSizes;
std::array<int32_t, NUM_BINS>
mBinCounts; // the number of samples that currently live in each bin
size_t getSampleBinIndex(int64_t sample) {
auto it = std::upper_bound(mBinSizes.begin(), mBinSizes.end(), sample);
return std::distance(mBinSizes.begin(), it);
}
};
void processStatistics(const InputEventTimeline& timeline);
// Identifier for the an input event. If two input events have the same identifiers we
// want to use the same histograms to count the latency samples
using InputEventLatencyIdentifier =
std::tuple<uint16_t /*vendorId*/, uint16_t /*productId*/,
const std::set<InputDeviceUsageSource> /*sources*/,
InputEventActionType /*inputEventActionType*/>;
// Maps an input event identifier to an array of 7 histograms, one for each latency
// stage. It is cleared after an atom push
std::map<InputEventLatencyIdentifier, std::array<Histogram, 7>> mHistograms;
void addSampleToHistogram(const InputEventLatencyIdentifier& identifier,
LatencyStageIndex latencyStageIndex, nsecs_t time);
// Stores all possible arrays of bin sizes. The order in the vector does not matter, as long
// as binSizesMappings points to the right index
static constexpr std::array<std::array<int, NUM_BINS - 1>, 6> allBinSizes = {
{{10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100},
{1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32},
{15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195, 210, 225, 240, 255, 270,
285},
{40, 80, 120, 160, 200, 240, 280, 320, 360, 400, 440, 480, 520, 560, 600, 640, 680,
720, 760},
{20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300, 320, 340, 360,
380},
{200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600,
1700, 1800, 1900, 2000}}};
// Stores indexes in allBinSizes to use with each {LatencyStage, InputEventType} pair.
// Bin sizes for a certain latencyStage and inputEventType are at:
// *(allBinSizes[binSizesMappings[latencyStageIndex][inputEventTypeIndex]])
// inputEventTypeIndex is the int value of InputEventActionType enum decreased by 1 since we
// don't want to record latencies for unknown events.
// e.g. MOTION_ACTION_DOWN is 0, MOTION_ACTION_MOVE is 1...
static constexpr std::array<std::array<int8_t, NUM_INPUT_EVENT_TYPES>,
static_cast<size_t>(LatencyStageIndex::SIZE)>
binSizesMappings = {{{0, 0, 0, 0, 0, 0},
{1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1},
{2, 2, 2, 2, 2, 2},
{3, 3, 3, 3, 3, 3},
{4, 4, 4, 4, 4, 4},
{5, 5, 5, 5, 5, 5}}};
// Similar to binSizesMappings, but holds the index of the array of bin ranges to use on the
// server. The index gets pushed with the atom within the histogram_version field.
static constexpr std::array<std::array<int8_t, NUM_INPUT_EVENT_TYPES>,
static_cast<size_t>(LatencyStageIndex::SIZE)>
histogramVersions = {{{0, 0, 0, 0, 0, 0},
{1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1},
{2, 2, 2, 2, 2, 2},
{3, 3, 3, 3, 3, 3},
{4, 4, 4, 4, 4, 4},
{5, 5, 5, 5, 5, 5}}};
};
} // namespace android::inputdispatcher