Files
Siarhei Vishniakou b07fe1debb Add infrastructure for registering a native frame metrics observer
This will allow NativeInputEventReceiver to subscribe to the frame
metrics notifications directly from the native code, without having to
involve java.

Java would still be needed to register the observer, though.

Bug: 376713684
Test: compile only, this infra is used in another CL
Flag: EXEMPT refactor
Change-Id: I7be8f0b2e94595f9a64ce69a22fe5910e0cd7711
2025-02-18 13:45:51 -08:00

214 lines
7.2 KiB
C++

/*
* Copyright (C) 2015 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.
*/
#ifndef FRAMEINFO_H_
#define FRAMEINFO_H_
#include <cutils/compiler.h>
#include <memory.h>
#include <utils/Timers.h>
#include <array>
#include <optional>
#include <string>
#include "SkippedFrameInfo.h"
#include "utils/Macros.h"
namespace android {
namespace uirenderer {
// This value must be in sync with `FRAME_INFO_SIZE` in FrameInfo.Java
static constexpr size_t UI_THREAD_FRAME_INFO_SIZE = 13;
enum class FrameInfoIndex {
Flags = 0,
FrameTimelineVsyncId,
IntendedVsync,
Vsync,
InputEventId,
HandleInputStart,
AnimationStart,
PerformTraversalsStart,
DrawStart,
FrameDeadline,
FrameStartTime,
FrameInterval,
// End of UI frame info
// The target workload duration based on the original frame deadline and
// and intended vsync. Counted in UI_THREAD_FRAME_INFO_SIZE so its value
// can be set in setVsync().
WorkloadTarget,
SyncQueued,
SyncStart,
IssueDrawCommandsStart,
SwapBuffers,
FrameCompleted,
DequeueBufferDuration,
QueueBufferDuration,
GpuCompleted,
SwapBuffersCompleted,
DisplayPresentTime,
CommandSubmissionCompleted,
// Must be the last value!
// Also must be kept in sync with FrameMetrics.java#FRAME_STATS_COUNT
NumIndexes
};
extern const std::array<const char*, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames;
namespace FrameInfoFlags {
enum {
WindowVisibilityChanged = 1 << 0,
RTAnimation = 1 << 1,
SurfaceCanvas = 1 << 2,
SkippedFrame = 1 << 3,
};
};
using FrameInfoBuffer = std::array<int64_t, static_cast<size_t>(FrameInfoIndex::NumIndexes)>;
class UiFrameInfoBuilder {
public:
static constexpr int64_t INVALID_VSYNC_ID = -1;
static constexpr int64_t UNKNOWN_DEADLINE = std::numeric_limits<int64_t>::max();
static constexpr int64_t UNKNOWN_FRAME_INTERVAL = -1;
explicit UiFrameInfoBuilder(int64_t* buffer) : mBuffer(buffer) {
memset(mBuffer, 0, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
set(FrameInfoIndex::FrameTimelineVsyncId) = INVALID_VSYNC_ID;
// The struct is zeroed by memset above. That also sets FrameInfoIndex::InputEventId to
// equal android::os::IInputConstants::INVALID_INPUT_EVENT_ID == 0.
// Therefore, we can skip setting the value for InputEventId here. If the value for
// INVALID_INPUT_EVENT_ID changes, this code would have to be updated, as well.
set(FrameInfoIndex::FrameDeadline) = std::numeric_limits<int64_t>::max();
}
UiFrameInfoBuilder& setVsync(nsecs_t vsyncTime, nsecs_t intendedVsync,
int64_t vsyncId, int64_t frameDeadline, nsecs_t frameInterval) {
set(FrameInfoIndex::FrameTimelineVsyncId) = vsyncId;
set(FrameInfoIndex::Vsync) = vsyncTime;
set(FrameInfoIndex::IntendedVsync) = intendedVsync;
// Pretend the other fields are all at vsync, too, so that naive
// duration calculations end up being 0 instead of very large
set(FrameInfoIndex::HandleInputStart) = vsyncTime;
set(FrameInfoIndex::AnimationStart) = vsyncTime;
set(FrameInfoIndex::PerformTraversalsStart) = vsyncTime;
set(FrameInfoIndex::DrawStart) = vsyncTime;
set(FrameInfoIndex::FrameStartTime) = vsyncTime;
set(FrameInfoIndex::FrameDeadline) = frameDeadline;
set(FrameInfoIndex::FrameInterval) = frameInterval;
set(FrameInfoIndex::WorkloadTarget) = frameDeadline - intendedVsync;
return *this;
}
UiFrameInfoBuilder& addFlag(int frameInfoFlag) {
set(FrameInfoIndex::Flags) |= static_cast<uint64_t>(frameInfoFlag);
return *this;
}
private:
inline int64_t& set(FrameInfoIndex index) { return mBuffer[static_cast<int>(index)]; }
int64_t* mBuffer;
};
class FrameInfo {
public:
void importUiThreadInfo(const int64_t* info);
void markSyncStart() { set(FrameInfoIndex::SyncStart) = systemTime(SYSTEM_TIME_MONOTONIC); }
void markIssueDrawCommandsStart() {
set(FrameInfoIndex::IssueDrawCommandsStart) = systemTime(SYSTEM_TIME_MONOTONIC);
}
void markSwapBuffers() { set(FrameInfoIndex::SwapBuffers) = systemTime(SYSTEM_TIME_MONOTONIC); }
void markSwapBuffersCompleted() {
set(FrameInfoIndex::SwapBuffersCompleted) = systemTime(SYSTEM_TIME_MONOTONIC);
}
void markFrameCompleted() { set(FrameInfoIndex::FrameCompleted) = systemTime(SYSTEM_TIME_MONOTONIC); }
void addFlag(int frameInfoFlag) {
set(FrameInfoIndex::Flags) |= static_cast<uint64_t>(frameInfoFlag);
}
const FrameInfoBuffer& data() const { return mFrameInfo; }
inline int64_t operator[](FrameInfoIndex index) const { return get(index); }
inline int64_t operator[](int index) const {
if (index < 0 || index >= static_cast<int>(FrameInfoIndex::NumIndexes)) return 0;
return mFrameInfo[index];
}
inline int64_t duration(FrameInfoIndex start, FrameInfoIndex end) const {
int64_t endtime = get(end);
int64_t starttime = get(start);
int64_t gap = endtime - starttime;
gap = starttime > 0 ? gap : 0;
if (end > FrameInfoIndex::SyncQueued && start < FrameInfoIndex::SyncQueued) {
// Need to subtract out the time spent in a stalled state
// as this will be captured by the previous frame's info
int64_t offset = get(FrameInfoIndex::SyncStart) - get(FrameInfoIndex::SyncQueued);
if (offset > 0) {
gap -= offset;
}
}
return gap > 0 ? gap : 0;
}
inline int64_t totalDuration() const {
return duration(FrameInfoIndex::IntendedVsync, FrameInfoIndex::FrameCompleted);
}
inline int64_t gpuDrawTime() const {
// GPU start time is approximated to the moment before swapBuffer is invoked.
// We could add an EGLSyncKHR fence at the beginning of the frame, but that is an overhead.
int64_t endTime = get(FrameInfoIndex::GpuCompleted);
return endTime > 0 ? endTime - get(FrameInfoIndex::SwapBuffers) : -1;
}
inline int64_t& set(FrameInfoIndex index) { return mFrameInfo[static_cast<int>(index)]; }
inline int64_t get(FrameInfoIndex index) const {
if (index == FrameInfoIndex::NumIndexes) return 0;
return mFrameInfo[static_cast<int>(index)];
}
void setSkippedFrameReason(SkippedFrameReason reason);
inline std::optional<SkippedFrameReason> getSkippedFrameReason() const {
return mSkippedFrameReason;
}
private:
FrameInfoBuffer mFrameInfo;
std::optional<SkippedFrameReason> mSkippedFrameReason;
};
} /* namespace uirenderer */
} /* namespace android */
#endif /* FRAMEINFO_H_ */