version 2.5.1

This commit is contained in:
Sean Lu
2025-01-10 17:31:06 +08:00
parent 2554daa86e
commit 25a5fd2124
91 changed files with 16226 additions and 11225 deletions

View File

@@ -5,9 +5,25 @@ using UnityEngine;
using AOT;
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespace VIVE.OpenXR
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
internal class HookHandlerAttribute : Attribute
{
public string xrFuncName { get; }
/// <summary>
/// Set this function to handle the hook process in <see cref="ViveInterceptors.XrGetInstanceProcAddrInterceptor" />
/// </summary>
/// <param name="xrFuncName">The hooked openxr function name</param>
public HookHandlerAttribute(string xrFuncName)
{
this.xrFuncName = xrFuncName;
}
}
/// <summary>
/// This class is made for all features that need to intercept OpenXR API calls.
/// Some APIs will be called by Unity internally, and we need to intercept them in c# to get some information.
@@ -22,7 +38,16 @@ namespace VIVE.OpenXR
/// return ViveInterceptors.Instance.HookGetInstanceProcAddr(func);
/// }
/// </summary>
partial class ViveInterceptors
// For extending the ViveInterceptors class, create a new partial class and implement the required functions.
// For example:
// public partial class ViveInterceptors
// {
// [HookHandler("xrYourFunction")]
// private static XrResult OnHookXrYourFunction(XrInstance instance, string name, out IntPtr function)
// { ... }
// }
partial class ViveInterceptors
{
public const string TAG = "VIVE.OpenXR.ViveInterceptors";
static StringBuilder m_sb = null;
@@ -32,8 +57,6 @@ namespace VIVE.OpenXR
return m_sb;
}
}
static void DEBUG(StringBuilder msg) { Debug.LogFormat("{0} {1}", TAG, msg); }
static void ERROR(StringBuilder msg) { Debug.LogErrorFormat("{0} {1}", TAG, msg); }
public static ViveInterceptors instance = null;
public static ViveInterceptors Instance
@@ -48,15 +71,32 @@ namespace VIVE.OpenXR
public ViveInterceptors()
{
Debug.Log("ViveInterceptors");
Log.D("ViveInterceptors");
RegisterFunctions();
}
public delegate XrResult DelegateXrGetInstanceProcAddr(XrInstance instance, string name, out IntPtr function);
private static readonly DelegateXrGetInstanceProcAddr hookXrGetInstanceProcAddrHandle = new DelegateXrGetInstanceProcAddr(XrGetInstanceProcAddrInterceptor);
private static readonly IntPtr hookGetInstanceProcAddrHandlePtr = Marshal.GetFunctionPointerForDelegate(hookXrGetInstanceProcAddrHandle);
static DelegateXrGetInstanceProcAddr XrGetInstanceProcAddrOriginal = null;
delegate XrResult HookHandler(XrInstance instance, string name, out IntPtr function);
static readonly Dictionary<string, HookHandler> interceptors = new Dictionary<string, HookHandler>();
[MonoPInvokeCallback(typeof(DelegateXrGetInstanceProcAddr))]
private static void RegisterFunctions()
{
var methods = typeof(ViveInterceptors).GetMethods(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
foreach (var method in methods)
{
var attribute = method.GetCustomAttributes(typeof(HookHandlerAttribute), false).FirstOrDefault() as HookHandlerAttribute;
if (attribute != null)
{
Log.I(TAG, $"Registering hook handler {attribute.xrFuncName}");
interceptors.Add(attribute.xrFuncName, (HookHandler)method.CreateDelegate(typeof(HookHandler)));
}
}
}
private static readonly OpenXRHelper.xrGetInstanceProcAddrDelegate hookXrGetInstanceProcAddrHandle = new OpenXRHelper.xrGetInstanceProcAddrDelegate(XrGetInstanceProcAddrInterceptor);
private static readonly IntPtr hookGetInstanceProcAddrHandlePtr = Marshal.GetFunctionPointerForDelegate(hookXrGetInstanceProcAddrHandle);
static OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddrOriginal = null;
[MonoPInvokeCallback(typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate))]
private static XrResult XrGetInstanceProcAddrInterceptor(XrInstance instance, string name, out IntPtr function)
{
// Used to check if the original function is already hooked.
@@ -66,64 +106,16 @@ namespace VIVE.OpenXR
return XrResult.XR_SUCCESS;
}
// Custom interceptors
if (name == "xrWaitFrame" && requiredFunctions.Contains(name))
// Check if the function is intercepted by other features
if (interceptors.ContainsKey(name))
{
Debug.Log($"{TAG}: XrGetInstanceProcAddrInterceptor() {name} is intercepted.");
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
if (ret == XrResult.XR_SUCCESS)
{
XrWaitFrameOriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrWaitFrame>(function);
function = xrWaitFrameInterceptorPtr;
}
return ret;
}
// If no request for this function, call the original function directly.
if (!requiredFunctions.Contains(name))
return XrGetInstanceProcAddrOriginal(instance, name, out function);
if (name == "xrEndFrame" && requiredFunctions.Contains(name))
{
Debug.Log($"{TAG}: XrGetInstanceProcAddrInterceptor() {name} is intercepted.");
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
var ret = interceptors[name](instance, name, out function);
if (ret == XrResult.XR_SUCCESS)
{
XrEndFrameOriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrEndFrame>(function);
function = xrEndFrameInterceptorPtr;
}
return ret;
}
#if PERFORMANCE_TEST
if (name == "xrLocateSpace" && requiredFunctions.Contains(name))
{
Debug.Log($"{TAG}: XrGetInstanceProcAddrInterceptor() {name} is intercepted.");
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
if (ret == XrResult.XR_SUCCESS)
{
XrLocateSpaceOriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrLocateSpace>(function);
function = xrLocateSpaceInterceptorPtr;
}
return ret;
}
#endif
if (name == "xrPollEvent" && requiredFunctions.Contains(name))
{
Debug.Log($"{TAG}: XrGetInstanceProcAddrInterceptor() {name} is intercepted.");
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
if (ret == XrResult.XR_SUCCESS)
{
xrPollEventOrigin = Marshal.GetDelegateForFunctionPointer < xrPollEventDelegate > (function);
function = xrPollEventPtr;
}
return ret;
}
if (name == "xrBeginSession" && requiredFunctions.Contains(name))
{
Debug.Log($"{TAG}: XrGetInstanceProcAddrInterceptor() {name} is intercepted.");
var ret = XrGetInstanceProcAddrOriginal(instance, name, out function);
if (ret == XrResult.XR_SUCCESS)
{
xrBeginSessionOrigin = Marshal.GetDelegateForFunctionPointer<xrBeginSessionDelegate>(function);
function = xrBeginSessionPtr;
}
Log.I(TAG, name + " is intercepted");
return ret;
}
@@ -132,23 +124,23 @@ namespace VIVE.OpenXR
public IntPtr HookGetInstanceProcAddr(IntPtr func)
{
Debug.Log($"{TAG}: HookGetInstanceProcAddr");
Log.D(TAG, "HookGetInstanceProcAddr");
if (XrGetInstanceProcAddrOriginal == null)
{
Debug.Log($"{TAG}: registering our own xrGetInstanceProcAddr");
XrGetInstanceProcAddrOriginal = Marshal.GetDelegateForFunctionPointer<DelegateXrGetInstanceProcAddr>(func);
Log.D(TAG, "registering our own xrGetInstanceProcAddr");
XrGetInstanceProcAddrOriginal = Marshal.GetDelegateForFunctionPointer<OpenXRHelper.xrGetInstanceProcAddrDelegate>(func);
#if UNITY_EDITOR
if (Application.isEditor) {
// This is a trick to check if the original function is already hooked by this class. Sometimes, the static XrGetInstanceProcAddrOriginal didn't work as expected.
Debug.Log($"{TAG}: Check if duplicate hooked by this script with instance=0 and \"ViveInterceptorHooked\" name. If following a loader error, ignore it.");
Log.D(TAG, "Check if duplicate hooked by this script with instance=0 and \"ViveInterceptorHooked\" name. If following a loader error, ignore it.");
// E OpenXR-Loader: Error [SPEC | xrGetInstanceProcAddr | VUID-xrGetInstanceProcAddr-instance-parameter] : XR_NULL_HANDLE for instance but query for ViveInterceptorHooked requires a valid instance
// Call XrGetInstanceProcAddrOriginal to check if the original function is already hooked by this class
if (XrGetInstanceProcAddrOriginal(0, "ViveInterceptorHooked", out IntPtr function) == XrResult.XR_SUCCESS)
{
// If it is called successfully, it means the original function is already hooked. So we should return the original function.
Debug.Log($"{TAG}: Already hooked");
Log.D(TAG, "Already hooked");
return func;
}
}
@@ -173,9 +165,34 @@ namespace VIVE.OpenXR
/// <param name="name"></param>
public void AddRequiredFunction(string name)
{
if (requiredFunctions.Contains(name)) return;
Debug.Log($"{TAG}: AddRequiredFunction({name})");
requiredFunctions.Add(name);
Log.D(TAG, $"AddRequiredFunction({name})");
if (!interceptors.ContainsKey(name))
{
Log.E(TAG, $"AddRequiredFunction({name}) failed. No such function.");
return;
}
if (!requiredFunctions.Contains(name))
requiredFunctions.Add(name);
// If your function support unregister, you can add the reference count here.
if (name == "xrLocateViews")
xrLocateViewsReferenceCount++;
}
/// <summary>
/// If no need to use this hooked function, call this will remove your requirement.
/// If all requirements are removed, the original function will be called directly.
/// </summary>
/// <param name="name"></param>
public void RemoveRequiredFunction(string name)
{
// If your function support unregister, you can add the reference count here.
if (requiredFunctions.Contains(name))
{
if (name == "xrLocateViews")
xrLocateViewsReferenceCount = Mathf.Max(xrLocateViewsReferenceCount--, 0);
}
}
}
}