309 lines
14 KiB
C
309 lines
14 KiB
C
|
|
/* Copyright Statement:
|
||
|
|
*
|
||
|
|
* This software/firmware and related documentation ("MediaTek Software") are
|
||
|
|
* protected under relevant copyright laws. The information contained herein is
|
||
|
|
* confidential and proprietary to MediaTek Inc. and/or its licensors. Without
|
||
|
|
* the prior written permission of MediaTek inc. and/or its licensors, any
|
||
|
|
* reproduction, modification, use or disclosure of MediaTek Software, and
|
||
|
|
* information contained herein, in whole or in part, shall be strictly
|
||
|
|
* prohibited.
|
||
|
|
*
|
||
|
|
* MediaTek Inc. (C) 2022. All rights reserved.
|
||
|
|
*
|
||
|
|
* BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
|
||
|
|
* THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
|
||
|
|
* RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
|
||
|
|
* ON AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL
|
||
|
|
* WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
|
||
|
|
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
|
||
|
|
* NONINFRINGEMENT. NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH
|
||
|
|
* RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
|
||
|
|
* INCORPORATED IN, OR SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES
|
||
|
|
* TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
|
||
|
|
* RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
|
||
|
|
* OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN MEDIATEK
|
||
|
|
* SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE
|
||
|
|
* RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
|
||
|
|
* STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S
|
||
|
|
* ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE
|
||
|
|
* RELEASED HEREUNDER WILL BE, AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE
|
||
|
|
* MEDIATEK SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
|
||
|
|
* CHARGE PAID BY RECEIVER TO MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
|
||
|
|
*
|
||
|
|
* The following software/firmware and/or related documentation ("MediaTek
|
||
|
|
* Software") have been modified by MediaTek Inc. All revisions are subject to
|
||
|
|
* any receiver's applicable license agreements with MediaTek Inc.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#pragma once
|
||
|
|
|
||
|
|
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
|
||
|
|
|
||
|
|
#include <log/log.h>
|
||
|
|
#include <thread>
|
||
|
|
#include <utils/Trace.h>
|
||
|
|
#include <utils/Mutex.h>
|
||
|
|
#include <sys/syscall.h>
|
||
|
|
#include <android-base/properties.h>
|
||
|
|
#include <dlfcn.h>
|
||
|
|
#include <cutils/properties.h>
|
||
|
|
|
||
|
|
typedef int (*MBNotifyT4)(char* layerName, long long frameId, long long vsyncSfTime, long long t4, long long sfDuration,
|
||
|
|
long long vsyncPeriod, int abnormal, long long pendingBuffer, int decoupleMode);
|
||
|
|
typedef int (*MBGetTouchDebugEnabled)(int &enabled);
|
||
|
|
|
||
|
|
namespace android {
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
class FenceTracer
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
static bool isSupportMBrain() {
|
||
|
|
static bool enable = false;
|
||
|
|
static bool read = false;
|
||
|
|
if (!read) {
|
||
|
|
char platform[PROPERTY_VALUE_MAX] = {0};
|
||
|
|
property_get("ro.board.platform", platform, "");
|
||
|
|
if (strcmp(platform, "mt6985") == 0 ||
|
||
|
|
strcmp(platform, "mt6897") == 0 ||
|
||
|
|
strcmp(platform, "mt6989") == 0 ||
|
||
|
|
strcmp(platform, "mt6878")) {
|
||
|
|
enable = android::base::GetBoolProperty("debug.sf.mbrain_support", false);
|
||
|
|
} else {
|
||
|
|
enable = false;
|
||
|
|
}
|
||
|
|
read = true;
|
||
|
|
}
|
||
|
|
return enable;
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
FenceTracer() {
|
||
|
|
}
|
||
|
|
|
||
|
|
class FenceMonitor {
|
||
|
|
public:
|
||
|
|
explicit FenceMonitor(const char* name) : mName(name) {
|
||
|
|
if (isSupportMBrain()) {
|
||
|
|
mMBrainHandle = dlopen("libmbrainSDK.so", RTLD_LAZY);
|
||
|
|
if (mMBrainHandle != nullptr) {
|
||
|
|
char *error = nullptr;
|
||
|
|
dlerror();
|
||
|
|
mMBnotifyT4Func = reinterpret_cast<MBNotifyT4>(dlsym(mMBrainHandle, "NotifyT4InfoHook"));
|
||
|
|
if ((error = dlerror()) != nullptr) {
|
||
|
|
ALOGW("%s: dlsym(NotifyT4InfoHook) failed, err = %s", __func__, error);
|
||
|
|
} else {
|
||
|
|
ALOGI("%s: dlsym(NotifyT4InfoHook) success", __func__);
|
||
|
|
}
|
||
|
|
mMBGetTouchDebugEnabledFunc = reinterpret_cast<MBGetTouchDebugEnabled>(dlsym(mMBrainHandle, "GetTouchDebugEnabledHook"));
|
||
|
|
if ((error = dlerror()) != nullptr) {
|
||
|
|
ALOGW("%s: dlsym(GetTouchDebugEnabledHook) failed, err = %s", __func__, error);
|
||
|
|
} else {
|
||
|
|
ALOGI("%s: dlsym(GetTouchDebugEnabledHook) success", __func__);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
ALOGW("%s: load MBrain so failed", __func__);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
std::thread thread(&FenceMonitor::loop, this);
|
||
|
|
pthread_setname_np(thread.native_handle(), mName);
|
||
|
|
thread.detach();
|
||
|
|
}
|
||
|
|
|
||
|
|
~FenceMonitor() {
|
||
|
|
if (isSupportMBrain()) {
|
||
|
|
if (mMBrainHandle != nullptr) {
|
||
|
|
dlclose(mMBrainHandle);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void queueFrameNumber(const std::string& layerName, const uint64_t frameNumber, const nsecs_t vsyncSfTime,
|
||
|
|
const int32_t pendingBufferCount, const bool bDecoupleMode, const nsecs_t sfDuration, const nsecs_t vsyncPeriod) {
|
||
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
||
|
|
auto it = mFrameByVsyncSf.find(vsyncSfTime);
|
||
|
|
if (it != mFrameByVsyncSf.end()) {
|
||
|
|
auto& vecLayerFrameNum = it->second;
|
||
|
|
vecLayerFrameNum.push_back(std::make_tuple(layerName, frameNumber, pendingBufferCount, bDecoupleMode, sfDuration, vsyncPeriod));
|
||
|
|
} else {
|
||
|
|
std::vector<std::tuple<std::string, uint64_t, int32_t, bool, int64_t, int64_t>> vecLayerFrameNum;
|
||
|
|
vecLayerFrameNum.push_back(std::make_tuple(layerName, frameNumber, pendingBufferCount, bDecoupleMode, sfDuration, vsyncPeriod));
|
||
|
|
mFrameByVsyncSf.emplace(vsyncSfTime, vecLayerFrameNum);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void queueFence(const sp<Fence>& fence, const nsecs_t vsyncSfTime = 0) {
|
||
|
|
char message[64];
|
||
|
|
int err = 0;
|
||
|
|
|
||
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
||
|
|
if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
|
||
|
|
err = snprintf(message, sizeof(message), "%s fence %d has signaled", mName, fence->get());
|
||
|
|
if (err < 0) {
|
||
|
|
ATRACE_NAME("SNPF1 error");
|
||
|
|
} else {
|
||
|
|
ATRACE_NAME(message);
|
||
|
|
}
|
||
|
|
// Need an increment on both to make the trace number correct.
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
err = snprintf(message, sizeof(message), "Trace %s fence %d", mName, fence->get());
|
||
|
|
if (err < 0) {
|
||
|
|
ATRACE_NAME("SNPF2 error");
|
||
|
|
} else {
|
||
|
|
ATRACE_NAME(message);
|
||
|
|
}
|
||
|
|
|
||
|
|
mQueue.push_back(std::make_pair(fence, vsyncSfTime));
|
||
|
|
mCondition.notify_one();
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
#pragma clang diagnostic push
|
||
|
|
#pragma clang diagnostic ignored "-Wmissing-noreturn"
|
||
|
|
void loop() {
|
||
|
|
struct sched_attr {
|
||
|
|
uint32_t size;
|
||
|
|
uint32_t sched_policy;
|
||
|
|
uint64_t sched_flags;
|
||
|
|
int32_t sched_nice;
|
||
|
|
uint32_t sched_priority;
|
||
|
|
uint64_t sched_runtime;
|
||
|
|
uint64_t sched_deadline;
|
||
|
|
uint64_t sched_period;
|
||
|
|
uint32_t sched_util_min;
|
||
|
|
uint32_t sched_util_max;
|
||
|
|
};
|
||
|
|
|
||
|
|
sched_attr attr = {};
|
||
|
|
attr.size = sizeof(attr);
|
||
|
|
|
||
|
|
attr.sched_flags = (SCHED_FLAG_KEEP_ALL | SCHED_FLAG_UTIL_CLAMP);
|
||
|
|
attr.sched_util_min = 0;
|
||
|
|
attr.sched_util_max = 1024;
|
||
|
|
attr.sched_priority = SCHED_RR;
|
||
|
|
|
||
|
|
syscall(__NR_sched_setattr, 0, &attr, 0);
|
||
|
|
|
||
|
|
while (true) {
|
||
|
|
threadLoop();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#pragma clang diagnostic pop
|
||
|
|
|
||
|
|
void threadLoop() {
|
||
|
|
sp<Fence> fence;
|
||
|
|
nsecs_t vsyncSfTime = 0;
|
||
|
|
std::vector<std::tuple<std::string, uint64_t, int32_t, bool, int64_t, int64_t>> vecLayerFrameNum;
|
||
|
|
{
|
||
|
|
std::unique_lock<std::mutex> lock(mMutex);
|
||
|
|
while (mQueue.empty()) {
|
||
|
|
mCondition.wait(lock);
|
||
|
|
}
|
||
|
|
fence = mQueue[0].first;
|
||
|
|
vsyncSfTime = mQueue[0].second;
|
||
|
|
}
|
||
|
|
{
|
||
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
||
|
|
auto it = mFrameByVsyncSf.find(vsyncSfTime);
|
||
|
|
if (it != mFrameByVsyncSf.end()) {
|
||
|
|
vecLayerFrameNum = it->second;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
char message[64];
|
||
|
|
int err = snprintf(message, sizeof(message), "waiting for %s %d", mName, fence->get());
|
||
|
|
if (err < 0) {
|
||
|
|
ATRACE_NAME("SNPF1 error");
|
||
|
|
} else {
|
||
|
|
ATRACE_NAME(message);
|
||
|
|
|
||
|
|
status_t result = fence->waitForever(message);
|
||
|
|
if (result != OK) {
|
||
|
|
ALOGE("Error waiting for fence: %d", result);
|
||
|
|
}
|
||
|
|
|
||
|
|
String8 strOutput;
|
||
|
|
auto now = systemTime();
|
||
|
|
auto fenceSignaledTime = fence->getSignalTime();
|
||
|
|
if (isSupportMBrain() && vsyncSfTime > 0) {
|
||
|
|
int touchDebugEnabled = 0;
|
||
|
|
for (auto [name, frame, pending, decouple, sfDuration, vsyncPeriod] : vecLayerFrameNum) {
|
||
|
|
strOutput.clear();
|
||
|
|
strOutput.appendFormat("%s:%d, now:%" PRId64 ", signaled:%" PRId64, mName, fence->get(), now, fenceSignaledTime);
|
||
|
|
strOutput.appendFormat(", vsync-sf:%" PRId64, vsyncSfTime);
|
||
|
|
strOutput.appendFormat(", (%s,%" PRIu64 ",%d,%d)", name.c_str(), frame, pending, decouple);
|
||
|
|
strOutput.appendFormat(", T4(sf2now):%" PRId64 ", T4(sf2signaled):%" PRId64, now-vsyncSfTime, fenceSignaledTime-vsyncSfTime);
|
||
|
|
strOutput.appendFormat(", sfDuration:%" PRId64 ", vsyncPeriod:%" PRId64, sfDuration, vsyncPeriod);
|
||
|
|
bool bAbnormal = false;
|
||
|
|
if (decouple) {
|
||
|
|
bAbnormal = ((now - vsyncSfTime) > (sfDuration + (vsyncPeriod * 3 / 2)));
|
||
|
|
} else {
|
||
|
|
bAbnormal = ((now - vsyncSfTime) > (sfDuration + (vsyncPeriod / 2)));
|
||
|
|
}
|
||
|
|
strOutput.appendFormat(", abnormal:%d", bAbnormal ? 1 : 0);
|
||
|
|
ATRACE_NAME(strOutput.c_str());
|
||
|
|
//ALOGI("%s", strOutput.c_str());
|
||
|
|
if (mMBGetTouchDebugEnabledFunc) {
|
||
|
|
mMBGetTouchDebugEnabledFunc(touchDebugEnabled);
|
||
|
|
}
|
||
|
|
if (touchDebugEnabled != 0 && mMBnotifyT4Func) {
|
||
|
|
mMBnotifyT4Func(const_cast<char*>(name.c_str()), frame, vsyncSfTime, now-vsyncSfTime, sfDuration, vsyncPeriod,
|
||
|
|
bAbnormal ? 1 : 0, pending, decouple ? 1 : 0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
strOutput.appendFormat("%s:%d, now:%" PRId64 ", signaled:%" PRId64, mName, fence->get(), now, fenceSignaledTime);
|
||
|
|
ATRACE_NAME(strOutput.c_str());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
||
|
|
mQueue.pop_front();
|
||
|
|
mFrameByVsyncSf.erase(vsyncSfTime);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const char* mName;
|
||
|
|
std::deque<std::pair<sp<Fence>, nsecs_t>> mQueue;
|
||
|
|
std::condition_variable mCondition;
|
||
|
|
std::mutex mMutex;
|
||
|
|
std::unordered_map<nsecs_t, std::vector<std::tuple<std::string, uint64_t, int32_t, bool, int64_t, int64_t>>>
|
||
|
|
mFrameByVsyncSf GUARDED_BY(mMutex);
|
||
|
|
void *mMBrainHandle = nullptr;
|
||
|
|
MBNotifyT4 mMBnotifyT4Func = nullptr;
|
||
|
|
MBGetTouchDebugEnabled mMBGetTouchDebugEnabledFunc = nullptr;
|
||
|
|
};
|
||
|
|
|
||
|
|
public:
|
||
|
|
static FenceTracer& getInstance() {
|
||
|
|
static FenceTracer gInstance;
|
||
|
|
return gInstance;
|
||
|
|
}
|
||
|
|
|
||
|
|
~FenceTracer() {};
|
||
|
|
|
||
|
|
static FenceMonitor& getPresentFenceMonitor() {
|
||
|
|
static FenceMonitor presentFenceMonitor("presentFence");
|
||
|
|
return presentFenceMonitor;
|
||
|
|
}
|
||
|
|
void trackFrameNumber(const std::string& layerName, const uint64_t frameNumber, const nsecs_t vsyncSfTime,
|
||
|
|
const int32_t pendingBufferCount, const bool bDecoupleMode, const nsecs_t sfDuration, const nsecs_t vsyncPeriod) {
|
||
|
|
if (!isSupportMBrain()) return;
|
||
|
|
FenceTracer::getPresentFenceMonitor().queueFrameNumber(layerName, frameNumber, vsyncSfTime, pendingBufferCount, bDecoupleMode, sfDuration, vsyncPeriod);
|
||
|
|
}
|
||
|
|
void trackPresentFence(const sp<Fence>& fence, const nsecs_t vsyncSfTime) {
|
||
|
|
if (!ATRACE_ENABLED() && !isSupportMBrain()) return;
|
||
|
|
FenceTracer::getPresentFenceMonitor().queueFence(fence, vsyncSfTime);
|
||
|
|
}
|
||
|
|
void trackScreenCaptureFence(const sp<Fence>& fence) {
|
||
|
|
static FenceMonitor trackScreenCaptureFence("screenCaptureFence");
|
||
|
|
trackScreenCaptureFence.queueFence(fence);
|
||
|
|
}
|
||
|
|
void trackWfdPresentFence(const sp<Fence>& fence) {
|
||
|
|
static FenceMonitor trackWfdPresentFence("WFD-Present");
|
||
|
|
trackWfdPresentFence.queueFence(fence);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
}; // namespace android
|