com.amanotes.gdk 0.2.66-1 → 0.2.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/Extra/AmaGDKInstaller.unitypackage +0 -0
- package/Extra/CheckDiskSpace.unitypackage +0 -0
- package/Extra/ForceUpdate.unitypackage +0 -0
- package/Extra/GoogleCMP.unitypackage +0 -0
- package/Extra/LegacyGDKUpdateHelper.unitypackage +0 -0
- package/Extra/PostProcessor.unitypackage +0 -0
- package/Packages/AmaGDKConfig.unitypackage +0 -0
- package/Packages/AmaGDKExample.unitypackage +0 -0
- package/Packages/AmaGDKTest.unitypackage +0 -0
- package/Packages/AppsFlyerAdapter.PurchaseConnector.unitypackage +0 -0
- package/Packages/AppsFlyerAdapter.unitypackage +0 -0
- package/Packages/FirebaseAnalyticsAdapter.unitypackage +0 -0
- package/Packages/FirebaseRemoteConfigAdapter.unitypackage +0 -0
- package/Packages/IronSourceAdapter.unitypackage +0 -0
- package/Packages/RevenueCatAdapter.unitypackage +0 -0
- package/Packages/SqliteAnalyticsAdapter.unitypackage +0 -0
- package/Runtime/Ad/AdLogic.cs +3 -4
- package/Runtime/Ad/AdModuleConfig.cs +1 -1
- package/Runtime/AmaGDK.Analytics.cs +5 -1
- package/Runtime/AmaGDK.Mono.cs +0 -1
- package/Runtime/AmaGDK.cs +1 -1
- package/Runtime/Fps/AmaFPS.Utils.cs +44 -0
- package/Runtime/Fps/AmaFPS.Utils.cs.meta +3 -0
- package/Runtime/Fps/AmaFPS.cs +197 -0
- package/Runtime/Fps/AmaFPS.cs.meta +11 -0
- package/Runtime/Fps/AmaFPSVisualizer.cs +246 -0
- package/Runtime/Fps/AmaFPSVisualizer.cs.meta +11 -0
- package/Runtime/Fps/BollingerBands.cs +99 -0
- package/Runtime/Fps/BollingerBands.cs.meta +11 -0
- package/Runtime/Fps/FPSVisualizer.prefab +406 -0
- package/Runtime/Fps/FPSVisualizer.prefab.meta +7 -0
- package/Runtime/Fps/FrameRecorder.cs +172 -0
- package/Runtime/Fps/FrameRecorder.cs.meta +3 -0
- package/Runtime/Fps.meta +8 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.67 - 2024-07-04]
|
|
4
|
+
- [Fix] Do retry immediately when OnAdLoadFailed trigger regardless of reason
|
|
5
|
+
- Also: Update default ad request timeout to 30 secs
|
|
6
|
+
- [Feature] Support FPS tracking
|
|
7
|
+
- [Release] 0.2.66-1
|
|
8
|
+
- [Dev] Add a flag to control flushEventDetail (save to file)
|
|
9
|
+
- [Fix] Remove device spec call from constructor of AmaGDK
|
|
10
|
+
- [Feature] classify device performance
|
|
11
|
+
- [Dev] Update GoogleCMP events
|
|
12
|
+
- + cmp_popup_show
|
|
13
|
+
- + cmp_popup_finish
|
|
14
|
+
- [Dev] Handle a special case when Ad close callback is not triggered
|
|
15
|
+
- [Dev] Automatically reimport adapters & extra packages on update
|
|
16
|
+
|
|
3
17
|
## [0.2.66 - 2024-06-25]
|
|
4
18
|
- [Dev] Verify all fields are reset in Init EventParams
|
|
5
19
|
- [Dev] Add try catch to prevent coroutines to stop silently
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/Runtime/Ad/AdLogic.cs
CHANGED
|
@@ -128,8 +128,8 @@ namespace Amanotes.Core.Internal
|
|
|
128
128
|
// Show Ad logic
|
|
129
129
|
internal ShowAdsState _showState = ShowAdsState.None;
|
|
130
130
|
private Coroutine _showAdRoutine;
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
protected bool _isRequesting;
|
|
132
|
+
protected bool _isInterstitial => adType == AdType.Interstitial;
|
|
133
133
|
|
|
134
134
|
public void StartShowAd()
|
|
135
135
|
{
|
|
@@ -521,11 +521,10 @@ namespace Amanotes.Core.Internal
|
|
|
521
521
|
_isRequesting = false;
|
|
522
522
|
}
|
|
523
523
|
|
|
524
|
-
|
|
525
|
-
|
|
526
524
|
protected void OnAdLoadFailed(string error)
|
|
527
525
|
{
|
|
528
526
|
Log($"[Ad] {adType} OnAdLoadFailed: {error}");
|
|
527
|
+
_isRequesting = false;
|
|
529
528
|
}
|
|
530
529
|
}
|
|
531
530
|
}
|
|
@@ -6,7 +6,7 @@ namespace Amanotes.Core.Internal
|
|
|
6
6
|
[Serializable] public class AdModuleConfig
|
|
7
7
|
{
|
|
8
8
|
[Tooltip("Automatically retry requesting ad if an ad failed to response in this amount of time (in secs)")]
|
|
9
|
-
public int adLoadTimeoutInSecs =
|
|
9
|
+
public int adLoadTimeoutInSecs = 30;
|
|
10
10
|
|
|
11
11
|
[Tooltip("When an ad is not ready to show, keep waiting for this amount of time (in secs)\n\nIf the ad is ready during that time, it would be shown normally.")]
|
|
12
12
|
public int adShowTimeoutInSecs = 5;
|
|
@@ -306,7 +306,7 @@ namespace Amanotes.Core
|
|
|
306
306
|
internal static readonly ConcurrentQueue<EventParams> eventQueue = new ConcurrentQueue<EventParams>();
|
|
307
307
|
internal static readonly List<AnalyticsAdapter> listAdapters = new List<AnalyticsAdapter>();
|
|
308
308
|
internal static readonly AnalyticsData localData = new AnalyticsData().LoadJsonFromFile().BuildCache();
|
|
309
|
-
internal static
|
|
309
|
+
internal static SessionStat sessionStat;
|
|
310
310
|
internal static readonly GDKPool<StringBuilder> sbPool = new GDKPool<StringBuilder>(3);
|
|
311
311
|
internal static readonly GDKPool<EventParams> epPool = new GDKPool<EventParams>(1);
|
|
312
312
|
|
|
@@ -354,6 +354,10 @@ namespace Amanotes.Core
|
|
|
354
354
|
|
|
355
355
|
internal static void InitModule()
|
|
356
356
|
{
|
|
357
|
+
if (Config.analytics.enableEventStat)
|
|
358
|
+
{
|
|
359
|
+
sessionStat = new SessionStat();
|
|
360
|
+
}
|
|
357
361
|
listAdapters.Clear();
|
|
358
362
|
listAdapters.AddRange(Adapter2.GetAllAdapter<AnalyticsAdapter>());
|
|
359
363
|
}
|
package/Runtime/AmaGDK.Mono.cs
CHANGED
package/Runtime/AmaGDK.cs
CHANGED
|
@@ -17,7 +17,7 @@ namespace Amanotes.Core
|
|
|
17
17
|
{
|
|
18
18
|
public partial class AmaGDK : MonoBehaviour
|
|
19
19
|
{
|
|
20
|
-
public const string VERSION = "0.2.
|
|
20
|
+
public const string VERSION = "0.2.67";
|
|
21
21
|
|
|
22
22
|
internal static Status _status = Status.None;
|
|
23
23
|
private static ConfigAsset _config = null;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
using System.Collections.Generic;
|
|
2
|
+
using static Amanotes.Core.Internal.Logging;
|
|
3
|
+
using UnityEngine;
|
|
4
|
+
namespace Amanotes.Core
|
|
5
|
+
{
|
|
6
|
+
public partial class AmaFPS
|
|
7
|
+
{
|
|
8
|
+
internal class Utils
|
|
9
|
+
{
|
|
10
|
+
internal static void InjectUserData(Dictionary<string, object> dictionary, Dictionary<string, object> userData)
|
|
11
|
+
{
|
|
12
|
+
if (userData == null || userData.Count == 0) return;
|
|
13
|
+
|
|
14
|
+
foreach (var kvp in userData)
|
|
15
|
+
{
|
|
16
|
+
if (dictionary.ContainsKey(kvp.Key))
|
|
17
|
+
{
|
|
18
|
+
Log("Duplicated key: " + kvp.Key);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
dictionary.Add(kvp.Key, kvp.Value);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
internal static void InjectDeviceInfo(Dictionary<string, object> dictionary)
|
|
26
|
+
{
|
|
27
|
+
var resolution = Screen.currentResolution;
|
|
28
|
+
dictionary.Add("system_memory_size", SystemInfo.systemMemorySize);
|
|
29
|
+
dictionary.Add("graphics_memory_size", SystemInfo.graphicsMemorySize);
|
|
30
|
+
dictionary.Add("graphics_device_type", SystemInfo.graphicsDeviceType);
|
|
31
|
+
dictionary.Add("graphics_shader_level", SystemInfo.graphicsShaderLevel);
|
|
32
|
+
dictionary.Add("processor_count", SystemInfo.processorCount);
|
|
33
|
+
dictionary.Add("processor_frequency", SystemInfo.processorFrequency);
|
|
34
|
+
dictionary.Add("screen_width", resolution.width);
|
|
35
|
+
dictionary.Add("screen_height", resolution.height);
|
|
36
|
+
dictionary.Add("screen_refresh_rate", resolution.refreshRate);
|
|
37
|
+
}
|
|
38
|
+
internal static void InjectDeviceStatus(Dictionary<string, object> dictionary)
|
|
39
|
+
{
|
|
40
|
+
dictionary.Add("battery_level", Mathf.RoundToInt(SystemInfo.batteryLevel * 100));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
using Amanotes.Core.Internal;
|
|
4
|
+
using static Amanotes.Core.Internal.Logging;
|
|
5
|
+
using UnityEngine;
|
|
6
|
+
using UnityEngine.Events;
|
|
7
|
+
|
|
8
|
+
namespace Amanotes.Core.Internal
|
|
9
|
+
{
|
|
10
|
+
public partial class ConfigAsset
|
|
11
|
+
{
|
|
12
|
+
[SerializeField]
|
|
13
|
+
public FPSConfig fpsConfig;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
namespace Amanotes.Core
|
|
18
|
+
{
|
|
19
|
+
public partial class AmaGDK
|
|
20
|
+
{
|
|
21
|
+
public static readonly AmaFPS FPS = new AmaFPS();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
[Serializable]
|
|
26
|
+
public class FPSConfig
|
|
27
|
+
{
|
|
28
|
+
[Tooltip("Enable this to always record FPS for the app session. This allows developers to get the current FPS and current average FPS at any time.")]
|
|
29
|
+
public bool enableRealtimeFPS = false;
|
|
30
|
+
|
|
31
|
+
[Tooltip("The number of frames over which FPS is calculated. The FPS will be averaged after every 'frameGroup' frames.")]
|
|
32
|
+
[Range(1, 60)]
|
|
33
|
+
public int frameGroup = 20;
|
|
34
|
+
|
|
35
|
+
[Header("Lagging Tolerance")]
|
|
36
|
+
[Tooltip("The size of the sliding window used to calculate the Simple Moving Average (SMA) of the FPS.")]
|
|
37
|
+
[Range(10, 100)]
|
|
38
|
+
public int windowSize = 15;
|
|
39
|
+
|
|
40
|
+
[Tooltip("The allowed deviation from the SMA to detect an FPS drop.")]
|
|
41
|
+
[Range(0f, 0.3f)]
|
|
42
|
+
public float deviation = 0.1f;
|
|
43
|
+
|
|
44
|
+
[Tooltip("Number of frames to skip after OnApplicationFocus to avoid false alarms.")]
|
|
45
|
+
[Range(0, 60)]
|
|
46
|
+
public int skipFrameFocus = 10;
|
|
47
|
+
|
|
48
|
+
[Header("Callback")]
|
|
49
|
+
[Tooltip("This callback is invoked when an FPS drop is detected.")]
|
|
50
|
+
public UnityEvent onFrameDrop = new UnityEvent();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public partial class AmaFPS // public API
|
|
54
|
+
{
|
|
55
|
+
|
|
56
|
+
public float CurrentFPS => _realtime?.lastFPS ?? 0;
|
|
57
|
+
public float AverageFPS => _realtime?.avgFPS ?? 0;
|
|
58
|
+
public float MinFPS => _realtime?.minFPS ?? 0;
|
|
59
|
+
public float MaxFPS => _realtime?.maxFPS ?? 0;
|
|
60
|
+
public Action<string, Dictionary<string, object>> LogEventHook;
|
|
61
|
+
|
|
62
|
+
private bool _isAppReady = false;
|
|
63
|
+
private int _playCount = 0;
|
|
64
|
+
|
|
65
|
+
public void TrackLaunchTime()
|
|
66
|
+
{
|
|
67
|
+
if (_isAppReady)
|
|
68
|
+
{
|
|
69
|
+
Log("TrackAppReady() being called multiple time!");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
_isAppReady = true;
|
|
73
|
+
|
|
74
|
+
var dict = new Dictionary<string, object>()
|
|
75
|
+
{
|
|
76
|
+
{"loading_time", Time.realtimeSinceStartup},
|
|
77
|
+
{"session", AmaGDK.User.Session},
|
|
78
|
+
{"device_name", SystemInfo.deviceModel},
|
|
79
|
+
{"device_os_version", SystemInfo.operatingSystem},
|
|
80
|
+
|
|
81
|
+
// Update
|
|
82
|
+
{"game_time", Time.time},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
Utils.InjectDeviceInfo(dict);
|
|
86
|
+
Utils.InjectDeviceStatus(dict);
|
|
87
|
+
|
|
88
|
+
LogEvent("pm_launch_time", dict);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
public void StartRecordFPS(string context, Dictionary<string, object> userData = null)
|
|
92
|
+
{
|
|
93
|
+
_recorder ??= new FrameRecorder(_config ?? AmaGDK.Config.fpsConfig);
|
|
94
|
+
if (_recorder.isRecording)
|
|
95
|
+
{
|
|
96
|
+
LogWarning("Not yet support - Start multiple recorders at the same time! Please stop the previous session first!");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
_recorder.Start(context, userData);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public Dictionary<string, object> StopRecordFPS(bool logEvent = true)
|
|
104
|
+
{
|
|
105
|
+
if (_recorder == null || !_recorder.isRecording)
|
|
106
|
+
{
|
|
107
|
+
LogWarning("No tracking session found!");
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
var data = _recorder.Stop();
|
|
112
|
+
if (logEvent) LogEvent("fps_measurement", data);
|
|
113
|
+
return data;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private void LogEvent(string eventName, Dictionary<string, object> parameters)
|
|
117
|
+
{
|
|
118
|
+
if (LogEventHook == null)
|
|
119
|
+
{
|
|
120
|
+
AmaGDK.Analytics.LogEvent(eventName, parameters);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
LogEventHook(eventName, parameters);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public partial class AmaFPS: IOnApplicationFocus, IOnApplicationPause, IOnFrameUpdate // Internal logic
|
|
128
|
+
{
|
|
129
|
+
private FrameRecorder _recorder;
|
|
130
|
+
private FrameRecorder _realtime;
|
|
131
|
+
private int _skipFrames = 0;
|
|
132
|
+
private FPSConfig _config;
|
|
133
|
+
|
|
134
|
+
internal AmaFPS()
|
|
135
|
+
{
|
|
136
|
+
AmaGDK.SetCallback_OnGDKInit(OnGDKInit);
|
|
137
|
+
AmaGDK.unityCallbacks.AddObserver(this);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
internal AmaFPS(FPSConfig config)
|
|
141
|
+
{
|
|
142
|
+
// Constructor for unit test
|
|
143
|
+
_config = config;
|
|
144
|
+
_realtime = new FrameRecorder(config);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private void OnGDKInit()
|
|
148
|
+
{
|
|
149
|
+
_config = AmaGDK.Config.fpsConfig;
|
|
150
|
+
_realtime ??= new FrameRecorder(_config);
|
|
151
|
+
|
|
152
|
+
_realtime.bands.onFPSDrop -= OnFrameDrop;
|
|
153
|
+
_realtime.bands.onFPSDrop += OnFrameDrop;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private void OnFrameDrop()
|
|
157
|
+
{
|
|
158
|
+
if (_config.onFrameDrop.GetPersistentEventCount() > 0)
|
|
159
|
+
{
|
|
160
|
+
_config.onFrameDrop.Invoke();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
public void OnFrameUpdate()
|
|
165
|
+
{
|
|
166
|
+
UpdateFrame(Time.unscaledDeltaTime);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
internal void UpdateFrame(float unscaledDeltaTime)
|
|
170
|
+
{
|
|
171
|
+
if (unscaledDeltaTime <= 1 / 1200f) unscaledDeltaTime = 1 / 1200f; // prevent the case when unscaledDeltaTime == 0
|
|
172
|
+
|
|
173
|
+
if (_skipFrames > 0)
|
|
174
|
+
{
|
|
175
|
+
_skipFrames--;
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (_realtime != null && _config.enableRealtimeFPS) _realtime.UpdateFrame(unscaledDeltaTime);
|
|
180
|
+
|
|
181
|
+
if (_recorder != null && _recorder.isRecording)
|
|
182
|
+
{
|
|
183
|
+
_recorder.UpdateFrame(unscaledDeltaTime);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
public void OnApplicationFocus(bool hasFocus)
|
|
188
|
+
{
|
|
189
|
+
_skipFrames = _config?.skipFrameFocus ?? 0;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
public void OnApplicationPause(bool pauseStatus)
|
|
193
|
+
{
|
|
194
|
+
_skipFrames = _config?.skipFrameFocus ?? 0;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
using UnityEngine;
|
|
4
|
+
using UnityEngine.Profiling;
|
|
5
|
+
using UnityEngine.UI;
|
|
6
|
+
using static Amanotes.Core.Internal.Logging;
|
|
7
|
+
|
|
8
|
+
namespace Amanotes.Core
|
|
9
|
+
{
|
|
10
|
+
|
|
11
|
+
[RequireComponent(typeof(RectTransform))]
|
|
12
|
+
[RequireComponent(typeof(CanvasRenderer))]
|
|
13
|
+
public class AmaFPSVisualizer : Graphic
|
|
14
|
+
{
|
|
15
|
+
[Serializable]
|
|
16
|
+
private class FlashOnDrop
|
|
17
|
+
{
|
|
18
|
+
public bool enabled = true;
|
|
19
|
+
public Color flashColor = new Color(1, 0, 0, 0.5f);
|
|
20
|
+
[Range(1f, 10f)] public float flashingSpeed = 10f;
|
|
21
|
+
|
|
22
|
+
public Image flashImage;
|
|
23
|
+
private float _alpha = 0;
|
|
24
|
+
|
|
25
|
+
public void OnFrameDrop()
|
|
26
|
+
{
|
|
27
|
+
_alpha = flashColor.a;
|
|
28
|
+
flashImage.color = flashColor;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public void Update()
|
|
32
|
+
{
|
|
33
|
+
float dAlpha = flashingSpeed * Time.deltaTime;
|
|
34
|
+
_alpha = Mathf.Max(0, _alpha - dAlpha);
|
|
35
|
+
|
|
36
|
+
Color c = flashColor;
|
|
37
|
+
c.a = _alpha;
|
|
38
|
+
flashImage.color = c;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
[Serializable]
|
|
43
|
+
private class FPSTextUpdate
|
|
44
|
+
{
|
|
45
|
+
private static readonly Dictionary<int, string> intMap = new Dictionary<int, string>();
|
|
46
|
+
private static string GetString(float value)
|
|
47
|
+
{
|
|
48
|
+
Profiler.BeginSample("FPSTextUpdate.GetString");
|
|
49
|
+
int intValue = Mathf.RoundToInt(value * 10);
|
|
50
|
+
if (intMap.TryGetValue(intValue, out string result))
|
|
51
|
+
{
|
|
52
|
+
Profiler.EndSample();
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
result = $"{(intValue/10f):F1}";
|
|
57
|
+
intMap.Add(intValue, result);
|
|
58
|
+
Profiler.EndSample();
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public bool enabled = true;
|
|
63
|
+
public GameObject parent = null;
|
|
64
|
+
|
|
65
|
+
public Text currentFPS;
|
|
66
|
+
private int frame;
|
|
67
|
+
|
|
68
|
+
public void Update(float fps)
|
|
69
|
+
{
|
|
70
|
+
if (frame-- > 0) return;
|
|
71
|
+
currentFPS.text = GetString(fps);
|
|
72
|
+
frame = 60;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
struct FPSVertex
|
|
77
|
+
{
|
|
78
|
+
public float fps;
|
|
79
|
+
public int colorIndex;
|
|
80
|
+
public Vector3 position;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
[Serializable]
|
|
84
|
+
private class FPSGraph : FrameRecorder
|
|
85
|
+
{
|
|
86
|
+
const int RESOLUTION = 64;
|
|
87
|
+
public bool enabled = true;
|
|
88
|
+
public Graphic graphic = null;
|
|
89
|
+
|
|
90
|
+
public int minFPS = 15;
|
|
91
|
+
public int maxFPS = 60;
|
|
92
|
+
public int goodFPS = 55;
|
|
93
|
+
public int acceptableFPS = 45;
|
|
94
|
+
public int badFPS = 30;
|
|
95
|
+
|
|
96
|
+
public Color[] colors = new Color[]{
|
|
97
|
+
Color.red, // min -> bad
|
|
98
|
+
new Color(1f, 0.65f, 0f), // bad -> ok
|
|
99
|
+
Color.yellow, // ok --> good
|
|
100
|
+
Color.green, // very good
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
private readonly float[] fpsHistory = new float[RESOLUTION];
|
|
104
|
+
private int vCount = 0;
|
|
105
|
+
|
|
106
|
+
public void Clear()
|
|
107
|
+
{
|
|
108
|
+
vCount = 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
protected override void UpdateGroupFrame(){
|
|
112
|
+
var hIndex = vCount % RESOLUTION;
|
|
113
|
+
fpsHistory[hIndex] = lastFPS;
|
|
114
|
+
vCount++;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public void PopulateMesh(VertexHelper vh)
|
|
118
|
+
{
|
|
119
|
+
var rect = ((RectTransform)graphic.transform).rect;
|
|
120
|
+
var barW = rect.width / (RESOLUTION-1);
|
|
121
|
+
var barH = rect.height;
|
|
122
|
+
|
|
123
|
+
var barX = vCount < RESOLUTION ? (RESOLUTION - vCount) * barW : 0;
|
|
124
|
+
var stIndex = vCount <= RESOLUTION ? 0 : (vCount % RESOLUTION);
|
|
125
|
+
var nVertex = vCount <= RESOLUTION ? vCount : RESOLUTION;
|
|
126
|
+
|
|
127
|
+
for (var i = 0; i < nVertex; i++)
|
|
128
|
+
{
|
|
129
|
+
var idx = (i + stIndex) % RESOLUTION;
|
|
130
|
+
var fps = fpsHistory[idx];
|
|
131
|
+
var h = fps <= minFPS ? 0 :
|
|
132
|
+
fps >= maxFPS ? 1 : (fps - minFPS) / (maxFPS - minFPS);
|
|
133
|
+
|
|
134
|
+
var v0 = new Vector3(barX, 0, 0);
|
|
135
|
+
var v1 = new Vector3(barX, h * barH, 0);
|
|
136
|
+
var c1 = colors[
|
|
137
|
+
fps <= badFPS ? 0 :
|
|
138
|
+
fps <= acceptableFPS ? 1 :
|
|
139
|
+
fps <= goodFPS ? 2 : 3
|
|
140
|
+
];
|
|
141
|
+
var c0 = c1;
|
|
142
|
+
c0.a = 0;
|
|
143
|
+
|
|
144
|
+
// Add 2 vertices
|
|
145
|
+
vh.AddVert(v0, c0, Vector2.zero);
|
|
146
|
+
vh.AddVert(v1, c1, Vector2.zero);
|
|
147
|
+
|
|
148
|
+
if (i > 0)
|
|
149
|
+
{
|
|
150
|
+
var vIndex = vh.currentVertCount - 2;
|
|
151
|
+
vh.AddTriangle(vIndex - 2, vIndex - 1, vIndex + 1);
|
|
152
|
+
vh.AddTriangle(vIndex - 2, vIndex + 1, vIndex);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
barX += barW;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
AddHorzLine(vh, rect.width, barH * ((goodFPS-minFPS) / (float)(maxFPS-minFPS)), colors[2], 2f);
|
|
159
|
+
AddHorzLine(vh, rect.width, barH * ((acceptableFPS-minFPS) / (float)(maxFPS-minFPS)), colors[1], 2f);
|
|
160
|
+
AddHorzLine(vh, rect.width, barH * ((badFPS-minFPS) / (float)(maxFPS-minFPS)), colors[0], 2f);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
void AddHorzLine(VertexHelper vh, float w, float h, Color color, float thickness = 1f)
|
|
164
|
+
{
|
|
165
|
+
var v0 = new Vector3(0, h, 0);
|
|
166
|
+
var v1 = new Vector3(w, h, 0);
|
|
167
|
+
var v2 = new Vector3(w, h+thickness, 0);
|
|
168
|
+
var v3 = new Vector3(0, h+thickness, 0);
|
|
169
|
+
|
|
170
|
+
var vIndex = vh.currentVertCount;
|
|
171
|
+
|
|
172
|
+
vh.AddVert(v0, color, new Vector2(0, 0));
|
|
173
|
+
vh.AddVert(v1, color, new Vector2(0, 0));
|
|
174
|
+
vh.AddVert(v2, color, new Vector2(0, 0));
|
|
175
|
+
vh.AddVert(v3, color, new Vector2(0, 0));
|
|
176
|
+
|
|
177
|
+
vh.AddTriangle(vIndex, vIndex+1, vIndex+2);
|
|
178
|
+
vh.AddTriangle(vIndex+2, vIndex+3, vIndex);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
public FPSGraph(FPSConfig config) : base(config)
|
|
182
|
+
{
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
[SerializeField] private FlashOnDrop flashOnDropFrame = new FlashOnDrop();
|
|
187
|
+
[SerializeField] private FPSTextUpdate textUpdate = new FPSTextUpdate();
|
|
188
|
+
|
|
189
|
+
private FPSGraph graph;
|
|
190
|
+
|
|
191
|
+
protected override void Awake()
|
|
192
|
+
{
|
|
193
|
+
flashOnDropFrame.flashImage.enabled = flashOnDropFrame.enabled;
|
|
194
|
+
textUpdate.parent.SetActive(textUpdate.enabled);
|
|
195
|
+
|
|
196
|
+
graph = new FPSGraph(AmaGDK.Config.fpsConfig)
|
|
197
|
+
{
|
|
198
|
+
graphic = GetComponent<Graphic>()
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
Application.targetFrameRate = Screen.currentResolution.refreshRate;
|
|
202
|
+
Log($"Target FrameRate: {Application.targetFrameRate}");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
protected override void Start()
|
|
206
|
+
{
|
|
207
|
+
var rect = Screen.safeArea;
|
|
208
|
+
var trans = transform as RectTransform;
|
|
209
|
+
var transRect = trans.rect;
|
|
210
|
+
trans.anchoredPosition = new Vector2(rect.x / rect.width * transRect.width, -rect.y / rect.height * transRect.height);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
public void OnFrameDrop()
|
|
214
|
+
{
|
|
215
|
+
flashOnDropFrame.OnFrameDrop();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private void Update()
|
|
219
|
+
{
|
|
220
|
+
if (!Application.isPlaying) return;
|
|
221
|
+
Profiler.BeginSample("AmaFPSVisualiser.Update()");
|
|
222
|
+
{
|
|
223
|
+
if (flashOnDropFrame.enabled) flashOnDropFrame.Update();
|
|
224
|
+
if (textUpdate.enabled) textUpdate.Update(1f/Time.unscaledDeltaTime);
|
|
225
|
+
if (graph != null && graph.enabled)
|
|
226
|
+
{
|
|
227
|
+
graph.UpdateFrame(Time.unscaledDeltaTime);
|
|
228
|
+
Profiler.BeginSample("SetVerticesDirty()");
|
|
229
|
+
{
|
|
230
|
+
SetVerticesDirty();
|
|
231
|
+
}
|
|
232
|
+
Profiler.EndSample();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
Profiler.EndSample();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
protected override void OnPopulateMesh(VertexHelper vh)
|
|
239
|
+
{
|
|
240
|
+
vh.Clear();
|
|
241
|
+
|
|
242
|
+
if (graph == null || !graph.enabled) return;
|
|
243
|
+
graph.PopulateMesh(vh);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|