Skip to content

Instantly share code, notes, and snippets.

@mikeskydev
Last active March 7, 2024 17:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikeskydev/8e27668eade137a1085843868fbc6269 to your computer and use it in GitHub Desktop.
Save mikeskydev/8e27668eade137a1085843868fbc6269 to your computer and use it in GitHub Desktop.
//Copyright Icosa Foundation
using System;
using System.Runtime.InteropServices;
using AOT;
using UnityEditor;
using UnityEngine;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features;
using System.Collections;
using Unity.Collections;
#if UNITY_EDITOR
using UnityEditor.XR.OpenXR.Features;
#endif
namespace IcosaFoundation.OpenXR.Unity.FBDisplayRefreshRate
{
/// <summary>
/// Example feature showing how to intercept a single OpenXR function.
/// </summary>
#if UNITY_EDITOR
[OpenXRFeature(UiName = "Facebook Display Refresh Rate",
BuildTargetGroups = new []{BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android},
Company = "Icosa Foundation",
Desc = "Implement XR_FB_display_refresh_rate without OVR SDK",
OpenxrExtensionStrings = "XR_FB_display_refresh_rate",
Version = "0.0.1",
FeatureId = featureId)]
#endif
public class FBDisplayRefreshRateFeature : OpenXRFeature
{
public const string featureId = "foundation.icosa.openxr-facebook-displayrefreshrate";
private static ulong XrSession = 0;
private static ulong XrInstance = 0;
protected override bool OnInstanceCreate(ulong xrInstance)
{
XrInstance = xrInstance;
var success = LoadBindings();
return true;
}
protected override void OnSessionCreate(ulong xrSession)
{
XrSession = xrSession;
}
public static float[] GetDisplayRefreshRates()
{
if(XrSession == 0)
{
return null;
}
var success = xrEnumerateDisplayRefreshRatesFB(XrSession, 0, out int displayRefreshRateCapacity, null);
if(success == XrResult.Success)
{
var displayRefreshRates = new float[displayRefreshRateCapacity];
success = xrEnumerateDisplayRefreshRatesFB(XrSession, displayRefreshRateCapacity, out displayRefreshRateCapacity, displayRefreshRates);
if(success == XrResult.Success)
{
return displayRefreshRates;
}
}
return null;
}
public static float RefreshRate
{
get { return GetRefreshRate(); }
set { SetRefreshRate(value); }
}
public static float GetRefreshRate()
{
if(XrSession == 0)
{
return 0.0f;
}
var success = xrGetDisplayRefreshRateFB(XrSession, out var displayRefreshRate);
if (success == XrResult.Success)
{
return displayRefreshRate;
}
return 0.0f;
}
public static bool SetRefreshRate(float targetRefreshRate)
{
if(XrSession == 0)
{
return false;
}
var success = xrRequestDisplayRefreshRateFB(XrSession, targetRefreshRate);
if(success == XrResult.Success)
{
return true;
}
return false;
}
private delegate ulong GetInstanceProcAddrDelegate(ulong instance, string procName, out IntPtr procAddr);
private static IntPtr GetFunction(string functionName)
{
var getInstanceProcAddr = Marshal.GetDelegateForFunctionPointer<GetInstanceProcAddrDelegate>(xrGetInstanceProcAddr);
ulong result = getInstanceProcAddr(XrInstance, functionName, out var returnPtr);
if (result < 0)
{
Debug.LogWarning($"Failed to find {functionName}");
return IntPtr.Zero;
}
return returnPtr;
}
#region OpenXR native bindings and types
enum XrStructureType : UInt64
{
XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB = 0x1000101000
}
enum XrResult : Int64
{
Success = 0,
XR_ERROR_DISPLAY_REFRESH_RATE_UNSUPPORTED_FB = -0x1000101000
}
[StructLayout(LayoutKind.Sequential)]
struct XrEventDataDisplayRefreshRateChangedFB
{
public XrStructureType type;
public IntPtr next;
public float fromDisplayRefreshRate;
public float toDisplayRefreshRate;
public XrEventDataDisplayRefreshRateChangedFB(float fromDisplayRefreshRate, float toDisplayRefreshRate)
{
type = XrStructureType.XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB;
next = IntPtr.Zero;
this.fromDisplayRefreshRate = fromDisplayRefreshRate;
this.toDisplayRefreshRate = toDisplayRefreshRate;
}
}
delegate XrResult del_xrEnumerateDisplayRefreshRatesFB(ulong session, [In] int displayRefreshRateCapacityInput, out int displayRefreshRateCapacityOutput, [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] float[] displayRefreshRates);
delegate XrResult del_xrGetDisplayRefreshRateFB (ulong session, out float displayRefreshRate);
delegate XrResult del_xrRequestDisplayRefreshRateFB (ulong session, float displayRefreshRate);
static del_xrEnumerateDisplayRefreshRatesFB xrEnumerateDisplayRefreshRatesFB;
static del_xrGetDisplayRefreshRateFB xrGetDisplayRefreshRateFB;
static del_xrRequestDisplayRefreshRateFB xrRequestDisplayRefreshRateFB;
bool LoadBindings()
{
xrEnumerateDisplayRefreshRatesFB = Marshal.GetDelegateForFunctionPointer<del_xrEnumerateDisplayRefreshRatesFB>(GetFunction("xrEnumerateDisplayRefreshRatesFB"));
xrGetDisplayRefreshRateFB = Marshal.GetDelegateForFunctionPointer<del_xrGetDisplayRefreshRateFB> (GetFunction("xrGetDisplayRefreshRateFB"));
xrRequestDisplayRefreshRateFB = Marshal.GetDelegateForFunctionPointer<del_xrRequestDisplayRefreshRateFB> (GetFunction("xrRequestDisplayRefreshRateFB"));
return
xrEnumerateDisplayRefreshRatesFB != null &&
xrGetDisplayRefreshRateFB != null &&
xrRequestDisplayRefreshRateFB != null;
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment