Sin descripción
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SamsungGameSDKAdaptivePerformanceSubsystem.cs 53KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420
  1. #if UNITY_ANDROID
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Threading;
  6. using static System.Threading.Thread;
  7. using UnityEngine.Scripting;
  8. using UnityEngine.AdaptivePerformance.Provider;
  9. using UnityEngine.SubsystemsImplementation;
  10. [assembly: AlwaysLinkAssembly]
  11. namespace UnityEngine.AdaptivePerformance.Samsung.Android
  12. {
  13. internal static class GameSDKLog
  14. {
  15. static SamsungAndroidProviderSettings settings = SamsungAndroidProviderSettings.GetSettings();
  16. [Conditional("DEVELOPMENT_BUILD")]
  17. public static void Debug(string format, params object[] args)
  18. {
  19. if (settings != null && settings.samsungProviderLogging)
  20. UnityEngine.Debug.Log(System.String.Format("[Samsung GameSDK] " + format, args));
  21. }
  22. }
  23. internal class AsyncUpdater : IDisposable
  24. {
  25. private Thread m_Thread;
  26. private bool m_Disposed = false;
  27. private bool m_Quit = false;
  28. private List<Action> m_UpdateAction = new List<Action>();
  29. private int[] m_UpdateRequests = null;
  30. private bool[] m_RequestComplete = null;
  31. private int m_UpdateRequestReadIndex = 0;
  32. private int m_UpdateRequestWriteIndex = 0;
  33. private object m_Mutex = new object();
  34. private Semaphore m_Semaphore = null;
  35. public int Register(Action action)
  36. {
  37. if (m_Thread.IsAlive)
  38. return -1;
  39. int handle = m_UpdateAction.Count;
  40. m_UpdateAction.Add(action);
  41. return handle;
  42. }
  43. public void Start()
  44. {
  45. int maxRequests = m_UpdateAction.Count;
  46. if (maxRequests <= 0)
  47. return;
  48. m_Semaphore = new Semaphore(0, maxRequests);
  49. m_UpdateRequests = new int[maxRequests];
  50. m_RequestComplete = new bool[maxRequests];
  51. m_Thread.Start();
  52. }
  53. public bool RequestUpdate(int handle)
  54. {
  55. lock (m_Mutex)
  56. {
  57. int newWriteIndex = (m_UpdateRequestWriteIndex + 1) % m_UpdateRequests.Length;
  58. if (newWriteIndex == m_UpdateRequestReadIndex)
  59. {
  60. return false;
  61. }
  62. m_UpdateRequests[m_UpdateRequestWriteIndex] = handle;
  63. m_RequestComplete[handle] = false;
  64. m_UpdateRequestWriteIndex = newWriteIndex;
  65. }
  66. m_Semaphore.Release();
  67. return true;
  68. }
  69. public bool IsRequestComplete(int handle)
  70. {
  71. lock (m_Mutex)
  72. {
  73. return m_RequestComplete[handle];
  74. }
  75. }
  76. public AsyncUpdater()
  77. {
  78. m_Thread = new Thread(new ThreadStart(ThreadProc));
  79. m_Thread.Name = "SamsungGameSDK";
  80. }
  81. private void ThreadProc()
  82. {
  83. AndroidJNI.AttachCurrentThread();
  84. while (true)
  85. {
  86. try
  87. {
  88. m_Semaphore.WaitOne();
  89. }
  90. catch (Exception)
  91. {
  92. break;
  93. }
  94. int handle = -1;
  95. lock (m_Mutex)
  96. {
  97. if (m_Quit)
  98. break;
  99. if (m_UpdateRequestReadIndex != m_UpdateRequestWriteIndex)
  100. {
  101. handle = m_UpdateRequests[m_UpdateRequestReadIndex];
  102. m_UpdateRequestReadIndex = (m_UpdateRequestReadIndex + 1) % m_UpdateRequests.Length;
  103. }
  104. }
  105. if (handle >= 0)
  106. {
  107. try
  108. {
  109. m_UpdateAction[handle].Invoke();
  110. }
  111. catch (Exception)
  112. {
  113. }
  114. lock (m_Mutex)
  115. {
  116. m_RequestComplete[handle] = true;
  117. }
  118. }
  119. }
  120. AndroidJNI.DetachCurrentThread();
  121. }
  122. private void Dispose(bool disposing)
  123. {
  124. if (m_Disposed)
  125. return;
  126. if (disposing)
  127. {
  128. if (m_Thread.IsAlive)
  129. {
  130. lock (m_Mutex)
  131. {
  132. m_Quit = true;
  133. }
  134. m_Semaphore.Release();
  135. m_Thread.Join();
  136. }
  137. }
  138. m_Disposed = true;
  139. }
  140. public void Dispose()
  141. {
  142. Dispose(true);
  143. GC.SuppressFinalize(this);
  144. }
  145. }
  146. internal class AsyncValue<T>
  147. {
  148. private AsyncUpdater updater = null;
  149. private int updateHandle = -1;
  150. private bool pendingUpdate = false;
  151. private Func<T> updateFunc = null;
  152. private T newValue;
  153. private float updateTimeDeltaSeconds;
  154. private float updateTimestamp;
  155. public AsyncValue(AsyncUpdater updater, T value, float updateTimeDeltaSeconds, Func<T> updateFunc)
  156. {
  157. this.updater = updater;
  158. this.updateTimeDeltaSeconds = updateTimeDeltaSeconds;
  159. this.updateFunc = updateFunc;
  160. this.value = value;
  161. this.updateHandle = updater.Register(() => newValue = updateFunc());
  162. }
  163. public bool Update(float timestamp)
  164. {
  165. bool changed = false;
  166. if (pendingUpdate && updater.IsRequestComplete(updateHandle))
  167. {
  168. changed = !value.Equals(newValue);
  169. if (changed)
  170. changeTimestamp = timestamp;
  171. value = newValue;
  172. updateTimestamp = timestamp;
  173. pendingUpdate = false;
  174. }
  175. if (!pendingUpdate)
  176. {
  177. if (timestamp - updateTimestamp > updateTimeDeltaSeconds)
  178. {
  179. pendingUpdate = updater.RequestUpdate(updateHandle);
  180. }
  181. }
  182. return changed;
  183. }
  184. public void SyncUpdate(float timestamp)
  185. {
  186. var oldValue = value;
  187. updateTimestamp = timestamp;
  188. value = updateFunc();
  189. if (!value.Equals(oldValue))
  190. changeTimestamp = timestamp;
  191. }
  192. public T value { get; private set; }
  193. public float changeTimestamp { get; private set; }
  194. }
  195. [Preserve]
  196. public class SamsungGameSDKAdaptivePerformanceSubsystem : AdaptivePerformanceSubsystem
  197. {
  198. [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
  199. static AdaptivePerformanceSubsystemDescriptor RegisterDescriptor()
  200. {
  201. if (!SystemInfo.deviceModel.StartsWith("samsung", StringComparison.OrdinalIgnoreCase))
  202. {
  203. GameSDKLog.Debug($"The device {SystemInfo.deviceModel} is not a supported Samsung phone. This provider will not run. Aborting registering the Adaptive Performance provider descriptor.");
  204. return null;
  205. }
  206. if (!SamsungGameSDKAdaptivePerformanceSubsystemProvider.NativeApi.IsAvailable())
  207. {
  208. GameSDKLog.Debug($"The native API for this provider is not available. Aborting registering the Adaptive Performance provider descriptor.");
  209. return null;
  210. }
  211. var registeredDesc = AdaptivePerformanceSubsystemDescriptor.RegisterDescriptor(new AdaptivePerformanceSubsystemDescriptor.Cinfo
  212. {
  213. id = "SamsungGameSDK",
  214. providerType = typeof(SamsungGameSDKAdaptivePerformanceSubsystem.SamsungGameSDKAdaptivePerformanceSubsystemProvider),
  215. subsystemTypeOverride = typeof(SamsungGameSDKAdaptivePerformanceSubsystem)
  216. });
  217. return registeredDesc;
  218. }
  219. public class SamsungGameSDKAdaptivePerformanceSubsystemProvider : APProvider, IApplicationLifecycle, IDevicePerformanceLevelControl
  220. {
  221. private NativeApi m_Api = null;
  222. private AsyncUpdater m_AsyncUpdater;
  223. private PerformanceDataRecord m_Data = new PerformanceDataRecord();
  224. private object m_DataLock = new object();
  225. private AsyncValue<double> m_SkinTemp = null;
  226. private AsyncValue<double> m_GPUTime = null;
  227. private Version m_Version = null;
  228. private float m_MinTempLevel = 0.0f;
  229. private float m_MaxTempLevel = 10.0f;
  230. bool m_PerformanceLevelControlSystemChange = false;
  231. bool m_AllowPerformanceLevelControlChanges = true;
  232. private AutoVariableRefreshRate m_AutoVariableRefreshRate;
  233. public override IApplicationLifecycle ApplicationLifecycle { get { return this; } }
  234. public override IDevicePerformanceLevelControl PerformanceLevelControl { get { return this; } }
  235. public int MaxCpuPerformanceLevel { get; set; }
  236. public int MaxGpuPerformanceLevel { get; set; }
  237. static SamsungAndroidProviderSettings settings = SamsungAndroidProviderSettings.GetSettings();
  238. public SamsungGameSDKAdaptivePerformanceSubsystemProvider()
  239. {
  240. MaxCpuPerformanceLevel = 3;
  241. MaxGpuPerformanceLevel = 3;
  242. m_Api = new NativeApi(OnPerformanceWarning, OnPerformanceLevelTimeout, () => (VariableRefreshRate.Instance as VRRManager)?.OnRefreshRateChanged(), OnCpuPerformanceBoostModeTimeout, OnGpuPerformanceBoostModeTimeout);
  243. m_AsyncUpdater = new AsyncUpdater();
  244. m_SkinTemp = new AsyncValue<double>(m_AsyncUpdater, -1.0, 2.7f, () => GetHighPrecisionSkinTempLevel());
  245. m_GPUTime = new AsyncValue<double>(m_AsyncUpdater, -1.0, 0.0f, () => m_Api.GetGpuFrameTime());
  246. Capabilities = Feature.CpuPerformanceLevel | Feature.GpuPerformanceLevel | Feature.PerformanceLevelControl | Feature.TemperatureLevel | Feature.WarningLevel | Feature.GpuFrameTime;
  247. m_AsyncUpdater.Start();
  248. }
  249. private void OnPerformanceWarning(WarningLevel warningLevel)
  250. {
  251. lock (m_DataLock)
  252. {
  253. m_Data.ChangeFlags |= Feature.WarningLevel;
  254. m_Data.ChangeFlags |= Feature.PerformanceLevelControl;
  255. m_Data.WarningLevel = warningLevel;
  256. }
  257. }
  258. private void OnPerformanceLevelTimeout()
  259. {
  260. lock (m_DataLock)
  261. {
  262. m_Data.ChangeFlags |= Feature.CpuPerformanceLevel;
  263. m_Data.ChangeFlags |= Feature.GpuPerformanceLevel;
  264. m_Data.CpuPerformanceLevel = Constants.UnknownPerformanceLevel;
  265. m_Data.GpuPerformanceLevel = Constants.UnknownPerformanceLevel;
  266. }
  267. }
  268. private void OnCpuPerformanceBoostModeTimeout()
  269. {
  270. lock (m_DataLock)
  271. {
  272. m_Data.ChangeFlags |= Feature.CpuPerformanceBoost;
  273. m_Data.CpuPerformanceBoost = false;
  274. }
  275. }
  276. private void OnGpuPerformanceBoostModeTimeout()
  277. {
  278. lock (m_DataLock)
  279. {
  280. m_Data.ChangeFlags |= Feature.GpuPerformanceBoost;
  281. m_Data.GpuPerformanceBoost = false;
  282. }
  283. }
  284. private float GetHighPrecisionSkinTempLevel()
  285. {
  286. return (float)m_Api.GetHighPrecisionSkinTempLevel();
  287. }
  288. private int GetClusterInfo()
  289. {
  290. return m_Api.GetClusterInfo();
  291. }
  292. private void ImmediateUpdateTemperature()
  293. {
  294. var timestamp = Time.time;
  295. m_SkinTemp.SyncUpdate(timestamp);
  296. lock (m_DataLock)
  297. {
  298. m_Data.ChangeFlags |= Feature.TemperatureLevel;
  299. m_Data.TemperatureLevel = GetTemperatureLevel();
  300. }
  301. }
  302. private static bool TryParseVersion(string versionString, out Version version)
  303. {
  304. try
  305. {
  306. version = new Version(versionString);
  307. }
  308. catch (Exception)
  309. {
  310. version = null;
  311. return false;
  312. }
  313. return true;
  314. }
  315. protected override bool TryInitialize()
  316. {
  317. if (Initialized)
  318. {
  319. return true;
  320. }
  321. if (!base.TryInitialize())
  322. {
  323. return false;
  324. }
  325. if (!m_Api.Initialize())
  326. {
  327. return false;
  328. }
  329. if (TryParseVersion(m_Api.GetVersion(), out m_Version))
  330. {
  331. if (m_Version >= new Version(3, 5))
  332. {
  333. Initialized = true;
  334. MaxCpuPerformanceLevel = m_Api.GetMaxCpuPerformanceLevel();
  335. MaxGpuPerformanceLevel = m_Api.GetMaxGpuPerformanceLevel();
  336. Capabilities |= Feature.CpuPerformanceBoost | Feature.GpuPerformanceBoost;
  337. }
  338. else if (m_Version >= new Version(3, 4))
  339. {
  340. Initialized = true;
  341. MaxCpuPerformanceLevel = m_Api.GetMaxCpuPerformanceLevel();
  342. MaxGpuPerformanceLevel = m_Api.GetMaxGpuPerformanceLevel();
  343. }
  344. else if (m_Version >= new Version(3, 2))
  345. {
  346. Initialized = true;
  347. MaxCpuPerformanceLevel = m_Api.GetMaxCpuPerformanceLevel();
  348. MaxGpuPerformanceLevel = m_Api.GetMaxGpuPerformanceLevel();
  349. }
  350. else
  351. {
  352. m_Api.Terminate();
  353. Initialized = false;
  354. }
  355. }
  356. if (MaxCpuPerformanceLevel == SamsungAndroidProviderConstants.k_InvalidOperation)
  357. {
  358. MaxCpuPerformanceLevel = Constants.UnknownPerformanceLevel;
  359. Capabilities &= ~Feature.CpuPerformanceLevel;
  360. m_AllowPerformanceLevelControlChanges = false;
  361. }
  362. if (MaxGpuPerformanceLevel == SamsungAndroidProviderConstants.k_InvalidOperation)
  363. {
  364. MaxGpuPerformanceLevel = Constants.UnknownPerformanceLevel;
  365. Capabilities &= ~Feature.GpuPerformanceLevel;
  366. m_AllowPerformanceLevelControlChanges = false;
  367. }
  368. m_Data.PerformanceLevelControlAvailable = m_AllowPerformanceLevelControlChanges;
  369. return Initialized;
  370. }
  371. public override void Start()
  372. {
  373. if (!Initialized)
  374. {
  375. return;
  376. }
  377. if (m_Running)
  378. {
  379. return;
  380. }
  381. ImmediateUpdateTemperature();
  382. Thread t = new Thread(CheckInitialTemperatureAndSendWarnings);
  383. t.Start();
  384. CheckAndInitializeVRR();
  385. m_Running = true;
  386. }
  387. void CheckAndInitializeVRR()
  388. {
  389. if (m_Api.IsVariableRefreshRateSupported())
  390. {
  391. if (VariableRefreshRate.Instance == null)
  392. {
  393. VariableRefreshRate.Instance = new VRRManager(m_Api);
  394. m_AutoVariableRefreshRate = new AutoVariableRefreshRate(VariableRefreshRate.Instance);
  395. }
  396. }
  397. else
  398. {
  399. VariableRefreshRate.Instance = null;
  400. m_AutoVariableRefreshRate = null;
  401. }
  402. }
  403. void CheckInitialTemperatureAndSendWarnings()
  404. {
  405. // If the device is already warm upon startup and past the throttling imminent warning level
  406. // the warning callback is not called as it's not available yet. We need to set it manually based on temperature as workaround.
  407. // On startup the temperature reading is always 0. After a couple of seconds a true value is returned. Therefore we wait for 2 seconds before we make the reading.
  408. Sleep(TimeSpan.FromSeconds(2));
  409. float currentTempLevel = GetHighPrecisionSkinTempLevel();
  410. if (m_Version >= new Version(3, 2))
  411. {
  412. if (currentTempLevel >= 7)
  413. OnPerformanceWarning(WarningLevel.Throttling);
  414. else if (currentTempLevel >= 5)
  415. OnPerformanceWarning(WarningLevel.ThrottlingImminent);
  416. }
  417. if (m_Version >= new Version(3, 5))
  418. {
  419. // Cluster info is not available in the same frame as game sdk init so we need to wait a bit.
  420. int clusterInfo = m_Api.GetClusterInfo();
  421. if (clusterInfo != -999)
  422. {
  423. var aClusterInfo = new ClusterInfo();
  424. aClusterInfo.BigCore = clusterInfo / 100;
  425. aClusterInfo.MediumCore = (clusterInfo % 100) / 10;
  426. aClusterInfo.LittleCore = (clusterInfo % 100) % 10;
  427. lock (m_DataLock)
  428. {
  429. m_Data.ClusterInfo = aClusterInfo;
  430. m_Data.ChangeFlags |= Feature.ClusterInfo;
  431. }
  432. Capabilities |= Feature.ClusterInfo;
  433. }
  434. }
  435. }
  436. public override void Stop()
  437. {
  438. m_Running = false;
  439. }
  440. public override void Destroy()
  441. {
  442. VariableRefreshRate.Instance = null;
  443. m_AutoVariableRefreshRate = null;
  444. if (m_Running)
  445. {
  446. Stop();
  447. }
  448. if (Initialized)
  449. {
  450. m_Api.Terminate();
  451. Initialized = false;
  452. }
  453. m_AsyncUpdater.Dispose();
  454. }
  455. public override string Stats => $"SkinTemp={m_SkinTemp?.value ?? -1} GPUTime={m_GPUTime?.value ?? -1}";
  456. public override PerformanceDataRecord Update()
  457. {
  458. // GameSDK API is very slow (~4ms per call), so update those numbers once per frame from another thread
  459. float timeSinceStartup = Time.time;
  460. m_GPUTime.Update(timeSinceStartup);
  461. bool tempChanged = m_SkinTemp.Update(timeSinceStartup);
  462. (VariableRefreshRate.Instance as VRRManager)?.Update();
  463. if ((VariableRefreshRate.Instance as VRRManager) != null && settings.automaticVRR)
  464. if (QualitySettings.vSyncCount == 0)
  465. m_AutoVariableRefreshRate.UpdateAutoVRR();
  466. if (m_PerformanceLevelControlSystemChange)
  467. {
  468. var temperatureLevel = (float)m_SkinTemp.value;
  469. if (temperatureLevel < 5)
  470. {
  471. lock (m_DataLock)
  472. {
  473. DisableSystemControl();
  474. }
  475. }
  476. }
  477. lock (m_DataLock)
  478. {
  479. if (tempChanged)
  480. {
  481. m_Data.ChangeFlags |= Feature.TemperatureLevel;
  482. m_Data.TemperatureLevel = GetTemperatureLevel();
  483. }
  484. m_Data.GpuFrameTime = LatestGpuFrameTime();
  485. m_Data.ChangeFlags |= Feature.GpuFrameTime;
  486. PerformanceDataRecord result = m_Data;
  487. m_Data.ChangeFlags = Feature.None;
  488. return result;
  489. }
  490. }
  491. public override Version Version
  492. {
  493. get
  494. {
  495. return m_Version;
  496. }
  497. }
  498. public override Feature Capabilities { get; set; }
  499. public override bool Initialized { get; set; }
  500. private static float NormalizeTemperatureLevel(float currentTempLevel, float minValue, float maxValue)
  501. {
  502. float tempLevel = -1.0f;
  503. if (currentTempLevel >= minValue && currentTempLevel <= maxValue)
  504. {
  505. tempLevel = currentTempLevel / maxValue;
  506. tempLevel = Math.Min(Math.Max(tempLevel, Constants.MinTemperatureLevel), maxValue);
  507. }
  508. return tempLevel;
  509. }
  510. private float NormalizeTemperatureLevel(float currentTempLevel)
  511. {
  512. return NormalizeTemperatureLevel(currentTempLevel, m_MinTempLevel, m_MaxTempLevel);
  513. }
  514. private float GetTemperatureLevel()
  515. {
  516. return NormalizeTemperatureLevel((float)m_SkinTemp.value);
  517. }
  518. private float LatestGpuFrameTime()
  519. {
  520. return (float)(m_GPUTime.value / 1000.0);
  521. }
  522. public bool SetPerformanceLevel(ref int cpuLevel, ref int gpuLevel)
  523. {
  524. if ((Capabilities & Feature.CpuPerformanceLevel) != Feature.CpuPerformanceLevel ||
  525. (Capabilities & Feature.GpuPerformanceLevel) != Feature.GpuPerformanceLevel)
  526. return false;
  527. if (cpuLevel < 0)
  528. cpuLevel = 0;
  529. else if (cpuLevel > MaxCpuPerformanceLevel)
  530. cpuLevel = MaxCpuPerformanceLevel;
  531. if (gpuLevel < 0)
  532. gpuLevel = 0;
  533. else if (gpuLevel > MaxGpuPerformanceLevel)
  534. gpuLevel = MaxGpuPerformanceLevel;
  535. if (m_Version == new Version(3, 2) && cpuLevel == 0)
  536. cpuLevel = 1;
  537. bool success = false;
  538. int result = m_Api.SetFreqLevels(cpuLevel, gpuLevel);
  539. success = result == 1;
  540. lock (m_DataLock)
  541. {
  542. var oldCpuLevel = m_Data.CpuPerformanceLevel;
  543. var oldGpuLevel = m_Data.GpuPerformanceLevel;
  544. m_Data.CpuPerformanceLevel = success ? cpuLevel : Constants.UnknownPerformanceLevel;
  545. m_Data.GpuPerformanceLevel = success ? gpuLevel : Constants.UnknownPerformanceLevel;
  546. if (success)
  547. {
  548. if (m_Data.CpuPerformanceLevel != oldCpuLevel)
  549. m_Data.ChangeFlags |= Feature.CpuPerformanceLevel;
  550. if (m_Data.GpuPerformanceLevel != oldGpuLevel)
  551. m_Data.ChangeFlags |= Feature.GpuPerformanceLevel;
  552. }
  553. if (result > 1)
  554. {
  555. if (result == 2)
  556. {
  557. GameSDKLog.Debug($"Thermal Mitigation Logic is working and CPU({cpuLevel})/GPU({gpuLevel}) level change request was not approved.");
  558. }
  559. else if (result == 3)
  560. {
  561. GameSDKLog.Debug($"CPU or GPU Boost mode is active and CPU({cpuLevel})/GPU({gpuLevel}) level change request was not approved.");
  562. }
  563. EnableSystemControl();
  564. }
  565. }
  566. return success;
  567. }
  568. public bool EnableCpuBoost()
  569. {
  570. var result = m_Api.EnableCpuBoost();
  571. lock (m_DataLock)
  572. {
  573. var oldPerformanceBoost = m_Data.CpuPerformanceBoost;
  574. m_Data.CpuPerformanceBoost = result;
  575. if (m_Data.CpuPerformanceBoost != oldPerformanceBoost)
  576. m_Data.ChangeFlags |= Feature.CpuPerformanceBoost;
  577. if (result)
  578. {
  579. EnableSystemControl();
  580. }
  581. }
  582. return result;
  583. }
  584. public bool EnableGpuBoost()
  585. {
  586. var result = m_Api.EnableGpuBoost();
  587. lock (m_DataLock)
  588. {
  589. var oldPerformanceBoost = m_Data.GpuPerformanceBoost;
  590. m_Data.GpuPerformanceBoost = result;
  591. if (m_Data.GpuPerformanceBoost != oldPerformanceBoost)
  592. m_Data.ChangeFlags |= Feature.GpuPerformanceBoost;
  593. if (result)
  594. {
  595. EnableSystemControl();
  596. }
  597. }
  598. return result;
  599. }
  600. public void ApplicationPause() { }
  601. public void ApplicationResume()
  602. {
  603. //We need to re-initialize because some Android onForegroundchange() APIs do not detect the change (e.g. bixby)
  604. if (!m_Api.Initialize())
  605. GameSDKLog.Debug("Resume: reinitialization failed!");
  606. if ((Capabilities & Feature.CpuPerformanceLevel) == Feature.CpuPerformanceLevel)
  607. {
  608. lock (m_DataLock)
  609. {
  610. m_Data.CpuPerformanceLevel = Constants.UnknownPerformanceLevel;
  611. m_Data.ChangeFlags |= Feature.CpuPerformanceLevel;
  612. }
  613. }
  614. if ((Capabilities & Feature.GpuPerformanceLevel) == Feature.GpuPerformanceLevel)
  615. {
  616. lock (m_DataLock)
  617. {
  618. m_Data.GpuPerformanceLevel = Constants.UnknownPerformanceLevel;
  619. m_Data.ChangeFlags |= Feature.GpuPerformanceLevel;
  620. }
  621. }
  622. ImmediateUpdateTemperature();
  623. CheckAndInitializeVRR();
  624. (VariableRefreshRate.Instance as VRRManager)?.Resume();
  625. }
  626. void EnableSystemControl()
  627. {
  628. if (!m_AllowPerformanceLevelControlChanges)
  629. return;
  630. m_Data.PerformanceLevelControlAvailable = false;
  631. m_Data.ChangeFlags |= Feature.PerformanceLevelControl;
  632. m_PerformanceLevelControlSystemChange = true;
  633. }
  634. void DisableSystemControl()
  635. {
  636. if (!m_AllowPerformanceLevelControlChanges)
  637. return;
  638. m_Data.PerformanceLevelControlAvailable = true;
  639. m_Data.ChangeFlags |= Feature.PerformanceLevelControl;
  640. m_PerformanceLevelControlSystemChange = false;
  641. }
  642. internal class NativeApi : AndroidJavaProxy
  643. {
  644. private static AndroidJavaObject s_GameSDK = null;
  645. private static IntPtr s_GameSDKRawObjectID;
  646. private static IntPtr s_GetGpuFrameTimeID;
  647. private static IntPtr s_GetHighPrecisionSkinTempLevelID;
  648. private static IntPtr s_GetClusterInfolID;
  649. private static bool s_isAvailable = false;
  650. private static jvalue[] s_NoArgs = new jvalue[0];
  651. private Action<WarningLevel> PerformanceWarningEvent;
  652. private Action PerformanceLevelTimeoutEvent;
  653. private Action CpuPerformanceBoostReleasedByTimeoutEvent;
  654. private Action GpuPerformanceBoostReleasedByTimeoutEvent;
  655. private Action RefreshRateChangedEvent;
  656. public NativeApi(Action<WarningLevel> sustainedPerformanceWarning, Action sustainedPerformanceTimeout, Action refreshRateChanged, Action cpuPerformanceBoostReleasedByTimeout, Action gpuPerformanceBoostReleasedByTimeout)
  657. : base("com.samsung.android.gamesdk.GameSDKManager$Listener")
  658. {
  659. PerformanceWarningEvent = sustainedPerformanceWarning;
  660. PerformanceLevelTimeoutEvent = sustainedPerformanceTimeout;
  661. RefreshRateChangedEvent = refreshRateChanged;
  662. CpuPerformanceBoostReleasedByTimeoutEvent = cpuPerformanceBoostReleasedByTimeout;
  663. GpuPerformanceBoostReleasedByTimeoutEvent = gpuPerformanceBoostReleasedByTimeout;
  664. StaticInit();
  665. }
  666. [Preserve]
  667. void onHighTempWarning(int warningLevel)
  668. {
  669. GameSDKLog.Debug("Listener: onHighTempWarning(warningLevel={0})", warningLevel);
  670. if (warningLevel == 0)
  671. PerformanceWarningEvent(WarningLevel.NoWarning);
  672. else if (warningLevel == 1)
  673. PerformanceWarningEvent(WarningLevel.ThrottlingImminent);
  674. else if (warningLevel == 2)
  675. PerformanceWarningEvent(WarningLevel.Throttling);
  676. }
  677. [Preserve]
  678. void onReleasedByTimeout()
  679. {
  680. GameSDKLog.Debug("Listener: onReleasedByTimeout()");
  681. PerformanceLevelTimeoutEvent();
  682. }
  683. [Preserve]
  684. void onReleasedCpuBoost()
  685. {
  686. GameSDKLog.Debug("Listener: onReleasedCpuBoost()");
  687. CpuPerformanceBoostReleasedByTimeoutEvent();
  688. }
  689. [Preserve]
  690. void onReleasedGpuBoost()
  691. {
  692. GameSDKLog.Debug("Listener: onReleasedGPUBoost()");
  693. GpuPerformanceBoostReleasedByTimeoutEvent();
  694. }
  695. [Preserve]
  696. void onRefreshRateChanged()
  697. {
  698. GameSDKLog.Debug("Listener: onRefreshRateChanged()");
  699. RefreshRateChangedEvent();
  700. }
  701. static IntPtr GetJavaMethodID(IntPtr classId, string name, string sig)
  702. {
  703. AndroidJNI.ExceptionClear();
  704. var mid = AndroidJNI.GetMethodID(classId, name, sig);
  705. IntPtr ex = AndroidJNI.ExceptionOccurred();
  706. if (ex != (IntPtr)0)
  707. {
  708. AndroidJNI.ExceptionDescribe();
  709. AndroidJNI.ExceptionClear();
  710. return (IntPtr)0;
  711. }
  712. else
  713. {
  714. return mid;
  715. }
  716. }
  717. private static void StaticInit()
  718. {
  719. if (s_GameSDK == null)
  720. {
  721. try
  722. {
  723. s_GameSDK = new AndroidJavaObject("com.samsung.android.gamesdk.GameSDKManager");
  724. if (s_GameSDK != null)
  725. s_isAvailable = s_GameSDK.CallStatic<bool>("isAvailable");
  726. }
  727. catch (Exception ex)
  728. {
  729. GameSDKLog.Debug($"GameSDK is not available due to {ex} Aborting Adaptive Performance initialization.");
  730. s_isAvailable = false;
  731. s_GameSDK = null;
  732. }
  733. if (s_isAvailable)
  734. {
  735. s_GameSDKRawObjectID = s_GameSDK.GetRawObject();
  736. var classID = s_GameSDK.GetRawClass();
  737. s_GetGpuFrameTimeID = GetJavaMethodID(classID, "getGpuFrameTime", "()D");
  738. s_GetHighPrecisionSkinTempLevelID = GetJavaMethodID(classID, "getHighPrecisionSkinTempLevel", "()D");
  739. s_GetClusterInfolID = GetJavaMethodID(classID, "getClusterInfo", "()I");
  740. if (s_GetGpuFrameTimeID == (IntPtr)0 || s_GetHighPrecisionSkinTempLevelID == (IntPtr)0)
  741. s_isAvailable = false;
  742. }
  743. }
  744. }
  745. public static bool IsAvailable()
  746. {
  747. StaticInit();
  748. return s_isAvailable;
  749. }
  750. public bool RegisterListener()
  751. {
  752. bool success = false;
  753. try
  754. {
  755. success = s_GameSDK.Call<bool>("setListener", this);
  756. }
  757. catch (Exception)
  758. {
  759. success = false;
  760. }
  761. if (!success)
  762. GameSDKLog.Debug("failed to register listener");
  763. return success;
  764. }
  765. public void UnregisterListener()
  766. {
  767. bool success = true;
  768. try
  769. {
  770. GameSDKLog.Debug("setListener(null)");
  771. success = s_GameSDK.Call<bool>("setListener", (Object)null);
  772. }
  773. catch (Exception)
  774. {
  775. success = false;
  776. }
  777. if (!success)
  778. GameSDKLog.Debug("setListener(null) failed!");
  779. }
  780. public bool Initialize()
  781. {
  782. bool isInitialized = false;
  783. try
  784. {
  785. Version initVersion;
  786. if (TryParseVersion(GetVersion(), out initVersion))
  787. {
  788. if (initVersion >= new Version(3, 2))
  789. {
  790. isInitialized = s_GameSDK.Call<bool>("initialize", initVersion.ToString());
  791. }
  792. else
  793. {
  794. GameSDKLog.Debug("GameSDK {0} is not supported and will not be initialized, Adaptive Performance will not be used.", initVersion);
  795. }
  796. if (isInitialized)
  797. {
  798. isInitialized = RegisterListener();
  799. }
  800. else
  801. {
  802. GameSDKLog.Debug("GameSDK.initialize() failed!");
  803. }
  804. }
  805. }
  806. catch (Exception)
  807. {
  808. GameSDKLog.Debug("[Exception] GameSDK.initialize() failed!");
  809. }
  810. return isInitialized;
  811. }
  812. public void Terminate()
  813. {
  814. UnregisterListener();
  815. try
  816. {
  817. var packageName = Application.identifier;
  818. GameSDKLog.Debug("GameSDK.finalize({0})", packageName);
  819. s_GameSDK.Call<bool>("finalize", packageName);
  820. }
  821. catch (Exception)
  822. {
  823. GameSDKLog.Debug("GameSDK.finalize() failed!");
  824. }
  825. }
  826. public string GetVersion()
  827. {
  828. string sdkVersion = "";
  829. try
  830. {
  831. sdkVersion = s_GameSDK.Call<string>("getVersion");
  832. }
  833. catch (Exception)
  834. {
  835. GameSDKLog.Debug("[Exception] GameSDK.getVersion() failed!");
  836. }
  837. return sdkVersion;
  838. }
  839. public double GetHighPrecisionSkinTempLevel()
  840. {
  841. double currentTempLevel = -1.0;
  842. try
  843. {
  844. currentTempLevel = AndroidJNI.CallDoubleMethod(s_GameSDKRawObjectID, s_GetHighPrecisionSkinTempLevelID, s_NoArgs);
  845. if (AndroidJNI.ExceptionOccurred() != IntPtr.Zero)
  846. {
  847. AndroidJNI.ExceptionDescribe();
  848. AndroidJNI.ExceptionClear();
  849. }
  850. }
  851. catch (Exception)
  852. {
  853. GameSDKLog.Debug("[Exception] GameSDK.getHighPrecisionSkinTempLevel() failed!");
  854. }
  855. return currentTempLevel;
  856. }
  857. public double GetGpuFrameTime()
  858. {
  859. double gpuFrameTime = -1.0;
  860. try
  861. {
  862. gpuFrameTime = AndroidJNI.CallDoubleMethod(s_GameSDKRawObjectID, s_GetGpuFrameTimeID, s_NoArgs);
  863. if (AndroidJNI.ExceptionOccurred() != IntPtr.Zero)
  864. {
  865. AndroidJNI.ExceptionDescribe();
  866. AndroidJNI.ExceptionClear();
  867. }
  868. }
  869. catch (Exception)
  870. {
  871. GameSDKLog.Debug("[Exception] GameSDK.getGpuFrameTime() failed!");
  872. }
  873. return gpuFrameTime;
  874. }
  875. public int SetFreqLevels(int cpu, int gpu)
  876. {
  877. int result = 0;
  878. try
  879. {
  880. result = s_GameSDK.Call<int>("setFreqLevels", cpu, gpu);
  881. GameSDKLog.Debug("setFreqLevels({0}, {1}) -> {2}", cpu, gpu, result);
  882. }
  883. catch (Exception x)
  884. {
  885. GameSDKLog.Debug("[Exception] GameSDK.setFreqLevels({0}, {1}) failed: {2}", cpu, gpu, x);
  886. }
  887. return result;
  888. }
  889. public bool EnableCpuBoost()
  890. {
  891. bool result = false;
  892. try
  893. {
  894. result = s_GameSDK.Call<bool>("setCpuBoostMode", 1);
  895. GameSDKLog.Debug("setCpuBoostMode(1) -> {0}", result);
  896. }
  897. catch (Exception x)
  898. {
  899. GameSDKLog.Debug("[Exception] GameSDK.setCpuBoostMode(1) failed: {0}", x);
  900. }
  901. return result;
  902. }
  903. public bool EnableGpuBoost()
  904. {
  905. bool result = false;
  906. try
  907. {
  908. result = s_GameSDK.Call<bool>("setGpuBoostMode", 1);
  909. GameSDKLog.Debug("setGpuBoostMode(1) -> {0}", result);
  910. }
  911. catch (Exception x)
  912. {
  913. GameSDKLog.Debug("[Exception] GameSDK.setGpuBoostMode(1) failed: {0}", x);
  914. }
  915. return result;
  916. }
  917. public int GetClusterInfo()
  918. {
  919. int result = -999;
  920. try
  921. {
  922. result = AndroidJNI.CallIntMethod(s_GameSDKRawObjectID, s_GetClusterInfolID, s_NoArgs);
  923. if (AndroidJNI.ExceptionOccurred() != IntPtr.Zero)
  924. {
  925. AndroidJNI.ExceptionDescribe();
  926. AndroidJNI.ExceptionClear();
  927. }
  928. GameSDKLog.Debug("getClusterInfo() -> {0}", result);
  929. }
  930. catch (Exception x)
  931. {
  932. GameSDKLog.Debug("[Exception] GameSDK.getClusterInfo() failed: {0}", x);
  933. }
  934. return result;
  935. }
  936. public int GetMaxCpuPerformanceLevel()
  937. {
  938. int maxCpuPerformanceLevel = Constants.UnknownPerformanceLevel;
  939. try
  940. {
  941. maxCpuPerformanceLevel = s_GameSDK.Call<int>("getCPULevelMax");
  942. }
  943. catch (Exception)
  944. {
  945. GameSDKLog.Debug("[Exception] GameSDK.getCPULevelMax() failed!");
  946. }
  947. return maxCpuPerformanceLevel;
  948. }
  949. public int GetMaxGpuPerformanceLevel()
  950. {
  951. int maxGpuPerformanceLevel = Constants.UnknownPerformanceLevel;
  952. try
  953. {
  954. maxGpuPerformanceLevel = s_GameSDK.Call<int>("getGPULevelMax");
  955. }
  956. catch (Exception)
  957. {
  958. GameSDKLog.Debug("[Exception] GameSDK.getGPULevelMax() failed!");
  959. }
  960. return maxGpuPerformanceLevel;
  961. }
  962. public bool IsVariableRefreshRateSupported()
  963. {
  964. bool vrrSupported = false;
  965. try
  966. {
  967. vrrSupported = s_GameSDK.Call<bool>("isGameSDKVariableRefreshRateSupported");
  968. GameSDKLog.Debug("isGameSDKVariableRefreshRateSupported->{0}", vrrSupported);
  969. }
  970. catch (Exception x)
  971. {
  972. GameSDKLog.Debug("[Exception] GameSDK.isGameSDKVariableRefreshRateSupported() failed: " + x.Message);
  973. }
  974. return vrrSupported;
  975. }
  976. public int[] GetSupportedRefreshRates()
  977. {
  978. int[] result = null;
  979. try
  980. {
  981. result = s_GameSDK.Call<int[]>("getSupportedRefreshRates");
  982. }
  983. catch (Exception x)
  984. {
  985. GameSDKLog.Debug("[Exception] GameSDK.getSupportedRefreshRates() failed: " + x.Message);
  986. }
  987. return result != null ? result : new int[0];
  988. }
  989. public bool SetRefreshRate(int targetRefreshRate)
  990. {
  991. try
  992. {
  993. s_GameSDK.Call("setRefreshRate", targetRefreshRate);
  994. }
  995. catch (Exception x)
  996. {
  997. GameSDKLog.Debug("[Exception] GameSDK.setRefreshRate() failed: " + x.Message);
  998. return false;
  999. }
  1000. return true;
  1001. }
  1002. public bool ResetRefreshRate()
  1003. {
  1004. try
  1005. {
  1006. s_GameSDK.Call("resetRefreshRate");
  1007. }
  1008. catch (Exception x)
  1009. {
  1010. GameSDKLog.Debug("[Exception] GameSDK.resetRefreshRate() failed: " + x.Message);
  1011. return false;
  1012. }
  1013. return true;
  1014. }
  1015. public int GetCurrentRefreshRate()
  1016. {
  1017. int result = -1;
  1018. try
  1019. {
  1020. result = s_GameSDK.Call<int>("getCurrentRefreshRate");
  1021. }
  1022. catch (Exception x)
  1023. {
  1024. GameSDKLog.Debug("[Exception] GameSDK.getCurrentRefreshRate() failed: " + x.Message);
  1025. }
  1026. return result;
  1027. }
  1028. }
  1029. [Preserve]
  1030. internal class VRRManager : IVariableRefreshRate
  1031. {
  1032. NativeApi m_Api;
  1033. object m_RefreshRateChangedLock = new object();
  1034. bool m_RefreshRateChanged;
  1035. int[] m_SupportedRefreshRates = new int[0];
  1036. int m_CurrentRefreshRate = -1;
  1037. int m_LastSetRefreshRate = -1;
  1038. private void UpdateRefreshRateInfo()
  1039. {
  1040. var supportedRefreshRates = m_Api.GetSupportedRefreshRates();
  1041. if (settings.highSpeedVRR)
  1042. {
  1043. m_SupportedRefreshRates = supportedRefreshRates;
  1044. }
  1045. else
  1046. {
  1047. List<int> shrunkSupportedRefreshRates = new List<int>();
  1048. for (var i = 0; i < supportedRefreshRates.Length; ++i)
  1049. {
  1050. if (supportedRefreshRates[i] <= 60)
  1051. shrunkSupportedRefreshRates.Add(supportedRefreshRates[i]);
  1052. }
  1053. m_SupportedRefreshRates = shrunkSupportedRefreshRates.ToArray();
  1054. }
  1055. m_CurrentRefreshRate = m_Api.GetCurrentRefreshRate();
  1056. }
  1057. public VRRManager(NativeApi api)
  1058. {
  1059. m_Api = api;
  1060. SetDefaultVRR();
  1061. UpdateRefreshRateInfo();
  1062. }
  1063. // If HighSpeedVRR is not enabled we should not set over 60hz by default
  1064. private void SetDefaultVRR()
  1065. {
  1066. if (settings.highSpeedVRR)
  1067. return;
  1068. var index = Array.IndexOf(m_SupportedRefreshRates, 60);
  1069. if (index != -1)
  1070. {
  1071. SetRefreshRateByIndexInternal(index);
  1072. }
  1073. }
  1074. public void Resume()
  1075. {
  1076. bool changed = false;
  1077. var oldSupportedRefreshRates = m_SupportedRefreshRates;
  1078. var oldRefreshRate = m_LastSetRefreshRate;
  1079. UpdateRefreshRateInfo();
  1080. if (m_CurrentRefreshRate != oldRefreshRate)
  1081. changed = true;
  1082. else if (oldSupportedRefreshRates != m_SupportedRefreshRates)
  1083. changed = true;
  1084. if (changed)
  1085. {
  1086. lock (m_RefreshRateChangedLock)
  1087. {
  1088. m_RefreshRateChanged = true;
  1089. }
  1090. }
  1091. }
  1092. public void Update()
  1093. {
  1094. bool refreshRateChanged = false;
  1095. lock (m_RefreshRateChangedLock)
  1096. {
  1097. refreshRateChanged = m_RefreshRateChanged;
  1098. m_RefreshRateChanged = false;
  1099. }
  1100. if (refreshRateChanged)
  1101. {
  1102. UpdateRefreshRateInfo();
  1103. var index = Array.IndexOf(m_SupportedRefreshRates, m_LastSetRefreshRate);
  1104. if (index != -1)
  1105. {
  1106. SetRefreshRateByIndexInternal(index);
  1107. }
  1108. else if (index == -1 && m_LastSetRefreshRate != -1)
  1109. {
  1110. // Previous set refresh rate is not in available in the refreshrate list.
  1111. // Need to set 60Hz or lowest refresh rate possible.
  1112. // User sets 48Hz, but 48Hz is not on list anymore, because user changed Setting App - Display - Smooth option.
  1113. index = Array.IndexOf(m_SupportedRefreshRates, 60);
  1114. if (index != -1)
  1115. SetRefreshRateByIndexInternal(index);
  1116. }
  1117. RefreshRateChanged?.Invoke();
  1118. }
  1119. }
  1120. public int[] SupportedRefreshRates { get { return m_SupportedRefreshRates; } }
  1121. public int CurrentRefreshRate { get { return m_CurrentRefreshRate; } }
  1122. public bool SetRefreshRateByIndex(int index)
  1123. {
  1124. // Refreshrate potentially set by user
  1125. settings.automaticVRR = false;
  1126. return SetRefreshRateByIndexInternal(index);
  1127. }
  1128. private bool SetRefreshRateByIndexInternal(int index)
  1129. {
  1130. if (index >= 0 && index < SupportedRefreshRates.Length)
  1131. {
  1132. var refreshRateFromIndex = SupportedRefreshRates[index];
  1133. if (Application.targetFrameRate > 0 && index > 0 && SupportedRefreshRates[--index] > Application.targetFrameRate)
  1134. {
  1135. GameSDKLog.Debug("SetRefreshRateByIndex tries to set the refreshRateTarget {0} way higher than the targetFrameRate {1} which is not recommended due to temperature increase and unused performance.", refreshRateFromIndex, Application.targetFrameRate);
  1136. }
  1137. if (!settings.highSpeedVRR)
  1138. {
  1139. if (refreshRateFromIndex > 60)
  1140. {
  1141. GameSDKLog.Debug("High-Speed VRR is not enabled in the settings. Setting a refreshrate ({0}Hz) over 60Hz is not permitted due to temperature reasons.", refreshRateFromIndex);
  1142. return false;
  1143. }
  1144. }
  1145. if (m_Api.SetRefreshRate(refreshRateFromIndex))
  1146. {
  1147. m_CurrentRefreshRate = refreshRateFromIndex;
  1148. m_LastSetRefreshRate = refreshRateFromIndex;
  1149. return true;
  1150. }
  1151. }
  1152. return false;
  1153. }
  1154. public event VariableRefreshRateEventHandler RefreshRateChanged;
  1155. public void OnRefreshRateChanged()
  1156. {
  1157. lock (m_RefreshRateChangedLock)
  1158. {
  1159. m_RefreshRateChanged = true;
  1160. }
  1161. }
  1162. }
  1163. internal class AutoVariableRefreshRate
  1164. {
  1165. SamsungAndroidProviderSettings settings = SamsungAndroidProviderSettings.GetSettings();
  1166. IVariableRefreshRate vrrManager;
  1167. public AutoVariableRefreshRate(IVariableRefreshRate vrrManagerInstance)
  1168. {
  1169. vrrManager = vrrManagerInstance;
  1170. }
  1171. // Temperature checks of hardware are around 5sec and we don't need to check that often.
  1172. float VrrUpdateTime = 1;
  1173. int lastRefreshRateIndex = -1;
  1174. public void UpdateAutoVRR()
  1175. {
  1176. VrrUpdateTime -= Time.unscaledDeltaTime;
  1177. if (VrrUpdateTime <= 0)
  1178. {
  1179. VrrUpdateTime = 1;
  1180. // targetFPS = 70 (in 48/60/96/120)-> vrr 96 never 120
  1181. // targetFPS = 40 (in 48/60/96/120)-> vrr 60 never 96
  1182. // targetFPS = 48/60/96/120 (in 48/60/96/120) -> vrr 48/60/96/12 never higher
  1183. // targetFPS = 70 (in 48/60)-> 60
  1184. var refreshRateIndex = vrrManager.SupportedRefreshRates.Length - 1;
  1185. // we look if a targetFrameRate is set, even in vsync mode were target framerate is ignored. Otherwise we use maximum framerate
  1186. if (Application.targetFrameRate > 0)
  1187. {
  1188. for (int i = 0; i < vrrManager.SupportedRefreshRates.Length; ++i)
  1189. {
  1190. if (Application.targetFrameRate > vrrManager.SupportedRefreshRates[i])
  1191. {
  1192. continue;
  1193. }
  1194. else
  1195. {
  1196. refreshRateIndex = i;
  1197. break;
  1198. }
  1199. }
  1200. }
  1201. if (lastRefreshRateIndex != refreshRateIndex)
  1202. {
  1203. lastRefreshRateIndex = refreshRateIndex;
  1204. vrrManager.SetRefreshRateByIndex(refreshRateIndex);
  1205. // automatic VRR gets disabled in SetRefreshRateByIndex and we want to ensure we still get updated.
  1206. settings.automaticVRR = true;
  1207. }
  1208. }
  1209. }
  1210. }
  1211. }
  1212. }
  1213. }
  1214. #endif