Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ADPF] PerformanceHintAPI - include core threads + use NDK API #16610

Merged
merged 7 commits into from
Dec 28, 2023
11 changes: 11 additions & 0 deletions native/cocos/base/threading/MessageQueue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
#include "MessageQueue.h"
#include "AutoReleasePool.h"
#include "base/Utils.h"
#include "base/Log.h"

#if CC_PLATFORM == CC_PLATFORM_ANDROID
#include <unistd.h>
#include "platform/android/adpf_manager.h"
#endif

namespace cc {

Expand Down Expand Up @@ -272,6 +278,11 @@ MessageQueue::~MessageQueue() {
}

void MessageQueue::consumerThreadLoop() noexcept {
#if CC_PLATFORM == CC_PLATFORM_ANDROID && CC_SUPPORT_ADPF == 1
// add tid to PerformanceHintManager
int32_t tid = gettid();
ADPFManager::getInstance().AddThreadIdToHintSession(tid);
#endif
while (!_reader.terminateConsumerThread) {
AutoReleasePool autoReleasePool;
flushMessages();
Expand Down
117 changes: 104 additions & 13 deletions native/cocos/platform/android/adpf_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#if CC_PLATFORM == CC_PLATFORM_ANDROID && __ANDROID_API__ >= 30

#include <algorithm>
#include <unistd.h>
#include <chrono>
#include <cstdio>
Expand Down Expand Up @@ -157,12 +158,30 @@ float ADPFManager::UpdateThermalStatusHeadRoom() {
thermal_headroom_ =
env->CallFloatMethod(obj_power_service_, get_thermal_headroom_,
kThermalHeadroomUpdateThreshold);

if (!std::isnan(thermal_headroom_)) {
thermal_headroom_valid_ = thermal_headroom_;
}

ALOGE("Current thermal Headroom %f", thermal_headroom_);
return thermal_headroom_;
}

// Initialize JNI calls for the PowerHintManager.
bool ADPFManager::InitializePerformanceHintManager() {
#if __ANDROID_API__ >= 33
if ( hint_manager_ == nullptr ) {
hint_manager_ = APerformanceHint_getManager();
}
if ( hint_session_ == nullptr && hint_manager_ != nullptr ) {
int32_t tid = gettid();
thread_ids_.push_back(tid);
int32_t tids[1];
tids[0] = tid;
hint_session_ = APerformanceHint_createSession(hint_manager_, tids, 1, last_target_);
}
return true;
#else
JNIEnv *env = cc::JniHelper::getEnv();
auto *javaGameActivity = cc::JniHelper::getActivity();

Expand All @@ -185,23 +204,25 @@ bool ADPFManager::InitializePerformanceHintManager() {

// Retrieve methods IDs for the APIs.
jclass cls_perfhint_service = env->GetObjectClass(obj_perfhint_service_);
jmethodID mid_createhintsession =
create_hint_session_ =
env->GetMethodID(cls_perfhint_service, "createHintSession",
"([IJ)Landroid/os/PerformanceHintManager$Session;");
jmethodID mid_preferedupdaterate = env->GetMethodID(
cls_perfhint_service, "getPreferredUpdateRateNanos", "()J");

// Create int array which contain current tid.
jintArray array = env->NewIntArray(1);
int32_t tid = getpid();
int32_t tid = gettid();
thread_ids_.push_back(tid);
env->SetIntArrayRegion(array, 0, 1, &tid);
const jlong DEFAULT_TARGET_NS = 16666666;

// Create Hint session for the thread.
jobject obj_hintsession = env->CallObjectMethod(
obj_perfhint_service_, mid_createhintsession, array, DEFAULT_TARGET_NS);
jobject obj_hintsession = env->CallObjectMethod(obj_perfhint_service_, create_hint_session_, array, DEFAULT_TARGET_NS);
jboolean check = env->ExceptionCheck();
CC_LOG_DEBUG("ADPFManager::InitializePerformanceHintManager threadId: %ld gettid: %d getpid: %ld %d %x", std::this_thread::get_id(), gettid(), getpid(), check, obj_hintsession);
if (obj_hintsession == nullptr) {
ALOGI("Failed to create a perf hint session.");
CC_LOG_DEBUG("Failed First to create a perf hint session.");
} else {
obj_perfhint_session_ = env->NewGlobalRef(obj_hintsession);
preferred_update_rate_ =
Expand All @@ -213,6 +234,14 @@ bool ADPFManager::InitializePerformanceHintManager() {
cls_perfhint_session, "reportActualWorkDuration", "(J)V");
update_target_work_duration_ = env->GetMethodID(
cls_perfhint_session, "updateTargetWorkDuration", "(J)V");
set_threads_ = env->GetMethodID(
cls_perfhint_session, "setThreads", "([I)V");
check = env->ExceptionCheck();
if ( check ) {
env->ExceptionDescribe();
env->ExceptionClear();
set_threads_ = nullptr;
}
}

// Free local references
Expand All @@ -229,6 +258,7 @@ bool ADPFManager::InitializePerformanceHintManager() {
}

return true;
#endif
}

thermalStateChangeListener ADPFManager::thermalListener = NULL;
Expand All @@ -249,24 +279,85 @@ void ADPFManager::SetThermalListener(thermalStateChangeListener listener) {
// Indicates the start and end of the performance intensive task.
// The methods call performance hint API to tell the performance
// hint to the system.
void ADPFManager::BeginPerfHintSession() { perfhintsession_start_ = std::chrono::high_resolution_clock::now(); }
void ADPFManager::BeginPerfHintSession() {
perf_start_ = std::chrono::steady_clock::now();
}

void ADPFManager::EndPerfHintSession(jlong target_duration_ns) {
auto current_clock = std::chrono::high_resolution_clock::now();
auto duration = current_clock - perfhintsession_start_;
frame_time_ns_ = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count();
#if __ANDROID_API__ >= 33
auto perf_end = std::chrono::steady_clock::now();
auto dur = std::chrono::duration_cast<std::chrono::nanoseconds>(perf_end - perf_start_).count();
int64_t actual_duration_ns = static_cast<int64_t>(dur);
APerformanceHint_reportActualWorkDuration(hint_session_, actual_duration_ns);
APerformanceHint_updateTargetWorkDuration(hint_session_, target_duration_ns);
#else
auto perf_end = std::chrono::steady_clock::now();
auto dur = std::chrono::duration_cast<std::chrono::nanoseconds>(perf_end - perf_start_).count();
jlong actual_duration_ns = static_cast<jlong>(dur);
if (obj_perfhint_session_) {
jlong duration_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
duration * 100000000)
.count();
auto *env = cc::JniHelper::getEnv();

// Report and update the target work duration using JNI calls.
env->CallVoidMethod(obj_perfhint_session_, report_actual_work_duration_,
duration_ns);
actual_duration_ns);
env->CallVoidMethod(obj_perfhint_session_, update_target_work_duration_,
target_duration_ns);
}
#endif
}
void ADPFManager::AddThreadIdToHintSession(int32_t tid) {
thread_ids_.push_back(tid);
auto data = thread_ids_.data();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems data is not used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleaned up in 1d6c8b9


RegisterThreadIdsToHintSession();
}

void ADPFManager::RemoveThreadIdFromHintSession(int32_t tid) {
thread_ids_.erase(std::remove(thread_ids_.begin(), thread_ids_.end(), tid), thread_ids_.end());
auto data = thread_ids_.data();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleaned up data in 1d6c8b9


RegisterThreadIdsToHintSession();
}

void ADPFManager::RegisterThreadIdsToHintSession() {
#if __ANDROID_API__ >= 34
auto data = thread_ids_.data();
std::size_t size = thread_ids_.size();
int result = APerformanceHint_setThreads(hint_session_, data, size);
CC_LOG_INFO("ADPFManager::RegisterThreadIdsToHintSession result: %d", result);
#elif __ANDROID_API__ >= 33
auto data = thread_ids_.data();
std::size_t size = thread_ids_.size();
int result = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems result is only used in CC_LOG_INFO ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleaned up variables in 1d6c8b9

if ( hint_session_ != nullptr ) {
APerformanceHint_closeSession(hint_session_);
}
hint_session_ = APerformanceHint_createSession(hint_manager_, data, size, last_target_);
CC_LOG_INFO("ADPFManager::RegisterThreadIdsToHintSession result: %d newHint: %x", result, hint_session_);
#else
JNIEnv *env = cc::JniHelper::getEnv();
std::size_t size = thread_ids_.size();
jintArray array = env->NewIntArray(size);
auto data = thread_ids_.data();
env->SetIntArrayRegion(array, 0, size, data);
if ( set_threads_ == nullptr ) {
// we have to recreate the hint session
if ( obj_perfhint_session_ ) {
env->DeleteGlobalRef(obj_perfhint_session_ );
}
const jlong DEFAULT_TARGET_NS = 16666666;
jobject obj_hintsession = env->CallObjectMethod(obj_perfhint_service_, create_hint_session_, array, DEFAULT_TARGET_NS);
obj_perfhint_session_ = env->NewGlobalRef(obj_hintsession);
} else {
// API Level 34
env->CallVoidMethod(obj_perfhint_session_, set_threads_, array);
jboolean check = env->ExceptionCheck();
if ( check ) {
env->ExceptionDescribe();
env->ExceptionClear();
}
}
#endif
}

#endif
31 changes: 29 additions & 2 deletions native/cocos/platform/android/adpf_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,21 @@
*/

#ifndef ADPF_MANAGER_H_
#define ADPF_MANAGER_H_

Check failure on line 18 in native/cocos/platform/android/adpf_manager.h

View workflow job for this annotation

GitHub Actions / ClangTidy Android

invalid case style for macro definition 'ADPF_MANAGER_H_' (readability-identifier-naming)

#if CC_PLATFORM == CC_PLATFORM_ANDROID && __ANDROID_API__ >= 30
#include <android/api-level.h>
#include <android/log.h>
#include <android/thermal.h>
#if __ANDROID_API__ >= 33
#include <ctime>
#include <android/performance_hint.h>
#endif
#include <jni.h>

#include <chrono>
#include <memory>
#include <thread>
#include "3d/models/SkinningModel.h"
#include "engine/EngineEvents.h"
#include "platform/java/jni/JniHelper.h"
Expand Down Expand Up @@ -62,6 +67,12 @@
AThermal_releaseManager(thermal_manager_);
}
}

// clean all hint sessions
for ( const auto& kv : map_hint_sessions ) {
jobject global_hint_session = kv.second;
env->DeleteGlobalRef(global_hint_session);
}
}

