// "VIVE SDK // © 2020 HTC Corporation. All Rights Reserved. // // Unless otherwise required by copyright law and practice, // upon the execution of HTC SDK license agreement, // HTC grants you access to and use of the VIVE SDK(s). // You shall fully comply with all of HTC’s SDK license agreement terms and // conditions signed by you and all SDK and API requirements, // specifications, and documentation provided by HTC to You." using UnityEngine.XR.OpenXR; using UnityEngine.XR.OpenXR.Features; using UnityEngine; using System.Runtime.InteropServices; using System; #if UNITY_EDITOR using UnityEditor; using UnityEditor.XR.OpenXR.Features; #endif namespace VIVE.OpenXR.DisplayRefreshRate { #if UNITY_EDITOR [OpenXRFeature(UiName = "VIVE XR Display Refresh Rate", BuildTargetGroups = new[] { BuildTargetGroup.Android}, Company = "HTC", Desc = "Support the display refresh rate.", DocumentationLink = "..\\Documentation", OpenxrExtensionStrings = kOpenxrExtensionString, Version = "1.0.0", FeatureId = featureId)] #endif public class ViveDisplayRefreshRate : OpenXRFeature { const string LOG_TAG = "VIVE.OpenXR.DisplayRefreshRate"; void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); } void WARNING(string msg) { Debug.LogWarning(LOG_TAG + " " + msg); } void ERROR(string msg) { Debug.LogError(LOG_TAG + " " + msg); } /// /// OpenXR specification 12.54. XR_FB_display_refresh_rate. /// public const string kOpenxrExtensionString = "XR_FB_display_refresh_rate"; /// /// The feature id string. This is used to give the feature a well known id for reference. /// public const string featureId = "vive.openxr.feature.displayrefreshrate"; #region OpenXR Life Cycle protected override IntPtr HookGetInstanceProcAddr(IntPtr func) { ViveInterceptors.Instance.AddRequiredFunction("xrPollEvent"); return ViveInterceptors.Instance.HookGetInstanceProcAddr(func); } private XrInstance m_XrInstance = 0; /// /// Called when xrCreateInstance is done. /// /// The created instance. /// True for valid XrInstance protected override bool OnInstanceCreate(ulong xrInstance) { if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString)) { WARNING("OnInstanceCreate() " + kOpenxrExtensionString + " is NOT enabled."); return false; } m_XrInstance = xrInstance; DEBUG("OnInstanceCreate() " + m_XrInstance); return GetXrFunctionDelegates(m_XrInstance); } /// /// Called when xrDestroyInstance is done. /// /// The instance to destroy. protected override void OnInstanceDestroy(ulong xrInstance) { m_XrInstance = 0; DEBUG("OnInstanceDestroy() " + xrInstance); } private XrSystemId m_XrSystemId = 0; /// /// Called when the XrSystemId retrieved by xrGetSystem is changed. /// /// The system id. protected override void OnSystemChange(ulong xrSystem) { m_XrSystemId = xrSystem; DEBUG("OnSystemChange() " + m_XrSystemId); } private XrSession m_XrSession = 0; /// /// Called when xrCreateSession is done. /// /// The created session ID. protected override void OnSessionCreate(ulong xrSession) { m_XrSession = xrSession; DEBUG("OnSessionCreate() " + m_XrSession); } /// /// Called when xrDestroySession is done. /// /// The session ID to destroy. protected override void OnSessionDestroy(ulong xrSession) { DEBUG("OnSessionDestroy() " + xrSession); m_XrSession = 0; } #endregion #region OpenXR function delegates /// xrGetInstanceProcAddr OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr; //xrRequestDisplayRefreshRateFB OpenXRHelper.xrRequestDisplayRefreshRateFBDelegate xrRequestDisplayRefreshRateFB; //xrGetDisplayRefreshRateFB OpenXRHelper.xrGetDisplayRefreshRateFBDelegate xrGetDisplayRefreshRateFB; //xrEnumerateDisplayRefreshRatesFB OpenXRHelper.xrEnumerateDisplayRefreshRatesFBDelegate xrEnumerateDisplayRefreshRatesFB; private bool GetXrFunctionDelegates(XrInstance xrInstance) { /// xrGetInstanceProcAddr if (xrGetInstanceProcAddr != null && xrGetInstanceProcAddr != IntPtr.Zero) { DEBUG("Get function pointer of xrGetInstanceProcAddr."); XrGetInstanceProcAddr = Marshal.GetDelegateForFunctionPointer( xrGetInstanceProcAddr, typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate)) as OpenXRHelper.xrGetInstanceProcAddrDelegate; } else { ERROR("xrGetInstanceProcAddr"); return false; } IntPtr funcPtr = IntPtr.Zero; DEBUG("Try Get function pointer of xrRequestDisplayRefreshRateFB."); /// xrRequestDisplayRefreshRateFB if (XrGetInstanceProcAddr(xrInstance, "xrRequestDisplayRefreshRateFB", out funcPtr) == XrResult.XR_SUCCESS) { if (funcPtr != IntPtr.Zero) { DEBUG("Get function pointer of xrRequestDisplayRefreshRateFB."); xrRequestDisplayRefreshRateFB = Marshal.GetDelegateForFunctionPointer( funcPtr, typeof(OpenXRHelper.xrRequestDisplayRefreshRateFBDelegate)) as OpenXRHelper.xrRequestDisplayRefreshRateFBDelegate; } else { ERROR("0. Get function pointer of xrRequestDisplayRefreshRateFB failed."); } } else { ERROR("1. Get function pointer of xrRequestDisplayRefreshRateFB failed."); } /// xrGetDisplayRefreshRateFB if (XrGetInstanceProcAddr(xrInstance, "xrGetDisplayRefreshRateFB", out funcPtr) == XrResult.XR_SUCCESS) { if (funcPtr != IntPtr.Zero) { DEBUG("Get function pointer of xrGetDisplayRefreshRateFB."); xrGetDisplayRefreshRateFB = Marshal.GetDelegateForFunctionPointer( funcPtr, typeof(OpenXRHelper.xrGetDisplayRefreshRateFBDelegate)) as OpenXRHelper.xrGetDisplayRefreshRateFBDelegate; } else { ERROR("0. Get function pointer of xrGetDisplayRefreshRateFB failed."); } } else { ERROR("1. Get function pointer of xrGetDisplayRefreshRateFB failed."); } /// xrEnumerateDisplayRefreshRatesFB if (XrGetInstanceProcAddr(xrInstance, "xrEnumerateDisplayRefreshRatesFB", out funcPtr) == XrResult.XR_SUCCESS) { if (funcPtr != IntPtr.Zero) { DEBUG("Get function pointer of xrEnumerateDisplayRefreshRatesFB."); xrEnumerateDisplayRefreshRatesFB = Marshal.GetDelegateForFunctionPointer( funcPtr, typeof(OpenXRHelper.xrEnumerateDisplayRefreshRatesFBDelegate)) as OpenXRHelper.xrEnumerateDisplayRefreshRatesFBDelegate; } else { ERROR("0. Get function pointer of xrEnumerateDisplayRefreshRatesFB failed."); } } else { ERROR("1. Get function pointer of xrEnumerateDisplayRefreshRatesFB failed."); } return true; } /// /// Refer to OpenXR xrRequestDisplayRefreshRateFB. /// /// /// public XrResult RequestDisplayRefreshRate(float displayRefreshRate) { if (!OpenXRRuntime.IsExtensionEnabled("XR_FB_display_refresh_rate")) { WARNING("RequestDisplayRefreshRate: XR_FB_display_refresh_rate is NOT enabled."); return XrResult.XR_ERROR_FEATURE_UNSUPPORTED; } return xrRequestDisplayRefreshRateFB(m_XrSession, displayRefreshRate); } /// /// Refer to OpenXR xrGetDisplayRefreshRateFB. /// /// /// public XrResult GetDisplayRefreshRate(out float displayRefreshRate) { if (!OpenXRRuntime.IsExtensionEnabled("XR_FB_display_refresh_rate")) { WARNING("GetDisplayRefreshRate: XR_FB_display_refresh_rate is NOT enabled."); displayRefreshRate = 90.0f; return XrResult.XR_ERROR_FEATURE_UNSUPPORTED; } return xrGetDisplayRefreshRateFB(m_XrSession, out displayRefreshRate); } /// /// Refer to OpenXR xrEnumerateDisplayRefreshRatesFB. /// /// /// /// /// public XrResult EnumerateDisplayRefreshRates(UInt32 displayRefreshRateCapacityInput, out UInt32 displayRefreshRateCountOutput, out float displayRefreshRates) { if (!OpenXRRuntime.IsExtensionEnabled("XR_FB_display_refresh_rate")) { WARNING("EnumerateDisplayRefreshRates: XR_FB_display_refresh_rate is NOT enabled."); displayRefreshRateCountOutput = 0; displayRefreshRates = 90.0f; return XrResult.XR_ERROR_FEATURE_UNSUPPORTED; } return xrEnumerateDisplayRefreshRatesFB(m_XrSession, displayRefreshRateCapacityInput, out displayRefreshRateCountOutput, out displayRefreshRates); } #endregion } }