add GamePerformanceSdk

This commit is contained in:
StarBeats 2025-02-21 10:30:52 +08:00
parent 0976656c34
commit d221880aaa
10 changed files with 378 additions and 0 deletions

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ac3a1b0385b933a4ba752182b9aff19e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f2a5b10838cdc174f87fbc9ad15c12fe
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application android:appCategory="game">
<activity android:name="com.x.gamesdk.GameActivity"
android:theme="@style/UnityThemeSelector">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
</activity>
</application>
</manifest>

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8255a3bd1c76aaf469a428a56ef5debf
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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());
}
}

View File

@ -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:

View File

@ -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<AndroidJavaObject>("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);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fb094df10d730994d95a79fd42061d77
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}

View File

@ -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: