From d221880aaa44b891237c46eb7bb4338b277511f3 Mon Sep 17 00:00:00 2001 From: StarBeats <977663818@qq.com> Date: Fri, 21 Feb 2025 10:30:52 +0800 Subject: [PATCH] add GamePerformanceSdk --- Assets/GamePerformanceSdk/Plugins.meta | 8 + .../GamePerformanceSdk/Plugins/Android.meta | 8 + .../Plugins/Android/AndroidManifest.xml | 15 ++ .../Plugins/Android/AndroidManifest.xml.meta | 7 + .../Plugins/Android/GameActivity.java | 28 +++ .../Plugins/Android/GameActivity.java.meta | 32 ++++ .../Plugins/Android/GamePerformanceSdk.cs | 177 ++++++++++++++++++ .../Android/GamePerformanceSdk.cs.meta | 11 ++ .../Plugins/Android/GameSdk.java | 60 ++++++ .../Plugins/Android/GameSdk.java.meta | 32 ++++ 10 files changed, 378 insertions(+) create mode 100644 Assets/GamePerformanceSdk/Plugins.meta create mode 100644 Assets/GamePerformanceSdk/Plugins/Android.meta create mode 100644 Assets/GamePerformanceSdk/Plugins/Android/AndroidManifest.xml create mode 100644 Assets/GamePerformanceSdk/Plugins/Android/AndroidManifest.xml.meta create mode 100644 Assets/GamePerformanceSdk/Plugins/Android/GameActivity.java create mode 100644 Assets/GamePerformanceSdk/Plugins/Android/GameActivity.java.meta create mode 100644 Assets/GamePerformanceSdk/Plugins/Android/GamePerformanceSdk.cs create mode 100644 Assets/GamePerformanceSdk/Plugins/Android/GamePerformanceSdk.cs.meta create mode 100644 Assets/GamePerformanceSdk/Plugins/Android/GameSdk.java create mode 100644 Assets/GamePerformanceSdk/Plugins/Android/GameSdk.java.meta diff --git a/Assets/GamePerformanceSdk/Plugins.meta b/Assets/GamePerformanceSdk/Plugins.meta new file mode 100644 index 0000000..cb81a00 --- /dev/null +++ b/Assets/GamePerformanceSdk/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ac3a1b0385b933a4ba752182b9aff19e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GamePerformanceSdk/Plugins/Android.meta b/Assets/GamePerformanceSdk/Plugins/Android.meta new file mode 100644 index 0000000..618f19c --- /dev/null +++ b/Assets/GamePerformanceSdk/Plugins/Android.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f2a5b10838cdc174f87fbc9ad15c12fe +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GamePerformanceSdk/Plugins/Android/AndroidManifest.xml b/Assets/GamePerformanceSdk/Plugins/Android/AndroidManifest.xml new file mode 100644 index 0000000..f1d62c1 --- /dev/null +++ b/Assets/GamePerformanceSdk/Plugins/Android/AndroidManifest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/Assets/GamePerformanceSdk/Plugins/Android/AndroidManifest.xml.meta b/Assets/GamePerformanceSdk/Plugins/Android/AndroidManifest.xml.meta new file mode 100644 index 0000000..6abf0e4 --- /dev/null +++ b/Assets/GamePerformanceSdk/Plugins/Android/AndroidManifest.xml.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 8255a3bd1c76aaf469a428a56ef5debf +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GamePerformanceSdk/Plugins/Android/GameActivity.java b/Assets/GamePerformanceSdk/Plugins/Android/GameActivity.java new file mode 100644 index 0000000..e8a2308 --- /dev/null +++ b/Assets/GamePerformanceSdk/Plugins/Android/GameActivity.java @@ -0,0 +1,28 @@ +package com.x.gamesdk; + +import com.unity3d.player.UnityPlayerActivity; +import android.os.Bundle; +import android.os.Build; + +public class GameActivity extends UnityPlayerActivity { + + private String appendCommandLineArgument(String cmdLine, String arg) { + if (arg == null || arg.isEmpty()) + return cmdLine; + else if (cmdLine == null || cmdLine.isEmpty()) + return arg; + else + return cmdLine + " " + arg; + } + + @Override protected String updateUnityCommandLineArguments(String cmdLine) + { + return cmdLine; // 让 Unity 根据 PlayerSettings 选择图形 API + } + + @Override protected void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + new GameSdk(getBaseContext()); + } +} \ No newline at end of file diff --git a/Assets/GamePerformanceSdk/Plugins/Android/GameActivity.java.meta b/Assets/GamePerformanceSdk/Plugins/Android/GameActivity.java.meta new file mode 100644 index 0000000..e209c5a --- /dev/null +++ b/Assets/GamePerformanceSdk/Plugins/Android/GameActivity.java.meta @@ -0,0 +1,32 @@ +fileFormatVersion: 2 +guid: a38f11cc1647a8f44b8dbc93244d95f5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Android: Android + second: + enabled: 1 + settings: {} + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GamePerformanceSdk/Plugins/Android/GamePerformanceSdk.cs b/Assets/GamePerformanceSdk/Plugins/Android/GamePerformanceSdk.cs new file mode 100644 index 0000000..8de72cd --- /dev/null +++ b/Assets/GamePerformanceSdk/Plugins/Android/GamePerformanceSdk.cs @@ -0,0 +1,177 @@ +using System; +using UnityEngine; +using UnityEngine.Rendering; + +[DefaultExecutionOrder(-9000)] +public unsafe class GamePerformanceSdk : MonoBehaviour +{ + private static AndroidJavaObject s_GameSDK = null; + private static IntPtr s_GameSDKRawObjectID; + private static IntPtr s_SupportPerformanceHintMethodId; + private static IntPtr s_SetPerfHitTargetNanosfHintMethodId; + private static IntPtr s_ReportPerfHintMethodId; + private static IntPtr s_SupportGameStateMethodId; + private static IntPtr s_SetGameStateMethodId; + + private static jvalue[] s_NoArgs = new jvalue[0]; + + private jvalue[] ReportPerfHintMethodArgs = new jvalue[1]; + private jvalue[] SetGameStateMethodArgs = new jvalue[2]; + + private bool supportGameStateApi = true; + private bool supportPerformanceHintApi = true; + + private static IntPtr GetJavaMethodID(IntPtr classId, string name, string sig) + { + AndroidJNI.ExceptionClear(); + var mid = AndroidJNI.GetMethodID(classId, name, sig); + + IntPtr ex = AndroidJNI.ExceptionOccurred(); + if (ex != (IntPtr)0) + { + AndroidJNI.ExceptionDescribe(); + AndroidJNI.ExceptionClear(); + return (IntPtr)0; + } + else + { + return mid; + } + } + + public enum GameState + { + MODE_UNKNOWN = 0, + /** + * No mode means that the game is not in active play, for example the user is using the game + * menu. + */ + MODE_NONE = 1, + + /** + * Indicates if the game is in active, but interruptible, game play. + */ + MODE_GAMEPLAY_INTERRUPTIBLE = 2, + + /** + * Indicates if the game is in active user play mode, which is real time and cannot be + * interrupted. + */ + MODE_GAMEPLAY_UNINTERRUPTIBLE = 3, + + /** + * Indicates that the current content shown is not gameplay related. For example it can be an + * ad, a web page, a text, or a video. + */ + MODE_CONTENT = 4 + } + + private void Awake() + { + Application.targetFrameRate = 120; + + s_GameSDK = new AndroidJavaObject("com.x.gamesdk.GameSdk").CallStatic("getInstance"); + if (s_GameSDK != null) + { + Debug.Log("Init GamePerformanceSdk start."); + s_GameSDKRawObjectID = s_GameSDK.GetRawObject(); + + var classID = s_GameSDK.GetRawClass(); + s_SupportPerformanceHintMethodId = GetJavaMethodID(classID, "SupportPerformanceHint", "()Z"); + if (s_SupportPerformanceHintMethodId != IntPtr.Zero) + { + supportPerformanceHintApi = AndroidJNI.CallBooleanMethod(s_GameSDKRawObjectID, s_SupportPerformanceHintMethodId, s_NoArgs); + } + + s_ReportPerfHintMethodId = GetJavaMethodID(classID, "ReportPerfHint", "(J)V"); + if (s_ReportPerfHintMethodId != IntPtr.Zero) + { + Debug.Log("found ReportPerfHint."); + } + + s_SetPerfHitTargetNanosfHintMethodId = GetJavaMethodID(classID, "SetPerfHitTargetNanos", "(J)V"); + if (s_SetPerfHitTargetNanosfHintMethodId != IntPtr.Zero) + { + Debug.Log("found SetPerfHitTargetNanos."); + SetTargetFps(120); + } + + s_SupportGameStateMethodId = GetJavaMethodID(classID, "SupportGameState", "()Z"); + if (s_SupportGameStateMethodId != IntPtr.Zero) + { + supportGameStateApi = AndroidJNI.CallBooleanMethod(s_GameSDKRawObjectID, s_SupportGameStateMethodId, s_NoArgs); + } + + s_SetGameStateMethodId = GetJavaMethodID(classID, "SetGameState", "(ZI)V"); + if (s_SetGameStateMethodId != IntPtr.Zero) + { + Debug.Log("found SetGameState."); + } + + Debug.Log($"Init GamePerformanceSdk done. supportGameStateApi:{supportGameStateApi} supportPerformanceHintApi:{supportPerformanceHintApi}"); + } + } + + private void OnEnable() + { + RenderPipelineManager.endFrameRendering += OnEndFrameRendering; + } + + float time = 0; + System.Diagnostics.Stopwatch stopwatch = new(); + + // XXX: insert to early update + private void Update() + { + time = Time.realtimeSinceStartup; + stopwatch.Restart(); + } + + private void OnEndFrameRendering(ScriptableRenderContext context, Camera[] arg2) + { + var dt = Time.realtimeSinceStartup - time; + stopwatch.Stop(); + if (s_ReportPerfHintMethodId != IntPtr.Zero) + { + ReportPerfHintMethodArgs[0] = new jvalue() { j = stopwatch.ElapsedMilliseconds * 1000 * 1000 }; + AndroidJNI.CallVoidMethod(s_GameSDKRawObjectID, s_ReportPerfHintMethodId, ReportPerfHintMethodArgs); + } + } + + private void OnDisable() + { + RenderPipelineManager.endFrameRendering -= OnEndFrameRendering; + } + + public void SetTargetFps(int fps) + { + ReportPerfHintMethodArgs[0] = new jvalue() { j = (long)(1000f / fps * 1000 * 1000) }; + AndroidJNI.CallVoidMethod(s_GameSDKRawObjectID, s_SetPerfHitTargetNanosfHintMethodId, ReportPerfHintMethodArgs); + } + + public void SetGameState(bool isLoading, GameState gameState) + { + SetGameStateMethodArgs[0] = new jvalue() { z = isLoading }; + SetGameStateMethodArgs[1] = new jvalue() { i = (int)gameState }; + + AndroidJNI.CallVoidMethod(s_GameSDKRawObjectID, s_SetGameStateMethodId, SetGameStateMethodArgs); + } + + private void OnGUI() + { + if (GUI.Button(new Rect(100, 100, 150, 50), "SetLoding")) + { + SetGameState(true, GameState.MODE_GAMEPLAY_INTERRUPTIBLE); + } + + if (GUI.Button(new Rect(100, 180, 150, 50), "SetUninterruptible")) + { + SetGameState(false, GameState.MODE_GAMEPLAY_UNINTERRUPTIBLE); + } + + if (GUI.Button(new Rect(100, 260, 150, 50), "SetUnknown")) + { + SetGameState(false, GameState.MODE_UNKNOWN); + } + } +} diff --git a/Assets/GamePerformanceSdk/Plugins/Android/GamePerformanceSdk.cs.meta b/Assets/GamePerformanceSdk/Plugins/Android/GamePerformanceSdk.cs.meta new file mode 100644 index 0000000..1eb6d3d --- /dev/null +++ b/Assets/GamePerformanceSdk/Plugins/Android/GamePerformanceSdk.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fb094df10d730994d95a79fd42061d77 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GamePerformanceSdk/Plugins/Android/GameSdk.java b/Assets/GamePerformanceSdk/Plugins/Android/GameSdk.java new file mode 100644 index 0000000..41fc044 --- /dev/null +++ b/Assets/GamePerformanceSdk/Plugins/Android/GameSdk.java @@ -0,0 +1,60 @@ +package com.x.gamesdk; + +import android.app.*; +import android.content.Context; +import android.os.Build; +import android.os.PerformanceHintManager; +import android.os.Process; +import android.util.Log; + +public class GameSdk { + private static final String LOG_TAG = "GamePerformacneSDK"; + private static GameSdk instane; + + private final Context context; + private PerformanceHintManager.Session performanceHintSession; + + public static GameSdk getInstance() { + return instane; + } + + public GameSdk(Context context) { + instane = this; + this.context = context; + Log.d(LOG_TAG, "new Game Performance sdk.SupportGameState:" + SupportGameState() + "|Build.VERSION.SDK_INT:" + Build.VERSION.SDK_INT); + + try { + PerformanceHintManager performanceHintManager = context.getSystemService(PerformanceHintManager.class); + performanceHintSession = performanceHintManager.createHintSession(new int[]{Process.myTid()}, 16666666); + Log.d(LOG_TAG, "create performance Hint Session." + performanceHintSession + "|tid:" + Process.myTid()); + } catch (Exception e) { + Log.d(LOG_TAG, "create performance Hint err:" + e.getMessage()); + } + } + + public boolean SupportGameState() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU; + } + + public boolean SupportPerformanceHint() { + return performanceHintSession != null; + } + + public void SetGameState(boolean isLoading, int gameState) { + try { + GameManager gameManager = context.getSystemService(GameManager.class); + if (SupportGameState()) { + gameManager.setGameState(new GameState(isLoading, gameState)); + Log.d(LOG_TAG, "set game state."); + } + } catch (Exception ignored) { + } + } + + public void SetPerfHitTargetNanos( long targetNs) { + performanceHintSession.updateTargetWorkDuration(targetNs); + } + public void ReportPerfHint(long durantionNs) { + performanceHintSession.reportActualWorkDuration(durantionNs); + } +} diff --git a/Assets/GamePerformanceSdk/Plugins/Android/GameSdk.java.meta b/Assets/GamePerformanceSdk/Plugins/Android/GameSdk.java.meta new file mode 100644 index 0000000..fc27c5f --- /dev/null +++ b/Assets/GamePerformanceSdk/Plugins/Android/GameSdk.java.meta @@ -0,0 +1,32 @@ +fileFormatVersion: 2 +guid: 5b395be1482ba044c827eb603f632eca +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Android: Android + second: + enabled: 1 + settings: {} + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + userData: + assetBundleName: + assetBundleVariant: