Is there / can there be a way to access events coming from xrPollEvent? The OpenXR™ 1.1.40 Specification (with all registered extensions)
I am looking to build new OpenXR feature extensions that can hook and respond to those functions. I am aware of xrGetInstanceProcAddr
but the spec states that the event will be consumed, so I wouldn’t want to do that to remove the core functions of the main OpenXR plugin.
If you only are interested in session lifecycle events you can override OnSessionStateChange(int, int) on your OpenXRFeature to receive a callback whenever the lifecycle state changes. The integers directly map to XrSessionState so it should be fairly straightforward to cast over to a C# enum with the same mappings.
If you specifically need the output of xrPollEvent though you’ll probably have to go through the interception route. While the event is consumed when calling the function you can still keep a reference to the old function pointer and just forward to it after you finish reading the event from the intercepted function. There’s an example of how to do exactly that included in the interception sample provided with the OpenXR Plugin
That makes sense, I’ll look into the interception route. Thanks!
As I’m aiming to do as much in C# land as possible, I’m hoping there’s a chance that the function (or some wrapper) could be exposed by Unity at some point. I can imagine this could be done on quite a generic level just be re-emitting the XrStructureType
and the data buffer, leaving the Feature to decide what to do with it.
If you dont mind the overhead and dealing with unsafe code, it is actually possible to do interception through C# as well and it works fairly well (in my experience anyways ). To give a brief example, intercepting something like xrEndFrame inside a feature could look like this:
private unsafe delegate XrResult XrGetInstanceProcAddr(XrHandle xrInstance, string functionName, IntPtr* function);
private unsafe delegate XrResult XrEndFrame(XrHandle xrSession, XrFrameEndInfo* frameEndInfo);
private static XrGetInstanceProcAddr Original_xrGetInstanceProcAddr = null;
private static XrEndFrame Original_xrEndFrame = null;
protected override IntPtr HookGetInstanceProcAddr(IntPtr xrGetInstanceProcAddr)
{
return Intercept_xrGetInstanceProcAddr(xrGetInstanceProcAddr);
}
// NOTE: Intercepted functions seem to require being static to avoid GC moving the "this" pointer of the class around, thus killing your function pointers
private static IntPtr Intercept_xrGetInstanceProcAddr(IntPtr xrGetInstanceProcAddr)
{
Original_xrGetInstanceProcAddr = Marshal.GetDelegateForFunctionPointer<XrGetInstanceProcAddr>(xrGetInstanceProcAddr);
return Marshal.GetFunctionPointerForDelegate<XrGetInstanceProcAddr>(Intercepted_xrGetInstanceProcAddr);
}
private static XrResult Intercepted_xrGetInstanceProcAddr(XrHandle instance, string requestedFunctionName, IntPtr* outgoingFunctionPointer)
{
void SetupInterception<T>(string functionNameToIntercept, ref T baseFunctionDelegate, T interceptedFunctionDelegate)
{
if (requestedFunctionName != functionNameToIntercept || baseFunctionDelegate != null)
return;
Debug.Log($"Setting up interception for: {requestedFunctionName}");
baseFunctionDelegate = Marshal.GetDelegateForFunctionPointer<T>(*outgoingFunctionPointer);
*outgoingFunctionPointer = Marshal.GetFunctionPointerForDelegate<T>(interceptedFunctionDelegate);
}
var result = Original_xrGetInstanceProcAddr(instance, requestedFunctionName, outgoingFunctionPointer);
SetupInterception("xrEndFrame", ref Original_xrEndFrame, Intercepted_xrEndFrame);
return result;
}
private static XrResult Intercepted_xrEndFrame(XrHandle xrSession, XrFrameEndInfo* frameEndInfo)
{
// Here you could read/modify frameEndInfo before forwarding it to the original function pointer
var result = Original_xrEndFrame(xrSession, &frameEndInfo);
if (result != XrResult.XR_SUCCESS)
Debug.LogError($"Failed xrEndFrame: {result}");
return result;
}
The overhead hasnt been large enough to cause issues on my end and the improved debugability and iteration time of being able to do things in C# has been pretty helpful. I didnt include the structs for the sake of brevity, but you can generally just recreate them in c# without much issue just like with the enums
2 Likes
Fantastic! I had figured out GetDelegateForFunctionPointer
but hadn’t worked out the opposite to send on after intercept, this is a really useful example.
FYI, what I’m working on is re-building the various proprietary vendor SDKs and their extensions as OpenXR features so they can all exist harmoniously in the same project and hopefully, a built apk. It will be open source so I’m hoping to get other contributors on board as there’s a lot of extensions out there, especially from Meta! Judging by the fact you shared the secret sauce, hopefully there’s a market for this
Thanks again!
1 Like
How are you structuring your frame end info? Compiler complains I’m passing a pointer to a managed struct…
I also tried a different implementation passing intptr around, but it seems to be preventing me overriding the pointer for the original functions.
If you’re just thinking of how the struct looks like it more or less maps to the spec for it:
[StructLayout(LayoutKind.Sequential)]
public unsafe struct XrFrameEndInfo
{
public XrStructureType type;
public void* next;
public Int64 displayTime;
public XrEnvironmentBlendMode environmentBlendMode;
public UInt32 layerCount;
public XrCompositionLayerBaseHeader** layers;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct XrCompositionLayerBaseHeader
{
public XrStructureType type;
public void* next;
public XrCompositionLayerFlags layerFlags;
public XrHandle space;
}
As far as using it goes, I mostly just deref it from the pointer to make a copy, make some changes then pass the address of the modified struct to the original xrEndFrame function
2 Likes