using System; using System.Runtime.InteropServices; using UnityEngine; #if ENABLE_VR || PACKAGE_DOCS_GENERATION using System.Collections.Generic; using UnityEngine.XR; using XRStats = UnityEngine.XR.Provider.XRStats; #endif namespace Unity.XR.Oculus { public static class Performance { /// /// Set the CPU Level for the device /// /// /// Allowable values are integers in the range 0 - 4 (inclusive). A value of 0 is the lowest performance level but is the most power efficient. /// Please note: as this is a hint, it may not be immediately reflected by the system. /// /// /// True if we have no errors returned by the native function. We return false when the native function returns a failure condition. /// Check the log for more information. /// public static bool TrySetCPULevel(int level) { return (NativeMethods.SetCPULevel(level) == 0); } /// /// Set the GPU performance level /// /// /// Allowable values are integers in the range 0 - 4 (inclusive). A value of 0 is the lowest performance level but is the most power efficient. /// Please note: as this is a hint, it may not be immediately reflected by the system. /// /// /// True if we have no errors returned by the native function. We return false when the native function returns a failure condition. Check /// the log for more information. /// public static bool TrySetGPULevel(int level) { return (NativeMethods.SetGPULevel(level) == 0); } private static float[] cachedDisplayAvailableFrequencies = null; /// /// Get an array of available display frequencies supported by this hardware. /// public static bool TryGetAvailableDisplayRefreshRates(out float[] refreshRates) { if (cachedDisplayAvailableFrequencies == null || cachedDisplayAvailableFrequencies.Length == 0) { cachedDisplayAvailableFrequencies = new float[0]; int numFrequencies = 0; if (NativeMethods.GetDisplayAvailableFrequencies(IntPtr.Zero, ref numFrequencies) && numFrequencies > 0) { int size = sizeof(float) * numFrequencies; IntPtr ptr = Marshal.AllocHGlobal(size); if (NativeMethods.GetDisplayAvailableFrequencies(ptr, ref numFrequencies) && numFrequencies > 0) { cachedDisplayAvailableFrequencies = new float[numFrequencies]; Marshal.Copy(ptr, cachedDisplayAvailableFrequencies, 0, numFrequencies); } Marshal.FreeHGlobal(ptr); } } refreshRates = cachedDisplayAvailableFrequencies; return (cachedDisplayAvailableFrequencies.Length > 0); } /// /// Set the current display frequency. /// public static bool TrySetDisplayRefreshRate(float refreshRate) { return NativeMethods.SetDisplayFrequency(refreshRate); } /// /// Get the current display frequency. /// public static bool TryGetDisplayRefreshRate(out float refreshRate) { return NativeMethods.GetDisplayFrequency(out refreshRate); } } public class Stats { private static IntegratedSubsystem m_Display; private static string m_PluginVersion = string.Empty; /// /// Gets the version of OVRPlugin currently in use. Format: "major.minor.release" /// public static string PluginVersion { get { if (string.Equals(string.Empty, m_PluginVersion)) { byte[] buf = new byte[256]; NativeMethods.GetOVRPVersion(buf); var end = Array.IndexOf(buf, 0); m_PluginVersion = System.Text.Encoding.ASCII.GetString(buf, 0, end); } return m_PluginVersion; } } /// /// Provides performance statistics useful for adaptive performance systems. Not every stat is supported on every Oculus platform and will always return a value of 0 if unsupported. /// public static class AdaptivePerformance { /// /// Reports the time the application spent on the GPU last frame in seconds. /// public static float GPUAppTime { get { #if ENABLE_VR float val; ((XRDisplaySubsystem) GetOculusDisplaySubsystem()).TryGetAppGPUTimeLastFrame(out val); return val; #else return default; #endif } } /// /// Reports the time the compositor spent on the GPU last frame in seconds. /// public static float GPUCompositorTime { get { #if ENABLE_VR float val; ((XRDisplaySubsystem) GetOculusDisplaySubsystem()).TryGetCompositorGPUTimeLastFrame(out val); return val; #else return default; #endif } } /// /// Reports the latency from when the last predicted tracking information was queried by the application to the moment the middle scaline of the target frame is illuminated on the display. /// public static float MotionToPhoton { get { #if ENABLE_VR float val; ((XRDisplaySubsystem) GetOculusDisplaySubsystem()).TryGetMotionToPhoton(out val); return val; #else return default; #endif } } /// /// Reports the display's refresh rate in frames per second /// [Obsolete("RefreshRate is deprecated. Use Performance.TryGetDisplayRefreshRate instead.", false)] public static float RefreshRate { get { #if ENABLE_VR float val; ((XRDisplaySubsystem) GetOculusDisplaySubsystem()).TryGetDisplayRefreshRate(out val); return val; #else return default; #endif } } /// /// Gets the current battery temperature in degrees Celsius. /// public static float BatteryTemp { get { #if ENABLE_VR float batteryTemp; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "batteryTemperature", out batteryTemp); return batteryTemp; #else return default; #endif } } /// /// Gets the current available battery charge, ranging from 0.0 (empty) to 1.0 (full). /// public static float BatteryLevel { get { #if ENABLE_VR float batteryLevel; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "batteryLevel", out batteryLevel); return batteryLevel; #else return default; #endif } } /// /// If true, the system is running in a reduced performance mode to save power. /// public static bool PowerSavingMode { get { #if ENABLE_VR float powerSavingMode; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "powerSavingMode", out powerSavingMode); return !(powerSavingMode == 0.0f); #else return default; #endif } } /// /// Returns the recommended amount to scale GPU work in order to maintain framerate. /// Can be used to adjust viewportScale and textureScale /// public static float AdaptivePerformanceScale { get { #if ENABLE_VR float performanceScale; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "adaptivePerformanceScale", out performanceScale); return performanceScale; #else return default; #endif } } /// /// Gets the current CPU performance level, integer in the range 0 - 3. /// public static int CPULevel { get { #if ENABLE_VR float cpuLevel; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "cpuLevel", out cpuLevel); return (int) cpuLevel; #else return default; #endif } } /// /// Gets the current GPU performance level, integer in the range 0 - 3. /// public static int GPULevel { get { #if ENABLE_VR float gpuLevel; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "gpuLevel", out gpuLevel); return (int) gpuLevel; #else return default; #endif } } } /// /// Provides additional perf metrics. These stats will not be tracked unless the user makes a PerfMetrics.EnablePerfMetrics(true) method call. Not every stat is supported on every Oculus platform and will always return a value of 0 if unsupported. /// Note: PerfMetrics stats will return 0 when using the OpenXR runtime. The suggested replacement is to use the profiling tools available via the Oculus Developer Hub: https://developer.oculus.com/documentation/unity/ts-odh-logs-metrics/ /// public static class PerfMetrics { /// /// Reports the time the application spent on the CPU last frame in seconds. /// public static float AppCPUTime { get { #if ENABLE_VR float val; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "perfmetrics.appcputime", out val); return val; #else return default; #endif } } /// /// Reports the time the application spen on the GPU last frame in seconds. /// public static float AppGPUTime { get { #if ENABLE_VR float val; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "perfmetrics.appgputime", out val); return val; #else return default; #endif } } /// /// Reports the time the compositor spent on the CPU last frame in seconds. /// public static float CompositorCPUTime { get { #if ENABLE_VR float val; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "perfmetrics.compositorcputime", out val); return val; #else return default; #endif } } /// /// Reports the time the compositor spent on the GPU last frame in seconds. /// public static float CompositorGPUTime { get { #if ENABLE_VR float val; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "perfmetrics.compositorgputime", out val); return val; #else return default; #endif } } /// /// Reports the GPU utilization as a value from 0.0 - 1.0. /// public static float GPUUtilization { get { #if ENABLE_VR float val; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "perfmetrics.gpuutil", out val); return val; #else return default; #endif } } /// /// Reports the average CPU utilization as a value from 0.0 - 1.0. /// public static float CPUUtilizationAverage { get { #if ENABLE_VR float val; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "perfmetrics.cpuutilavg", out val); return val; #else return default; #endif } } /// /// Reports the worst CPU utilization as a value from 0.0 - 1.0. /// public static float CPUUtilizationWorst { get { #if ENABLE_VR float val; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "perfmetrics.cpuutilworst", out val); return val; #else return default; #endif } } /// /// Reports the CPU clock frequency /// public static float CPUClockFrequency { get { #if ENABLE_VR float val; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "perfmetrics.cpuclockfreq", out val); return val; #else return default; #endif } } /// /// Reports the GPU clock frequency /// public static float GPUClockFrequency { get { #if ENABLE_VR float val; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "perfmetrics.gpuclockfreq", out val); return val; #else return default; #endif } } /// /// Enable or disable provider tracking perf metrics. Perf metrics are disabled by default. /// public static void EnablePerfMetrics(bool enable) { NativeMethods.EnablePerfMetrics(enable); } } /// /// Provides additional application metrics. These metrics will not be tracked unless the user makes a AppMetrics.EnableAppMetrics(true) method call. /// Note: AppMetrics are deprecated and currently return 0 on all Oculus runtimes. The suggested replacement is to use the profiling tools available via the Oculus Developer Hub: https://developer.oculus.com/documentation/unity/ts-odh-logs-metrics/ /// public static class AppMetrics { public static float AppQueueAheadTime { get { #if ENABLE_VR float val; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "appstats.appqueueahead", out val); return val; #else return default; #endif } } public static float AppCPUElapsedTime { get { #if ENABLE_VR float val; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "appstats.cpuelapsedtime", out val); return val; #else return default; #endif } } /// /// Reports the number of frames dropped by the compositor. /// public static float CompositorDroppedFrames { get { #if ENABLE_VR float val; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "appstats.compositordroppedframes", out val); return val; #else return default; #endif } } public static float CompositorLatency { get { #if ENABLE_VR float val; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "appstats.compositorlatency", out val); return val; #else return default; #endif } } /// /// Reports the time the compositor spent on the CPU last frame in seconds. /// public static float CompositorCPUTime { get { #if ENABLE_VR float val; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "appstats.compositorcputime", out val); return val; #else return default; #endif } } /// /// Reports the total time from beginning of the main thread until compositor frame submission. /// public static float CPUStartToGPUEnd { get { #if ENABLE_VR float val; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "appstats.compositorcpustartgpuendelapsedtime", out val); return val; #else return default; #endif } } /// /// Reports the total time from compositor frame submission until the frame sync point. /// public static float GPUEndToVsync { get { #if ENABLE_VR float val; XRStats.TryGetStat(GetOculusDisplaySubsystem(), "appstats.compositorgpuendtovsyncelapsedtime", out val); return val; #else return default; #endif } } public static void EnableAppMetrics(bool enable) { NativeMethods.EnableAppMetrics(enable); } } private static IntegratedSubsystem GetOculusDisplaySubsystem() { if (m_Display != null) return m_Display; #if ENABLE_VR List displays = new List(); SubsystemManager.GetInstances(displays); foreach (XRDisplaySubsystem xrDisplaySubsystem in displays) { if (xrDisplaySubsystem.SubsystemDescriptor.id == "oculus display" && xrDisplaySubsystem.running) { m_Display = xrDisplaySubsystem; return m_Display; } } #endif Debug.LogError("No active Oculus display subsystem was found."); return m_Display; } } }