From 6b4ad9183a04a34cc19c4977a28c4b0422793733 Mon Sep 17 00:00:00 2001 From: Patrice Jiang <397136899@qq.com> Date: Fri, 27 Oct 2023 14:34:27 +0800 Subject: [PATCH] add adpf bindings --- @types/jsb.d.ts | 176 ++++++------ cocos/native-binding/impl.ts | 1 + cocos/native-binding/index.ts | 54 +++- native/CMakeLists.txt | 2 + native/cocos/application/BaseGame.cpp | 7 + native/cocos/bindings/manual/jsb_global.cpp | 165 +++++++++-- native/cocos/core/Root.h | 8 +- native/cocos/engine/Engine.cpp | 8 +- native/cocos/engine/EngineEvents.h | 2 + .../cocos/platform/android/adpf_manager.cpp | 272 ++++++++++++++++++ native/cocos/platform/android/adpf_manager.h | 161 +++++++++++ 11 files changed, 737 insertions(+), 119 deletions(-) create mode 100644 native/cocos/platform/android/adpf_manager.cpp create mode 100644 native/cocos/platform/android/adpf_manager.h diff --git a/@types/jsb.d.ts b/@types/jsb.d.ts index 2c6347e1994..5a8d24fbc1f 100644 --- a/@types/jsb.d.ts +++ b/@types/jsb.d.ts @@ -64,13 +64,13 @@ declare namespace jsb { wheelDeltaX: number, wheelDeltaY: number, } - type MouseWheelEventCallback = (mouseEvent: MouseWheelEvent) => void; + type MouseWheelEventCallback = (mouseEvent: MouseWheelEvent) => void; export let onMouseDown: MouseEventCallback | undefined; export let onMouseMove: MouseEventCallback | undefined; export let onMouseUp: MouseEventCallback | undefined; export let onMouseWheel: MouseWheelEventCallback | undefined; - type TouchEventCallback = (touchList: TouchList, windowId?: number) => void; + type TouchEventCallback = (touchList: TouchList, windowId?: number) => void; export let onTouchStart: TouchEventCallback | undefined; export let onTouchMove: TouchEventCallback | undefined; export let onTouchEnd: TouchEventCallback | undefined; @@ -123,7 +123,7 @@ declare namespace jsb { } type KeyboardEventCallback = (keyboardEvent: KeyboardEvent) => void; export let onKeyDown: KeyboardEventCallback | undefined; - export let onKeyUp: KeyboardEventCallback| undefined; + export let onKeyUp: KeyboardEventCallback | undefined; export interface WindowEvent { windowId: number; @@ -136,7 +136,7 @@ declare namespace jsb { * @zh WindowEvent.width 和 WindowEvent.height 都已乘以 DPR */ export let onResize: (event: WindowEvent) => void | undefined; - export let onOrientationChanged: (event: {orientation: number}) => void | undefined; // TODO: enum orientation type + export let onOrientationChanged: (event: { orientation: number }) => void | undefined; // TODO: enum orientation type export let onResume: () => void | undefined; export let onPause: () => void | undefined; export let onClose: () => void | undefined; @@ -161,43 +161,43 @@ declare namespace jsb { channelCount: number; } export namespace AudioEngine { - export function preload (url: string, cb: (isSuccess: boolean) => void); - - export function play2d (url: string, loop: boolean, volume: number): number; - export function pause (id: number); - export function pauseAll (); - export function resume (id: number); - export function resumeAll (); - export function stop (id: number); - export function stopAll (); - - export function getPlayingAudioCount (): number; - export function getMaxAudioInstance (): number; - export function getState (id: number): any; - export function getDuration (id: number): number; - export function getVolume (id: number): number; - export function isLoop (id: number): boolean; - export function getCurrentTime (id: number): number; - - export function setVolume (id: number, val: number); - export function setLoop (id: number, val: boolean); - export function setCurrentTime (id: number, val: number); - - export function uncache (url: string); - export function uncacheAll (); - export function setErrorCallback (id: number, cb: (err: any) => void); - export function setFinishCallback (id: number, cb: () => void); + export function preload(url: string, cb: (isSuccess: boolean) => void); + + export function play2d(url: string, loop: boolean, volume: number): number; + export function pause(id: number); + export function pauseAll(); + export function resume(id: number); + export function resumeAll(); + export function stop(id: number); + export function stopAll(); + + export function getPlayingAudioCount(): number; + export function getMaxAudioInstance(): number; + export function getState(id: number): any; + export function getDuration(id: number): number; + export function getVolume(id: number): number; + export function isLoop(id: number): boolean; + export function getCurrentTime(id: number): number; + + export function setVolume(id: number, val: number); + export function setLoop(id: number, val: boolean); + export function setCurrentTime(id: number, val: number); + + export function uncache(url: string); + export function uncacheAll(); + export function setErrorCallback(id: number, cb: (err: any) => void); + export function setFinishCallback(id: number, cb: () => void); /** * Get PCM header without pcm data. if you want to get pcm data, use getOriginalPCMBuffer instead */ - export function getPCMHeader (url: string): PCMHeader; + export function getPCMHeader(url: string): PCMHeader; /** * Get PCM Data in decode format for example Int16Array, the format information is written in PCMHeader. * @param url: file relative path, for example player._path * @param channelID: ChannelID which should smaller than channel count, start from 0 */ - export function getOriginalPCMBuffer (url: string, channelID: number): ArrayBuffer | undefined; + export function getOriginalPCMBuffer(url: string, channelID: number): ArrayBuffer | undefined; } class NativePOD { @@ -230,22 +230,22 @@ declare namespace jsb { } export class Manifest { - constructor (manifestUrl: string); - constructor (content: string, manifestRoot: string); - parseFile (manifestUrl: string): void; - parseJSONString (content: string, manifestRoot: string): void; + constructor(manifestUrl: string); + constructor(content: string, manifestRoot: string); + parseFile(manifestUrl: string): void; + parseJSONString(content: string, manifestRoot: string): void; - getManifestRoot (): string; - getManifestFileUrl (): string; - getVersionFileUrl (): string; - getSearchPaths (): [string]; - getVersion (): string; - getPackageUrl (): boolean; + getManifestRoot(): string; + getManifestFileUrl(): string; + getVersionFileUrl(): string; + getSearchPaths(): [string]; + getVersion(): string; + getPackageUrl(): boolean; - setUpdating (isUpdating: boolean): void; - isUpdating (): boolean; - isVersionLoaded (): boolean; - isLoaded (): boolean; + setUpdating(isUpdating: boolean): void; + isUpdating(): boolean; + isVersionLoaded(): boolean; + isLoaded(): boolean; } export class EventAssetsManager { @@ -262,23 +262,23 @@ declare namespace jsb { static UPDATE_FAILED: number; static ERROR_DECOMPRESS: number; - constructor (eventName: string, manager: AssetsManager, eventCode: number, + constructor(eventName: string, manager: AssetsManager, eventCode: number, assetId?: string, message?: string, curleCode?: number, curlmCode?: number); - getAssetsManagerEx (): AssetsManager; - isResuming (): boolean; + getAssetsManagerEx(): AssetsManager; + isResuming(): boolean; - getDownloadedFiles (): number; - getDownloadedBytes (): number; - getTotalFiles (): number; - getTotalBytes (): number; - getPercent (): number; - getPercentByFile (): number; + getDownloadedFiles(): number; + getDownloadedBytes(): number; + getTotalFiles(): number; + getTotalBytes(): number; + getPercent(): number; + getPercentByFile(): number; - getEventCode (): number; - getMessage (): string; - getAssetId (): string; - getCURLECode (): number; - getCURLMCode (): number; + getEventCode(): number; + getMessage(): string; + getAssetId(): string; + getCURLECode(): number; + getCURLMCode(): number; } export namespace AssetsManager { @@ -301,30 +301,30 @@ declare namespace jsb { } export class AssetsManager { - constructor (manifestUrl: string, storagePath: string, versionCompareHandle?: (versionA: string, versionB: string) => number); - static create (manifestUrl: string, storagePath: string): AssetsManager; + constructor(manifestUrl: string, storagePath: string, versionCompareHandle?: (versionA: string, versionB: string) => number); + static create(manifestUrl: string, storagePath: string): AssetsManager; - getState (): AssetsManager.State; - getStoragePath (): string - getMaxConcurrentTask (): number; + getState(): AssetsManager.State; + getStoragePath(): string + getMaxConcurrentTask(): number; // setMaxConcurrentTask (max: number): void; // actually not supported - checkUpdate (): void; - prepareUpdate (): void; - update (): void; - isResuming (): boolean; + checkUpdate(): void; + prepareUpdate(): void; + update(): void; + isResuming(): boolean; - getDownloadedFiles (): number; - getDownloadedBytes (): number; - getTotalFiles (): number; - getTotalBytes (): number; - downloadFailedAssets (): void; + getDownloadedFiles(): number; + getDownloadedBytes(): number; + getTotalFiles(): number; + getTotalBytes(): number; + downloadFailedAssets(): void; - getLocalManifest (): Manifest; - loadLocalManifest (manifestUrl: string): boolean; - loadLocalManifest (localManifest: Manifest, storagePath: string): boolean; - getRemoteManifest (): Manifest; - loadRemoteManifest (remoteManifest: Manifest): boolean; + getLocalManifest(): Manifest; + loadLocalManifest(manifestUrl: string): boolean; + loadLocalManifest(localManifest: Manifest, storagePath: string): boolean; + getRemoteManifest(): Manifest; + loadRemoteManifest(remoteManifest: Manifest): boolean; /** * Setup your own version compare handler, versionA and B is versions in string. @@ -332,13 +332,23 @@ declare namespace jsb { * if the return value equals 0, versionA equals to B, * if the return value smaller than 0, versionA is smaller than B. */ - setVersionCompareHandle (versionCompareHandle?: (versionA: string, versionB: string) => number): void; + setVersionCompareHandle(versionCompareHandle?: (versionA: string, versionB: string) => number): void; /** * Setup the verification callback, Return true if the verification passed, otherwise return false */ - setVerifyCallback (verifyCallback: (path: string, asset: ManifestAsset) => boolean): void; - setEventCallback (eventCallback: (event: EventAssetsManager) => void): void; - } + setVerifyCallback(verifyCallback: (path: string, asset: ManifestAsset) => boolean): void; + setEventCallback(eventCallback: (event: EventAssetsManager) => void): void; + } + + // Android ADPF module + const adpf: { + readonly thermalHeadroom: number; + readonly thermalStatus: number; + readonly thermalStatusMin: number; + readonly thermalStatusMax: number; + readonly thermalStatusNormalized: number; + onThermalStatusChanged?: (previousStatus: number, newStatus: number, statusMin: number, statusMax: number) => void; + } | undefined; } declare namespace ns { diff --git a/cocos/native-binding/impl.ts b/cocos/native-binding/impl.ts index 6bba0aaefe2..471aefdd038 100644 --- a/cocos/native-binding/impl.ts +++ b/cocos/native-binding/impl.ts @@ -156,4 +156,5 @@ export const native = { Manifest: globalJsb.Manifest, saveImageData: globalJsb.saveImageData, process: globalJsb.process, + adpf: globalJsb.adpf, }; diff --git a/cocos/native-binding/index.ts b/cocos/native-binding/index.ts index c48c439b698..4855a69c68b 100644 --- a/cocos/native-binding/index.ts +++ b/cocos/native-binding/index.ts @@ -381,7 +381,7 @@ export declare namespace native { * @param storagePath @en Storage path for downloaded file @zh 下载文件存储路径 * @param identifier @en identifier @zh 标识符 */ - export type DownloadTask = { requestURL: string, storagePath: string, identifier: string }; + export interface DownloadTask { requestURL: string, storagePath: string, identifier: string } /** * @en DownloaderTask @zh 下载任务对象 @@ -390,7 +390,7 @@ export declare namespace native { * @param identifier @en identifier @zh 标识符 * @deprecated since v3.7.0, please use `DownloadTask` to instead. */ - export type DownloaderTask = { requestURL: string, storagePath: string, identifier: string }; + export interface DownloaderTask { requestURL: string, storagePath: string, identifier: string } /** * @en DownloaderHints @zh 下载任务的配置接口 @@ -1401,4 +1401,54 @@ export declare namespace native { */ export const argv: Readonly; } + + /** + * @en This object provides properties related to thermal characteristics and an optional callback function to track changes in thermal status. + * It is supported only on Android platforms with an API level of 31 or higher. + * @zh 该对象提供与热特性相关的属性以及用于跟踪热状态变化的可选回调函数。仅支持 API 等级为 31 或更高的 Android 平台。 + * + * @see https://developer.android.com/ndk/reference/group/thermal#group___thermal_1ga1055f6c8d5910a1904162bea75807314 + */ + const adpf: { + /** + * @en A number indicating the current thermal headroom. The value can exceed 1. + * @zh 表示当前热头寸余量数字。该值可以超过1 + * @see https://developer.android.com/ndk/reference/group/thermal#group___thermal_1ga1055f6c8d5910a1904162bea75807314 + */ + readonly thermalHeadroom: number; + /** + * @en A number indicating the current thermal status + * @zh 表示当前热状态的数字 + */ + readonly thermalStatus: number; + /** + * @en A number indicating the minimum threshold for thermal status + * @zh 表示热状态的最大阈值的数字 + */ + readonly thermalStatusMin: number; + /** + * @en A number indicating the maximum threshold for thermal status + * @zh 表示热状态的最大阈值的数字 + */ + readonly thermalStatusMax: number; + /** + * @en A normalized value of the current thermal status. It's computed based on the formula: + * (thermalStatus - thermalStatusMax) / thermalStatusMax. + * This value ranges between 0 and 1, giving a relative measure of the current thermal status against its minimum and maximum thresholds. + * @zh 当前热状态的归一化值,范围在 0 到 1 之间. 它是基于以下公式计算的: (thermalStatus - thermalStatusMax) / thermalStatusMax. + * 提供了当前热状态相对于其最小和最大阈值的相对测量。 + */ + readonly thermalStatusNormalized: number; + /** + * @en An optional callback function that is triggered when the thermal status changes + * @zh 该对象提供与热特性相关的属性以及用于跟踪热状态变化的可选回调函数 + * + * @param previousStatus @zh 之前的热状态 @en The previous thermal status + * @param newStatus @zh 更改后的新热状态 @en The new thermal status after the change + * @param statusMin @zh 热状态的最小阈值 @en The minimum threshold for thermal status + * @param statusMax @zh 热状态的最大阈值 @en The maximum threshold for thermal status + * @returns + */ + onThermalStatusChanged?: (previousStatus: number, newStatus: number, statusMin: number, statusMax: number) => void; + } | undefined; } diff --git a/native/CMakeLists.txt b/native/CMakeLists.txt index 7125a5ff234..29331989d7f 100644 --- a/native/CMakeLists.txt +++ b/native/CMakeLists.txt @@ -548,6 +548,8 @@ elseif(ANDROID) cocos/platform/android/AndroidPlatform.cpp cocos/platform/android/AndroidPlatform.h cocos/platform/android/AndroidKeyCodes.cpp + cocos/platform/android/adpf_manager.h + cocos/platform/android/adpf_manager.cpp ) elseif(OPENHARMONY) cocos_source_files( diff --git a/native/cocos/application/BaseGame.cpp b/native/cocos/application/BaseGame.cpp index fa7ee208c61..ecad44b3319 100644 --- a/native/cocos/application/BaseGame.cpp +++ b/native/cocos/application/BaseGame.cpp @@ -28,6 +28,9 @@ #include "platform/interfaces/modules/ISystemWindowManager.h" #include "renderer/pipeline/GlobalDescriptorSetManager.h" +#if CC_PLATFORM == CC_PLATFORM_ANDROID + #include "platform/android/adpf_manager.h" +#endif extern "C" void cc_load_all_plugins(); // NOLINT namespace cc { @@ -36,6 +39,10 @@ int BaseGame::init() { cc_load_all_plugins(); +#if (CC_PLATFORM == CC_PLATFORM_ANDROID) && CC_SUPPORT_ADPF + ADPFManager::getInstance().Initialize(); +#endif + #if CC_PLATFORM == CC_PLATFORM_WINDOWS || CC_PLATFORM == CC_PLATFORM_LINUX || CC_PLATFORM == CC_PLATFORM_QNX || CC_PLATFORM == CC_PLATFORM_MACOS // override default value //_windowInfo.x = _windowInfo.x == -1 ? 0 : _windowInfo.x; diff --git a/native/cocos/bindings/manual/jsb_global.cpp b/native/cocos/bindings/manual/jsb_global.cpp index 81a74027567..f46acea36f2 100644 --- a/native/cocos/bindings/manual/jsb_global.cpp +++ b/native/cocos/bindings/manual/jsb_global.cpp @@ -24,6 +24,7 @@ #include "jsb_global.h" #include "application/ApplicationManager.h" +#include "base/Assertf.h" #include "base/Data.h" #include "base/DeferredReleasePool.h" #include "base/Scheduler.h" @@ -42,12 +43,13 @@ #include "ui/edit-box/EditBox.h" #include "xxtea/xxtea.h" -#if CC_PLATFORM == CC_PLATFORM_ANDROID - #include "platform/java/jni/JniImp.h" -#endif #include #include #include +#if CC_PLATFORM == CC_PLATFORM_ANDROID + #include "platform/android/adpf_manager.h" + #include "platform/java/jni/JniImp.h" +#endif using namespace cc; // NOLINT @@ -213,7 +215,7 @@ static bool doModuleRequire(const ccstd::string &path, se::Value *ret, const ccs const ccstd::string &reletivePath = fullPath; #endif - auto se = se::ScriptEngine::getInstance(); + auto *se = se::ScriptEngine::getInstance(); bool succeed = se->evalString(scriptBuffer.c_str(), static_cast(scriptBuffer.length()), nullptr, reletivePath.c_str()); se::Value moduleVal; if (succeed && se->getGlobalObject()->getProperty("module", &moduleVal) && moduleVal.isObject()) { @@ -351,7 +353,7 @@ static bool JSBCore_os(se::State &s) { // NOLINT SE_BIND_FUNC(JSBCore_os) static bool JSBCore_getCurrentLanguage(se::State &s) { // NOLINT - ISystem *systemIntf = CC_GET_PLATFORM_INTERFACE(ISystem); + auto *systemIntf = CC_GET_PLATFORM_INTERFACE(ISystem); CC_ASSERT_NOT_NULL(systemIntf); ccstd::string languageStr = systemIntf->getCurrentLanguageToString(); s.rval().setString(languageStr); @@ -360,7 +362,7 @@ static bool JSBCore_getCurrentLanguage(se::State &s) { // NOLINT SE_BIND_FUNC(JSBCore_getCurrentLanguage) static bool JSBCore_getCurrentLanguageCode(se::State &s) { // NOLINT - ISystem *systemIntf = CC_GET_PLATFORM_INTERFACE(ISystem); + auto *systemIntf = CC_GET_PLATFORM_INTERFACE(ISystem); CC_ASSERT_NOT_NULL(systemIntf); ccstd::string language = systemIntf->getCurrentLanguageCode(); s.rval().setString(language); @@ -369,7 +371,7 @@ static bool JSBCore_getCurrentLanguageCode(se::State &s) { // NOLINT SE_BIND_FUNC(JSBCore_getCurrentLanguageCode) static bool JSB_getOSVersion(se::State &s) { // NOLINT - ISystem *systemIntf = CC_GET_PLATFORM_INTERFACE(ISystem); + auto *systemIntf = CC_GET_PLATFORM_INTERFACE(ISystem); CC_ASSERT_NOT_NULL(systemIntf); ccstd::string systemVersion = systemIntf->getSystemVersion(); s.rval().setString(systemVersion); @@ -394,13 +396,13 @@ static bool JSB_core_restartVM(se::State &s) { // NOLINT } SE_BIND_FUNC(JSB_core_restartVM) -static bool JSB_closeWindow(se::State &s) { +static bool JSB_closeWindow(se::State &s) { // NOLINT CC_CURRENT_APPLICATION()->close(); return true; } SE_BIND_FUNC(JSB_closeWindow) -static bool JSB_exit(se::State &s) { +static bool JSB_exit(se::State &s) { // NOLINT BasePlatform::getPlatform()->exit(); return true; } @@ -675,7 +677,7 @@ static bool js_loadImage(se::State &s) { // NOLINT ok &= sevalue_to_native(args[0], &path); SE_PRECONDITION2(ok, false, "Error processing arguments"); - se::Value callbackVal = args[1]; + const auto &callbackVal = args[1]; CC_ASSERT(callbackVal.isObject()); CC_ASSERT(callbackVal.toObject()->isFunction()); @@ -697,8 +699,8 @@ static bool js_saveImageData(se::State &s) { // NOLINT uint8ArrayObj->root(); uint8ArrayObj->incRef(); uint8ArrayObj->getTypedArrayData(&uint8ArrayData, &length); - uint32_t width; - uint32_t height; + int32_t width; + int32_t height; ok &= sevalue_to_native(args[1], &width); ok &= sevalue_to_native(args[2], &height); @@ -770,7 +772,7 @@ static bool JSB_openURL(se::State &s) { // NOLINT ccstd::string url; ok = sevalue_to_native(args[0], &url); SE_PRECONDITION2(ok, false, "url is invalid!"); - ISystem *systemIntf = CC_GET_PLATFORM_INTERFACE(ISystem); + auto *systemIntf = CC_GET_PLATFORM_INTERFACE(ISystem); CC_ASSERT_NOT_NULL(systemIntf); systemIntf->openURL(url); return true; @@ -789,7 +791,7 @@ static bool JSB_copyTextToClipboard(se::State &s) { // NOLINT ccstd::string text; ok = sevalue_to_native(args[0], &text); SE_PRECONDITION2(ok, false, "text is invalid!"); - ISystem *systemIntf = CC_GET_PLATFORM_INTERFACE(ISystem); + auto *systemIntf = CC_GET_PLATFORM_INTERFACE(ISystem); CC_ASSERT_NOT_NULL(systemIntf); systemIntf->copyTextToClipboard(text); return true; @@ -892,13 +894,13 @@ static bool JSB_showInputBox(se::State &s) { // NOLINT if (obj->getProperty("fontSize", &tmp)) { SE_PRECONDITION2(tmp.isNumber(), false, "fontSize is invalid!"); if (!tmp.isUndefined()) { - showInfo.fontSize = tmp.toDouble(); + showInfo.fontSize = tmp.toUint32(); } } if (obj->getProperty("fontColor", &tmp)) { SE_PRECONDITION2(tmp.isNumber(), false, "fontColor is invalid!"); if (!tmp.isUndefined()) { - showInfo.fontColor = tmp.toDouble(); + showInfo.fontColor = tmp.toUint32(); } } if (obj->getProperty("isBold", &tmp)) { @@ -922,25 +924,25 @@ static bool JSB_showInputBox(se::State &s) { // NOLINT if (obj->getProperty("underlineColor", &tmp)) { SE_PRECONDITION2(tmp.isNumber(), false, "underlinrColor is invalid!"); if (!tmp.isUndefined()) { - showInfo.underlineColor = tmp.toDouble(); + showInfo.underlineColor = tmp.toUint32(); } } if (obj->getProperty("backColor", &tmp)) { SE_PRECONDITION2(tmp.isNumber(), false, "backColor is invalid!"); if (!tmp.isUndefined()) { - showInfo.backColor = tmp.toDouble(); + showInfo.backColor = tmp.toUint32(); } } if (obj->getProperty("backgroundColor", &tmp)) { SE_PRECONDITION2(tmp.isNumber(), false, "backgroundColor is invalid!"); if (!tmp.isUndefined()) { - showInfo.backgroundColor = tmp.toDouble(); + showInfo.backgroundColor = tmp.toUint32(); } } if (obj->getProperty("textAlignment", &tmp)) { SE_PRECONDITION2(tmp.isNumber(), false, "textAlignment is invalid!"); if (!tmp.isUndefined()) { - showInfo.textAlignment = tmp.toDouble(); + showInfo.textAlignment = tmp.toUint32(); } } EditBox::show(showInfo); @@ -960,7 +962,7 @@ SE_BIND_FUNC(JSB_hideInputBox) #endif -static bool jsb_createExternalArrayBuffer(se::State &s) { +static bool jsb_createExternalArrayBuffer(se::State &s) { // NOLINT const auto &args = s.args(); size_t argc = args.size(); CC_UNUSED bool ok = true; @@ -1019,7 +1021,7 @@ static bool JSB_zipUtils_inflateMemory(se::State &s) { // NOLINT } SE_PRECONDITION2(ok, false, "args[0] is not in type of string | ArrayBuffer | TypedArray"); unsigned char *arg2 = nullptr; - int32_t len = 0; + uint32_t len = 0; if (argc == 1) { len = ZipUtils::inflateMemory(arg0, static_cast(arg1), &arg2); } else if (argc == 2) { @@ -1269,9 +1271,9 @@ static bool JSB_zipUtils_setPvrEncryptionKey(se::State &s) { // NOLINT SE_BIND_FUNC(JSB_zipUtils_setPvrEncryptionKey) // TextEncoder -static se::Class *__jsb_TextEncoder_class = nullptr; +static se::Class *__jsb_TextEncoder_class = nullptr; // NOLINT -static bool js_TextEncoder_finalize(se::State &s) // NOLINT(readability-identifier-naming) +static bool js_TextEncoder_finalize(se::State &) // NOLINT { return true; } @@ -1314,9 +1316,9 @@ static bool js_TextEncoder_encode(se::State &s) // NOLINT(readability-identifier SE_BIND_FUNC(js_TextEncoder_encode) // TextDecoder -static se::Class *__jsb_TextDecoder_class = nullptr; +static se::Class *__jsb_TextDecoder_class = nullptr; // NOLINT -static bool js_TextDecoder_finalize(se::State &s) // NOLINT(readability-identifier-naming) +static bool js_TextDecoder_finalize(se::State &) // NOLINT { return true; } @@ -1363,7 +1365,7 @@ static bool js_TextDecoder_decode(se::State &s) // NOLINT(readability-identifier } SE_BIND_FUNC(js_TextDecoder_decode) -static bool jsb_register_TextEncoder(se::Object *globalObj) { +static bool jsb_register_TextEncoder(se::Object *globalObj) { // NOLINT auto *cls = se::Class::create("TextEncoder", globalObj, nullptr, _SE(js_TextEncoder_constructor)); cls->defineFunction("encode", _SE(js_TextEncoder_encode)); cls->defineFinalizeFunction(_SE(js_TextEncoder_finalize)); @@ -1375,7 +1377,7 @@ static bool jsb_register_TextEncoder(se::Object *globalObj) { return true; } -static bool jsb_register_TextDecoder(se::Object *globalObj) { +static bool jsb_register_TextDecoder(se::Object *globalObj) { // NOLINT auto *cls = se::Class::create("TextDecoder", globalObj, nullptr, _SE(js_TextDecoder_constructor)); cls->defineFunction("decode", _SE(js_TextDecoder_decode)); cls->defineFinalizeFunction(_SE(js_TextDecoder_finalize)); @@ -1387,6 +1389,110 @@ static bool jsb_register_TextDecoder(se::Object *globalObj) { return true; } +#if CC_PLATFORM == CC_PLATFORM_ANDROID && CC_SUPPORT_ADPF + +struct VmCallback { + uint32_t vmId{0xFEFEFEFE}; + se::Value *cbFn{nullptr}; + void reset() { + vmId = 0xFEFEFEFE; + delete cbFn; + cbFn = nullptr; + } +}; +static VmCallback vmCallback; +static bool jsb_adpf_onThermalStatusChanged_set(se::State &state) { // NOLINT + + auto fn = state.args()[0]; + vmCallback.reset(); + if (fn.isNullOrUndefined()) { + return true; + } + CC_ASSERT_TRUE(fn.toObject()->isFunction()); + auto *scriptEngine = se::ScriptEngine::getInstance(); + if (vmCallback.vmId != scriptEngine->getVMId()) { + vmCallback.vmId = scriptEngine->getVMId(); + scriptEngine->addBeforeCleanupHook([]() { + vmCallback.reset(); + }); + } + + vmCallback.cbFn = new se::Value(fn.toObject(), true); + // NOLINTNEXTLINE + ADPFManager::getInstance().SetThermalListener(+[](int prevStatus, int currentStatus) { + CC_CURRENT_ENGINE()->getScheduler()->performFunctionInCocosThread([=]() { + se::AutoHandleScope scope; + se::ValueArray args; + args.push_back(se::Value(prevStatus)); + args.push_back(se::Value(currentStatus)); + args.push_back(se::Value(ATHERMAL_STATUS_NONE)); + args.push_back(se::Value(ATHERMAL_STATUS_SHUTDOWN)); + CC_ASSERT_EQ(vmCallback.vmId, se::ScriptEngine::getInstance()->getVMId()); + if (vmCallback.cbFn && vmCallback.cbFn->isObject() && vmCallback.cbFn->toObject()->isFunction()) { + vmCallback.cbFn->toObject()->call(args, nullptr); + } + }); + }); + return true; +} +SE_BIND_PROP_SET(jsb_adpf_onThermalStatusChanged_set) + +static bool jsb_adpf_onThermalStatusChanged_get(se::State &state) { // NOLINT + if (!vmCallback.cbFn) { + state.rval().setUndefined(); + } else { + state.rval().setObject(vmCallback.cbFn->toObject()); + } + return true; +} +SE_BIND_PROP_GET(jsb_adpf_onThermalStatusChanged_get) + +static bool jsb_adpf_getThermalStatus(se::State &state) { // NOLINT + int statusInt = ADPFManager::getInstance().GetThermalStatus(); + state.rval().setUint32(statusInt); + return true; +} +SE_BIND_PROP_GET(jsb_adpf_getThermalStatus) + +static bool jsb_adpf_getThermalStatusMin(se::State &state) { // NOLINT + state.rval().setUint32(ATHERMAL_STATUS_NONE); + return true; +} +SE_BIND_PROP_GET(jsb_adpf_getThermalStatusMin) +static bool jsb_adpf_getThermalStatusMax(se::State &state) { // NOLINT + state.rval().setUint32(ATHERMAL_STATUS_SHUTDOWN); + return true; +} +SE_BIND_PROP_GET(jsb_adpf_getThermalStatusMax) + +static bool jsb_adpf_getThermalStatusNormalized(se::State &state) { // NOLINT + float statusNormalized = ADPFManager::getInstance().GetThermalStatusNormalized(); + state.rval().setFloat(statusNormalized); + return true; +} +SE_BIND_PROP_GET(jsb_adpf_getThermalStatusNormalized) + +static bool jsb_adpf_getThermalHeadroom(se::State &state) { // NOLINT + float headroom = ADPFManager::getInstance().GetThermalHeadroom(); + state.rval().setFloat(headroom); + return true; +} +SE_BIND_PROP_GET(jsb_adpf_getThermalHeadroom) + +static void jsb_register_ADPF(se::Object *ns) { // NOLINT + se::Value adpfObj{se::Object::createPlainObject()}; + adpfObj.toObject()->defineProperty("thermalHeadroom", _SE(jsb_adpf_getThermalHeadroom), nullptr); + adpfObj.toObject()->defineProperty("thermalStatus", _SE(jsb_adpf_getThermalStatus), nullptr); + adpfObj.toObject()->defineProperty("thermalStatusMin", _SE(jsb_adpf_getThermalStatusMin), nullptr); + adpfObj.toObject()->defineProperty("thermalStatusMax", _SE(jsb_adpf_getThermalStatusMax), nullptr); + adpfObj.toObject()->defineProperty("thermalStatusNormalized", _SE(jsb_adpf_getThermalStatusNormalized), nullptr); + adpfObj.toObject()->defineProperty("thermalHeadroom", _SE(jsb_adpf_getThermalHeadroom), nullptr); + adpfObj.toObject()->defineProperty("onThermalStatusChanged", _SE(jsb_adpf_onThermalStatusChanged_get), _SE(jsb_adpf_onThermalStatusChanged_set)); + ns->setProperty("adpf", adpfObj); +} + +#endif // CC_PLATFORM_ANDROID + static bool JSB_process_get_argv(se::State &s) // NOLINT(readability-identifier-naming) { const auto &args = CC_CURRENT_APPLICATION()->getArguments(); @@ -1460,6 +1566,9 @@ bool jsb_register_global_variables(se::Object *global) { // NOLINT jsb_register_TextEncoder(global); jsb_register_TextDecoder(global); +#if CC_PLATFORM == CC_PLATFORM_ANDROID && CC_SUPPORT_ADPF + jsb_register_ADPF(__jsbObj); +#endif se::ScriptEngine::getInstance()->clearException(); diff --git a/native/cocos/core/Root.h b/native/cocos/core/Root.h index 5020f11e2d9..f2b42b05c10 100644 --- a/native/cocos/core/Root.h +++ b/native/cocos/core/Root.h @@ -24,20 +24,20 @@ #pragma once #include -//#include "3d/skeletal-animation/DataPoolManager.h" +// #include "3d/skeletal-animation/DataPoolManager.h" #include "bindings/event/EventDispatcher.h" #include "core/event/Event.h" #include "core/memop/Pool.h" -#include "renderer/pipeline/RenderPipeline.h" #include "renderer/pipeline/DebugView.h" +#include "renderer/pipeline/RenderPipeline.h" #include "scene/DrawBatch2D.h" #include "scene/Light.h" #include "scene/Model.h" +#include "scene/PointLight.h" +#include "scene/RangedDirectionalLight.h" #include "scene/RenderScene.h" #include "scene/RenderWindow.h" #include "scene/SphereLight.h" -#include "scene/PointLight.h" -#include "scene/RangedDirectionalLight.h" namespace cc { class IXRInterface; diff --git a/native/cocos/engine/Engine.cpp b/native/cocos/engine/Engine.cpp index bae11828fdd..01ccdcc57db 100644 --- a/native/cocos/engine/Engine.cpp +++ b/native/cocos/engine/Engine.cpp @@ -275,7 +275,7 @@ void Engine::tick() { // iOS/macOS use its own fps limitation algorithm. // Windows for Editor should not sleep,because Editor call tick function synchronously -#if (CC_PLATFORM == CC_PLATFORM_ANDROID || (CC_PLATFORM == CC_PLATFORM_WINDOWS && !CC_EDITOR) || CC_PLATFORM == CC_PLATFORM_OHOS || CC_PLATFORM == CC_PLATFORM_OPENHARMONY || CC_PLATFORM == CC_PLATFORM_MACOS) +#if (CC_PLATFORM == CC_PLATFORM_ANDROID || (CC_PLATFORM == CC_PLATFORM_WINDOWS && !CC_EDITOR) || CC_PLATFORM == CC_PLATFORM_OHOS || CC_PLATFORM == CC_PLATFORM_OPENHARMONY || CC_PLATFORM == CC_PLATFORM_MACOS) if (dtNS < static_cast(_preferredNanosecondsPerFrame)) { CC_PROFILE(EngineSleep); std::this_thread::sleep_for( @@ -284,6 +284,8 @@ void Engine::tick() { } #endif + events::BeforeTick::broadcast(); + prevTime = std::chrono::steady_clock::now(); if (_xr) _xr->beginRenderFrame(); _scheduler->update(dt); @@ -297,6 +299,8 @@ void Engine::tick() { now = std::chrono::steady_clock::now(); dtNS = dtNS * 0.1 + 0.9 * static_cast(std::chrono::duration_cast(now - prevTime).count()); dt = static_cast(dtNS) / NANOSECONDS_PER_SECOND; + + events::AfterTick::broadcast(); } CC_PROFILER_END_FRAME; @@ -328,7 +332,7 @@ bool Engine::redirectWindowEvent(const WindowEvent &ev) { CC_ASSERT(w); w->setViewSize(ev.width, ev.height); // Because the ts layer calls the getviewsize interface in response to resize. - // So we need to set the view size when sending the message. + // So we need to set the view size when sending the message. events::Resize::broadcast(ev.width, ev.height, ev.windowId); isHandled = true; } else if (ev.type == WindowEvent::Type::HIDDEN || diff --git a/native/cocos/engine/EngineEvents.h b/native/cocos/engine/EngineEvents.h index e73cbaf4a0b..b5f63c894da 100644 --- a/native/cocos/engine/EngineEvents.h +++ b/native/cocos/engine/EngineEvents.h @@ -322,6 +322,8 @@ DECLARE_BUS_EVENT_ARG1(Keyboard, Engine, const cc::KeyboardEvent &) DECLARE_BUS_EVENT_ARG1(Controller, Engine, const cc::ControllerEvent &) DECLARE_BUS_EVENT_ARG1(ControllerChange, Engine, const cc::ControllerChangeEvent &) DECLARE_BUS_EVENT_ARG1(Tick, Engine, float) +DECLARE_BUS_EVENT_ARG0(BeforeTick, Engine) +DECLARE_BUS_EVENT_ARG0(AfterTick, Engine) DECLARE_BUS_EVENT_ARG3(Resize, Engine, int, int, uint32_t /* windowId*/) DECLARE_BUS_EVENT_ARG1(Orientation, Engine, int) DECLARE_BUS_EVENT_ARG1(PointerLock, Engine, bool) diff --git a/native/cocos/platform/android/adpf_manager.cpp b/native/cocos/platform/android/adpf_manager.cpp new file mode 100644 index 00000000000..2411ae01ee5 --- /dev/null +++ b/native/cocos/platform/android/adpf_manager.cpp @@ -0,0 +1,272 @@ +/* + * Copyright 2022 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 + * + * https://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. + */ + +#include "adpf_manager.h" + +#if CC_PLATFORM == CC_PLATFORM_ANDROID && __ANDROID_API__ >= 30 + + #include + #include + #include + #include + #include + #include + #include "java/jni/JniHelper.h" + + #define ALOGI(...) + #define ALOGE(...) + +// Invoke the method periodically (once a frame) to monitor +// the device's thermal throttling status. +void ADPFManager::Monitor() { + auto currentClock = std::chrono::high_resolution_clock::now(); + auto past = currentClock - last_clock_; + auto pastMS = std::chrono::duration_cast(past).count(); + + // if (current_clock - last_clock_ >= kThermalHeadroomUpdateThreshold) { + if (past > kThermalHeadroomUpdateThreshold) { + // Update thermal headroom. + // CC_LOG_INFO(" Monitor past %d ms", static_cast(pastMS)); + UpdateThermalStatusHeadRoom(); + last_clock_ = currentClock; + } +} + +float ADPFManager::GetThermalStatusNormalized() const { + if (thermal_manager_ == nullptr) { + return 0; + } + auto level = AThermal_getCurrentThermalStatus(thermal_manager_); + auto levelValue = (static_cast(level) - static_cast(ATHERMAL_STATUS_NONE)) * 1.0f / + static_cast(ATHERMAL_STATUS_SHUTDOWN); + return levelValue; +} + +// Invoke the API first to set the android_app instance. +void ADPFManager::Initialize() { + // Initialize PowerManager reference. + InitializePowerManager(); + + // Initialize PowerHintManager reference. + InitializePerformanceHintManager(); + + beforeTick.bind([&]() { + this->BeginPerfHintSession(); + this->Monitor(); + }); + + afterTick.bind([&]() { + auto frameDuration = std::chrono::duration_cast( + std::chrono::milliseconds(16)) + .count(); + this->EndPerfHintSession(frameDuration); + }); + + if (thermal_manager_) { + auto ret = AThermal_registerThermalStatusListener( + thermal_manager_, +[](void *data, AThermalStatus status) { + ADPFManager::getInstance().SetThermalStatus(status); + CC_LOG_INFO("Thermal Status :%d", static_cast(status)); + }, + nullptr); + ALOGI("Thermal Status callback registerred to:%d", ret); + } +} + +// Initialize JNI calls for the powermanager. +bool ADPFManager::InitializePowerManager() { + #if __ANDROID_API__ >= 31 + if (android_get_device_api_level() >= 31) { + // Initialize the powermanager using NDK API. + thermal_manager_ = AThermal_acquireManager(); + return true; + } + #endif + + JNIEnv *env = cc::JniHelper::getEnv(); + auto *javaGameActivity = cc::JniHelper::getActivity(); + + // Retrieve class information + jclass context = env->FindClass("android/content/Context"); + + // Get the value of a constant + jfieldID fid = + env->GetStaticFieldID(context, "POWER_SERVICE", "Ljava/lang/String;"); + jobject str_svc = env->GetStaticObjectField(context, fid); + + // Get the method 'getSystemService' and call it + jmethodID mid_getss = env->GetMethodID( + context, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); + jobject obj_power_service = env->CallObjectMethod(javaGameActivity, mid_getss, str_svc); + + // Add global reference to the power service object. + obj_power_service_ = env->NewGlobalRef(obj_power_service); + + jclass cls_power_service = env->GetObjectClass(obj_power_service_); + get_thermal_headroom_ = + env->GetMethodID(cls_power_service, "getThermalHeadroom", "(I)F"); + + // Free references + env->DeleteLocalRef(cls_power_service); + env->DeleteLocalRef(obj_power_service); + env->DeleteLocalRef(str_svc); + env->DeleteLocalRef(context); + + if (get_thermal_headroom_ == 0) { + // The API is not supported in the platform version. + return false; + } + + return true; +} + +// Retrieve current thermal headroom using JNI call. +float ADPFManager::UpdateThermalStatusHeadRoom() { + #if __ANDROID_API__ >= 31 + if (android_get_device_api_level() >= 31) { + // Use NDK API to retrieve thermal status headroom. + auto seconds = kThermalHeadroomUpdateThreshold.count(); + thermal_headroom_ = AThermal_getThermalHeadroom( + thermal_manager_, seconds); + if (!std::isnan(thermal_headroom_)) { + thermal_headroom_valid_ = thermal_headroom_; + } + return thermal_headroom_; + } + #endif + + if (get_thermal_headroom_ == 0) { + return 0.f; + } + JNIEnv *env = cc::JniHelper::getEnv(); + + // Get thermal headroom! + thermal_headroom_ = + env->CallFloatMethod(obj_power_service_, get_thermal_headroom_, + kThermalHeadroomUpdateThreshold); + ALOGE("Current thermal Headroom %f", thermal_headroom_); + return thermal_headroom_; +} + +// Initialize JNI calls for the PowerHintManager. +bool ADPFManager::InitializePerformanceHintManager() { + JNIEnv *env = cc::JniHelper::getEnv(); + auto *javaGameActivity = cc::JniHelper::getActivity(); + + // Retrieve class information + jclass context = env->FindClass("android/content/Context"); + + // Get the value of a constant + jfieldID fid = env->GetStaticFieldID(context, "PERFORMANCE_HINT_SERVICE", + "Ljava/lang/String;"); + jobject str_svc = env->GetStaticObjectField(context, fid); + + // Get the method 'getSystemService' and call it + jmethodID mid_getss = env->GetMethodID( + context, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); + jobject obj_perfhint_service = env->CallObjectMethod( + javaGameActivity, mid_getss, str_svc); + + // Add global reference to the power service object. + obj_perfhint_service_ = env->NewGlobalRef(obj_perfhint_service); + + // Retrieve methods IDs for the APIs. + jclass cls_perfhint_service = env->GetObjectClass(obj_perfhint_service_); + jmethodID mid_createhintsession = + 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(); + 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); + if (obj_hintsession == nullptr) { + ALOGI("Failed to create a perf hint session."); + } else { + obj_perfhint_session_ = env->NewGlobalRef(obj_hintsession); + preferred_update_rate_ = + env->CallLongMethod(obj_perfhint_service_, mid_preferedupdaterate); + + // Retrieve mid of Session APIs. + jclass cls_perfhint_session = env->GetObjectClass(obj_perfhint_session_); + report_actual_work_duration_ = env->GetMethodID( + cls_perfhint_session, "reportActualWorkDuration", "(J)V"); + update_target_work_duration_ = env->GetMethodID( + cls_perfhint_session, "updateTargetWorkDuration", "(J)V"); + } + + // Free local references + env->DeleteLocalRef(obj_hintsession); + env->DeleteLocalRef(array); + env->DeleteLocalRef(cls_perfhint_service); + env->DeleteLocalRef(obj_perfhint_service); + env->DeleteLocalRef(str_svc); + env->DeleteLocalRef(context); + + if (report_actual_work_duration_ == 0 || update_target_work_duration_ == 0) { + // The API is not supported in the platform version. + return false; + } + + return true; +} + +thermalStateChangeListener ADPFManager::thermalListener = NULL; + +void ADPFManager::SetThermalStatus(int32_t i) { + int32_t prev_status_ = thermal_status_; + int32_t current_status_ = i; + thermal_status_ = i; + if (thermalListener != NULL) { + thermalListener(prev_status_, current_status_); + } +} + +void ADPFManager::SetThermalListener(thermalStateChangeListener listener) { + thermalListener = 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::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(duration).count(); + if (obj_perfhint_session_) { + jlong duration_ns = std::chrono::duration_cast( + 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); + env->CallVoidMethod(obj_perfhint_session_, update_target_work_duration_, + target_duration_ns); + } +} + +#endif diff --git a/native/cocos/platform/android/adpf_manager.h b/native/cocos/platform/android/adpf_manager.h new file mode 100644 index 00000000000..9ef9bc8e23d --- /dev/null +++ b/native/cocos/platform/android/adpf_manager.h @@ -0,0 +1,161 @@ +/* + * Copyright 2022 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 + * + * https://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 ADPF_MANAGER_H_ +#define ADPF_MANAGER_H_ + +#if CC_PLATFORM == CC_PLATFORM_ANDROID && __ANDROID_API__ >= 30 + #include + #include + #include + #include + + #include + #include + #include "3d/models/SkinningModel.h" + #include "engine/EngineEvents.h" + #include "platform/java/jni/JniHelper.h" + +// Forward declarations of functions that need to be in C decl. + +typedef void (*thermalStateChangeListener)(int32_t, int32_t); + +/* + * ADPFManager class anages the ADPF APIs. + */ +class ADPFManager { +public: + // Singleton function. + static ADPFManager &getInstance() { + static ADPFManager instance; + return instance; + } + + // Dtor. + ~ADPFManager() { + // Remove global reference. + auto env = cc::JniHelper::getEnv(); + if (env != nullptr) { + if (obj_power_service_ != nullptr) { + env->DeleteGlobalRef(obj_power_service_); + } + if (obj_perfhint_service_ != nullptr) { + env->DeleteGlobalRef(obj_perfhint_service_); + } + if (obj_perfhint_session_ != nullptr) { + env->DeleteGlobalRef(obj_perfhint_session_); + } + if (thermal_manager_ != nullptr) { + AThermal_releaseManager(thermal_manager_); + } + } + } + + // Delete copy constructor since the class is used as a singleton. + ADPFManager(ADPFManager const &) = delete; + + void operator=(ADPFManager const &) = delete; + + // Invoke the method periodically (once a frame) to monitor + // the device's thermal throttling status. + void Monitor(); + + // Invoke the API first to set the android_app instance. + + // Method to set thermal status. Need to be public since the method + // is called from C native listener. + void SetThermalStatus(int32_t i); + + // Get current thermal status and headroom. + int32_t GetThermalStatus() { return thermal_status_; } + float GetThermalStatusNormalized() const; + + float GetFrameTimeMS() const { return frame_time_ns_ / 1000000.0F; } + + float GetThermalHeadroom() { return thermal_headroom_; } + + void 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 BeginPerfHintSession(); + + void EndPerfHintSession(jlong target_duration_ns); + + // Method to retrieve thermal manager. The API is used to register/unregister + // callbacks from C API. + AThermalManager *GetThermalManager() { return thermal_manager_; } + + void Initialize(); + +private: + // Update thermal headroom each sec. + static constexpr auto kThermalHeadroomUpdateThreshold = std::chrono::seconds(1); + + // Function pointer from the game, will be invoked when we receive state changed event from Thermal API + static thermalStateChangeListener thermalListener; + + // Ctor. It's private since the class is designed as a singleton. + ADPFManager() + : thermal_manager_(nullptr), + thermal_status_(0), + thermal_headroom_(0.f), + obj_power_service_(nullptr), + get_thermal_headroom_(0), + obj_perfhint_service_(nullptr), + obj_perfhint_session_(nullptr), + report_actual_work_duration_(0), + 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. + bool InitializePowerManager(); + + float UpdateThermalStatusHeadRoom(); + + bool InitializePerformanceHintManager(); + + AThermalManager *thermal_manager_ = nullptr; + int32_t thermal_status_; + float thermal_headroom_ = 0; + float thermal_headroom_valid_ = 0; + std::chrono::time_point last_clock_; + jobject obj_power_service_; + jmethodID get_thermal_headroom_; + + jobject obj_perfhint_service_; + jobject obj_perfhint_session_; + 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 perfhintsession_start_; + int64_t frame_time_ns_{0}; +}; + + #define CC_SUPPORT_ADPF 1 // NOLINT +#else + #define CC_SUPPORT_ADPF 0 // NOLINT +#endif // ADPF_MANAGER_H_ + +#endif