// Delete copy constructor since the class is used as a singleton.
Expand Down Expand Up @@ -96,6 +107,9 @@

void EndPerfHintSession(jlong target_duration_ns);

void AddThreadIdToHintSession(int32_t tid);
void RemoveThreadIdFromHintSession(int32_t tid);

// Method to retrieve thermal manager. The API is used to register/unregister
// callbacks from C API.
AThermalManager *GetThermalManager() { return thermal_manager_; }
Expand All @@ -122,7 +136,6 @@
update_target_work_duration_(0),
preferred_update_rate_(0) {
last_clock_ = std::chrono::high_resolution_clock::now();
perfhintsession_start_ = std::chrono::high_resolution_clock::now();
}

// Functions to initialize ADPF API's calls.
Expand All @@ -132,6 +145,8 @@

bool InitializePerformanceHintManager();

void RegisterThreadIdsToHintSession();

AThermalManager *thermal_manager_ = nullptr;
int32_t thermal_status_;
float thermal_headroom_ = 0;
Expand All @@ -140,17 +155,29 @@
jobject obj_power_service_;
jmethodID get_thermal_headroom_;

std::map<std::string, jobject> map_hint_sessions;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is better to use std::unordered_map<> if the order is not important.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, order isn't important, only the key value mapping is, changed in 1d6c8b9


jobject obj_perfhint_service_;
jobject obj_perfhint_session_;
jmethodID create_hint_session_;
jmethodID set_threads_;
jmethodID report_actual_work_duration_;
jmethodID update_target_work_duration_;
jlong preferred_update_rate_;

cc::events::BeforeTick::Listener beforeTick;
cc::events::AfterTick::Listener afterTick;

std::chrono::time_point<std::chrono::high_resolution_clock> perfhintsession_start_;
int64_t frame_time_ns_{0};

std::vector<int32_t> thread_ids_;
std::chrono::time_point<std::chrono::steady_clock> perf_start_;

#if __ANDROID_API__ >= 33
APerformanceHintManager *hint_manager_ = nullptr;
APerformanceHintSession *hint_session_ = nullptr;
int64_t last_target_ = 16666666;
#endif
};

#define CC_SUPPORT_ADPF 1 // NOLINT
Expand Down
Loading