上传YomovSDK

This commit is contained in:
Sora丶kong
2026-03-03 03:15:46 +08:00
parent 9096da7e6c
commit eb97f31065
6477 changed files with 1932208 additions and 3 deletions

View File

@@ -0,0 +1,520 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using UnityEngine.Networking.PlayerConnection;
using CompressionLevel = System.IO.Compression.CompressionLevel;
[assembly: InternalsVisibleTo("Unity.XR.OpenXR.Features.RuntimeDebugger.Editor")]
namespace UnityEditor.XR.OpenXR.Features.RuntimeDebugger
{
internal class DebuggerState
{
public enum Command
{
kStartFunctionCall,
kStartStruct,
kFloat,
kString,
kInt32,
kInt64,
kUInt32,
kUInt64,
kEndStruct,
kEndFunctionCall,
kCacheNotLargeEnough,
kLUTDefineTables,
kLUTEntryUpdateStart,
kLutEntryUpdateEnd,
kLUTLookup,
};
private const byte FileVersion = 2;
private static readonly byte[] Header = new byte[] { 0xea, 0x24, 0x39, 0x5c, 0xe0, 0xac, 0x79, FileVersion };
internal static List<FunctionCall> _functionCalls = new List<FunctionCall>();
private static List<byte> saveToFile = new List<byte>(Header);
private static byte openedFileVersion = FileVersion;
internal static Dictionary<UInt32, Dictionary<UInt64, HandleDebugEvent>> xrLut = new Dictionary<UInt32, Dictionary<UInt64, HandleDebugEvent>>();
internal static List<string> lutNames = new List<string>();
internal static void Clear()
{
_functionCalls.Clear();
saveToFile.Clear();
saveToFile.AddRange(Header);
openedFileVersion = FileVersion;
}
private static Action _doneCallback;
internal static UInt32 _lastPayloadSize;
internal static UInt32 _frameCount;
internal static UInt32 _lutSize;
internal static void SetDoneCallback(Action done)
{
_doneCallback = done;
}
private static StringBuilder _sb = new StringBuilder();
internal static string ReadString(BinaryReader r)
{
_sb.Clear();
byte b;
while ((b = r.ReadByte()) != (byte)0)
{
_sb.Append((Char)b);
}
return _sb.ToString();
}
internal static void SaveToFile(string path)
{
using var stream = File.Open(path, FileMode.Create);
using var gzip = new GZipStream(stream, CompressionLevel.Optimal);
gzip.Write(saveToFile.ToArray(), 0, saveToFile.Count);
}
internal static void LoadFromFile(string path)
{
xrLut.Clear();
lutNames.Clear();
lutNames.Add("All Calls");
using var inStream = File.OpenRead(path);
var gzip = new GZipStream(inStream, CompressionMode.Decompress);
byte[] bytes;
using (var outStream = new MemoryStream())
{
gzip.CopyTo(outStream);
bytes = outStream.ToArray();
}
var headerCounter = 0;
while (headerCounter < 7)
{
if (Header[headerCounter] != bytes[headerCounter])
{
Debug.Log("Wrong file format.");
return;
}
++headerCounter;
}
openedFileVersion = bytes[7];
if (openedFileVersion > FileVersion)
{
Debug.Log($"File created with newer version ({openedFileVersion} > {FileVersion}.");
}
OnMessageEvent(new MessageEventArgs() {data = bytes.Skip(8).ToArray()});
}
internal static void OnMessageEvent(MessageEventArgs args)
{
if (args == null || args.data == null)
return;
_lastPayloadSize = (UInt32)args.data.Length;
_frameCount = 0;
saveToFile.AddRange(args.data);
try
{
using (MemoryStream ms = new MemoryStream(args.data))
{
using (BinaryReader r = new BinaryReader(ms, Encoding.UTF8))
{
while (r.BaseStream.Position != r.BaseStream.Length)
{
var command = (Command)r.ReadUInt32();
switch (command)
{
case Command.kStartFunctionCall:
var thread = ReadString(r);
var funcName = ReadString(r);
var funcCall = new FunctionCall(thread, funcName);
_functionCalls.Add(funcCall);
funcCall.Parse(r);
if (funcName == "xrBeginFrame")
{
++_frameCount;
}
break;
case Command.kLUTDefineTables:
lutNames.Clear();
lutNames.Add("All Calls");
var numLUTs = r.ReadUInt32();
for (UInt32 lutIndex = 0; lutIndex < numLUTs; ++lutIndex)
{
xrLut[lutIndex] = new Dictionary<UInt64, HandleDebugEvent>();
lutNames.Add(ReadString(r));
}
break;
case Command.kLUTEntryUpdateStart:
var lutKey = r.ReadUInt32();
var handle = r.ReadUInt64();
var handleName = ReadString(r);
// struct command, skip it
r.ReadUInt32();
ReadString(r);
ReadString(r);
var evt = new HandleDebugEvent(handleName, handle);
evt.Parse(r);
xrLut[lutKey][handle] = evt;
break;
case Command.kLutEntryUpdateEnd:
break;
case Command.kCacheNotLargeEnough:
funcCall = new FunctionCall(r.ReadUInt32().ToString(), ReadString(r));
_functionCalls.Add(funcCall);
var result = ReadString(r);
funcCall.displayName += " = " + result + " (cache not large enough)";
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
}
}
catch (Exception e)
{
Debug.LogError(e);
}
_doneCallback?.Invoke();
}
internal class DebugEvent : TreeViewItem
{
private static int idCounter = 1;
private List<DebugEvent> childrenEvents = new List<DebugEvent>();
protected string fieldname;
protected DebugEvent(string fieldname, string display)
: base(idCounter++, 0, display)
{
this.fieldname = fieldname;
}
public virtual DebugEvent Clone()
{
return null;
}
public virtual string GetValue()
{
return "";
}
public override string ToString()
{
string var = displayName;
foreach (var child in childrenEvents)
{
var += "\n\t" + child.ToString().Replace("\n", "\n\t");
}
return var;
}
public void Parse(BinaryReader r)
{
DebugEvent parsedChild = null;
bool endEvent = false;
do
{
if (parsedChild != null)
{
AddChildEvent(parsedChild);
parsedChild.Parse(r);
parsedChild = null;
}
var command = (Command)r.ReadUInt32();
switch (command)
{
case Command.kStartStruct:
parsedChild = new StructDebugEvent(ReadString(r), ReadString(r));
break;
case Command.kLUTLookup:
var lutKey = r.ReadUInt32();
var fieldName = ReadString(r);
var handle = r.ReadUInt64();
if (xrLut[lutKey].TryGetValue(handle, out var evt))
{
AddChildEvent(evt.Clone(fieldName));
}
else
{
AddChildEvent(new UInt64DebugEvent(fieldName, handle));
}
break;
case Command.kFloat:
AddChildEvent(new FloatDebugEvent(ReadString(r), r.ReadSingle()));
break;
case Command.kString:
AddChildEvent(new StringDebugEvent(ReadString(r), ReadString(r)));
break;
case Command.kInt32:
AddChildEvent(new Int32DebugEvent(ReadString(r), r.ReadInt32()));
break;
case Command.kInt64:
AddChildEvent(new Int64DebugEvent(ReadString(r), r.ReadInt64()));
break;
case Command.kUInt32:
AddChildEvent(new UInt32DebugEvent(ReadString(r), r.ReadUInt32()));
break;
case Command.kUInt64:
AddChildEvent(new UInt64DebugEvent(ReadString(r), r.ReadUInt64()));
break;
case Command.kEndStruct:
endEvent = true;
break;
case Command.kEndFunctionCall:
var result = ReadString(r);
displayName += " = " + result;
endEvent = true;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
while (!endEvent && r.BaseStream.Position != r.BaseStream.Length);
}
// public IEnumerable<DebugEvent> GetChildren()
// {
// return childrenEvents;
// }
public DebugEvent GetFirstChild()
{
return childrenEvents[0];
}
private DebugEvent AddChildEvent(DebugEvent evt)
{
childrenEvents.Add(evt);
AddChild(evt);
return this;
}
protected DebugEvent AddClonedChildren(DebugEvent clone)
{
foreach (var evt in childrenEvents)
{
clone.AddChildEvent(evt.Clone());
}
return clone;
}
}
internal class HandleDebugEvent : DebugEvent
{
private string niceDisplay;
private UInt64 handle;
public HandleDebugEvent(string niceDisplay, UInt64 handle)
: base("", $"{niceDisplay} = {handle}")
{
this.niceDisplay = niceDisplay;
this.handle = handle;
}
public override string GetValue()
{
return niceDisplay;
}
public override DebugEvent Clone()
{
var evt = new HandleDebugEvent(niceDisplay, handle);
evt.displayName = displayName;
return AddClonedChildren(evt);
}
public DebugEvent Clone(string fieldName)
{
var ret = Clone();
ret.displayName = $"{fieldName} = {niceDisplay} ({handle})";
return ret;
}
}
internal class FunctionCall : DebugEvent
{
public string threadId { get; }
public string returnVal { get; set; }
public FunctionCall(string threadId, string displayName)
: base("", displayName)
{
this.threadId = threadId;
}
public override DebugEvent Clone()
{
return AddClonedChildren(new FunctionCall(threadId, displayName));
}
}
internal class StructDebugEvent : DebugEvent
{
public string structname { get; }
public StructDebugEvent(string fieldname, string structname)
: base(fieldname, $"{fieldname} = {structname}")
{
this.structname = structname;
}
public override string GetValue()
{
return structname;
}
public override DebugEvent Clone()
{
return AddClonedChildren(new StructDebugEvent(fieldname, structname));
}
}
internal class FloatDebugEvent : DebugEvent
{
public float value { get; }
public FloatDebugEvent(string displayName, float val)
: base(displayName, displayName + " = " + val)
{
this.value = val;
}
public override string GetValue()
{
return $"{value}";
}
public override DebugEvent Clone()
{
return new FloatDebugEvent(fieldname, value);
}
}
internal class StringDebugEvent : DebugEvent
{
public string value { get; }
public StringDebugEvent(string displayName, string val)
: base(displayName, displayName + " = " + val)
{
this.value = val;
}
public override string GetValue()
{
return value;
}
public override DebugEvent Clone()
{
return new StringDebugEvent(fieldname, value);
}
}
internal class Int32DebugEvent : DebugEvent
{
public Int32 value { get; }
public Int32DebugEvent(string displayName, Int32 val)
: base(displayName, displayName + " = " + val)
{
this.value = val;
}
public override string GetValue()
{
return $"{value}";
}
public override DebugEvent Clone()
{
return new Int32DebugEvent(fieldname, value);
}
}
internal class Int64DebugEvent : DebugEvent
{
public Int64 value { get; }
public Int64DebugEvent(string displayName, Int64 val)
: base(displayName, displayName + " = " + val)
{
this.value = val;
}
public override string GetValue()
{
return $"{value}";
}
public override DebugEvent Clone()
{
return new Int64DebugEvent(fieldname, value);
}
}
internal class UInt32DebugEvent : DebugEvent
{
public UInt32 value { get; }
public UInt32DebugEvent(string displayName, UInt32 val)
: base(displayName, displayName + " = " + val)
{
this.value = val;
}
public override string GetValue()
{
return $"{value}";
}
public override DebugEvent Clone()
{
return new UInt32DebugEvent(fieldname, value);
}
}
internal class UInt64DebugEvent : DebugEvent
{
public UInt64 value { get; }
public UInt64DebugEvent(string displayName, UInt64 val)
: base(displayName, displayName + " = " + val)
{
this.value = val;
}
public override string GetValue()
{
return $"{value}";
}
public override DebugEvent Clone()
{
return new UInt64DebugEvent(fieldname, value);
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 15350e969b159e747a2d967827002b40
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 246e578028c2be145beff6c79a4f193b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,34 @@
using UnityEngine;
using UnityEngine.XR.OpenXR.Features.RuntimeDebugger;
namespace UnityEditor.XR.OpenXR.Features.RuntimeDebugger
{
[CustomEditor(typeof(RuntimeDebuggerOpenXRFeature))]
internal class RuntimeDebuggerOpenXRFeatureEditor : Editor
{
private SerializedProperty cacheSize;
private SerializedProperty perThreadCacheSize;
void OnEnable()
{
cacheSize = serializedObject.FindProperty("cacheSize");
perThreadCacheSize = serializedObject.FindProperty("perThreadCacheSize");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(cacheSize);
EditorGUILayout.PropertyField(perThreadCacheSize);
if (GUILayout.Button("Open Debugger Window"))
{
RuntimeDebuggerWindow.Init();
}
serializedObject.ApplyModifiedProperties();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ac3a007820947a349b40fa3f0dcfaae8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,301 @@
using System;
using System.Collections.Generic;
using System.Text;
using UnityEditor.IMGUI.Controls;
using UnityEditor.Networking.PlayerConnection;
using UnityEngine;
using UnityEngine.Networking.PlayerConnection;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features.RuntimeDebugger;
namespace UnityEditor.XR.OpenXR.Features.RuntimeDebugger
{
internal class DebuggerTreeView : TreeView
{
private int _mode;
public new string searchString { get; set; }
private bool deep = false;
public DebuggerTreeView(TreeViewState state, int mode, string search = "")
: base(state)
{
_mode = mode;
searchString = search;
Reload();
}
public void ReloadDeepSearch()
{
deep = true;
Reload();
deep = false;
}
protected override TreeViewItem BuildRoot()
{
var root = new TreeViewItem(0, -1, "Root");
if (_mode == 0)
{
foreach (var t in DebuggerState._functionCalls)
{
if (string.IsNullOrEmpty(searchString) || (deep ? t.ToString() : t.displayName).IndexOf(searchString, StringComparison.OrdinalIgnoreCase) >= 0)
root.AddChild(t);
}
}
else
{
if (DebuggerState.xrLut.TryGetValue((UInt32)_mode - 1, out var lut))
{
foreach (var t in lut.Values)
{
if (string.IsNullOrEmpty(searchString) || (deep ? t.ToString() : t.displayName).IndexOf(searchString, StringComparison.OrdinalIgnoreCase) >= 0)
root.AddChild(t);
}
}
}
if (root.hasChildren)
SetupDepthsFromParentsAndChildren(root);
else
root.AddChild(new TreeViewItem(0));
return root;
}
protected override void KeyEvent()
{
if (Event.current.commandName == "Copy")
{
StringBuilder copy = new StringBuilder("");
foreach (var id in state.selectedIDs)
{
copy.Append(FindItem(id, rootItem).ToString());
copy.Append("\n");
}
EditorGUIUtility.systemCopyBuffer = copy.ToString();
}
base.KeyEvent();
}
}
internal class RuntimeDebuggerWindow : EditorWindow
{
private static class Styles
{
public static GUIStyle s_Wrap;
}
private static void InitStyles()
{
if (Styles.s_Wrap != null)
return;
Styles.s_Wrap = new GUIStyle(EditorStyles.label)
{
wordWrap = true,
alignment = TextAnchor.MiddleLeft,
padding = new RectOffset(0, 5, 1, 1)
};
}
[MenuItem("Window/Analysis/OpenXR Runtime Debugger")]
[MenuItem("Window/XR/OpenXR/Runtime Debugger")]
internal static void Init()
{
RuntimeDebuggerWindow w = EditorWindow.GetWindow<RuntimeDebuggerWindow>() as RuntimeDebuggerWindow;
w.titleContent = new GUIContent("OpenXR Runtime Debugger");
w.Show();
}
private IConnectionState state;
void OnEnable()
{
state = PlayerConnectionGUIUtility.GetConnectionState(this);
EditorConnection.instance.Initialize();
EditorConnection.instance.Register(RuntimeDebuggerOpenXRFeature.kPlayerToEditorSendDebuggerOutput, DebuggerState.OnMessageEvent);
}
void OnDisable()
{
EditorConnection.instance.Unregister(RuntimeDebuggerOpenXRFeature.kPlayerToEditorSendDebuggerOutput, DebuggerState.OnMessageEvent);
state.Dispose();
}
private Vector2 scrollpos = new Vector2();
private List<TreeViewState> treeViewState = new List<TreeViewState>();
private DebuggerTreeView treeView;
private SearchField searchField = null;
private string searchString = "";
private int viewMode = 0;
private string _lastRefreshStats;
void Clear()
{
DebuggerState.Clear();
treeView = null;
searchField = new SearchField();
treeViewState.Clear();
_lastRefreshStats = "";
scrollpos = Vector2.zero;
}
void OnGUI()
{
InitStyles();
if (searchField == null)
searchField = new SearchField();
var debuggerFeatureInfo = FeatureHelpers.GetFeatureWithIdForActiveBuildTarget("com.unity.openxr.features.runtimedebugger");
if (!debuggerFeatureInfo.enabled)
{
EditorGUILayout.BeginVertical();
EditorGUILayout.Space();
EditorGUILayout.LabelField("OpenXR Runtime Debugger must be enabled for this build target.", Styles.s_Wrap);
EditorGUILayout.Space();
if (GUILayout.Button("Enable Runtime Debugger"))
{
debuggerFeatureInfo.enabled = true;
}
EditorGUILayout.EndVertical();
return;
}
PlayerConnectionGUILayout.ConnectionTargetSelectionDropdown(state);
GUILayout.BeginHorizontal();
if (GUILayout.Button("Refresh"))
{
DebuggerState.SetDoneCallback(() =>
{
if (treeViewState.Count != DebuggerState.lutNames.Count)
{
treeViewState.Clear();
for (int i = 0; i < DebuggerState.lutNames.Count; ++i)
{
treeViewState.Add(new TreeViewState());
}
}
treeView = new DebuggerTreeView(treeViewState[viewMode], viewMode, searchString);
searchField = new SearchField();
var debugger = OpenXRSettings.ActiveBuildTargetInstance.GetFeature<RuntimeDebuggerOpenXRFeature>();
if (debugger != null)
_lastRefreshStats = $"Last payload size: {DebuggerState._lastPayloadSize} ({((100.0f * DebuggerState._lastPayloadSize / debugger.cacheSize)):F2}% cache full) Number of Frames: {DebuggerState._frameCount}";
else
_lastRefreshStats = $"Last payload size: {DebuggerState._lastPayloadSize} Number of Frames: {DebuggerState._frameCount}";
});
_lastRefreshStats = "Refreshing ...";
if (EditorApplication.isPlaying)
{
var debugger = OpenXRSettings.Instance.GetFeature<RuntimeDebuggerOpenXRFeature>();
if (debugger.enabled)
{
debugger.RecvMsg(new MessageEventArgs());
}
}
else
{
EditorConnection.instance.Send(RuntimeDebuggerOpenXRFeature.kEditorToPlayerRequestDebuggerOutput, new byte[] { byte.MinValue });
}
}
if (GUILayout.Button("Clear"))
{
Clear();
}
if (GUILayout.Button(EditorGUIUtility.IconContent("d_SaveAs")))
{
string path = EditorUtility.SaveFilePanel("Save OpenXR Dump", "", state.connectionName, "openxrdump");
if (path.Length != 0)
{
DebuggerState.SaveToFile(path);
}
}
if (GUILayout.Button(EditorGUIUtility.IconContent("d_FolderOpened Icon")))
{
string path = EditorUtility.OpenFilePanelWithFilters("Load OpenXR Dump", "", new[] { "OpenXR Dump", "openxrdump" });
if (path.Length != 0)
{
Clear();
DebuggerState.SetDoneCallback(() =>
{
if (treeViewState.Count != DebuggerState.lutNames.Count)
{
treeViewState.Clear();
for (int i = 0; i < DebuggerState.lutNames.Count; ++i)
{
treeViewState.Add(new TreeViewState());
}
}
treeView = new DebuggerTreeView(treeViewState[viewMode], viewMode, searchString);
_lastRefreshStats = $"Last payload size: {DebuggerState._lastPayloadSize} Number of Frames: {DebuggerState._frameCount}";
});
DebuggerState.LoadFromFile(path);
}
}
GUILayout.EndHorizontal();
GUILayout.Label($"Connections: {EditorConnection.instance.ConnectedPlayers.Count}");
GUILayout.Label(_lastRefreshStats);
if (treeView != null)
{
GUILayout.BeginHorizontal();
var newSearchString = searchField.OnGUI(treeView.searchString);
if (newSearchString != treeView.searchString)
{
treeView.searchString = newSearchString;
treeView.Reload();
}
if (searchField.HasFocus())
{
if (Event.current.keyCode == KeyCode.Return)
{
treeView.ReloadDeepSearch();
}
}
if (GUILayout.Button("Deep Search"))
{
treeView.ReloadDeepSearch();
}
GUILayout.EndHorizontal();
}
int newViewMode = 0;
if (DebuggerState.lutNames.Count > 0)
{
newViewMode = EditorGUILayout.Popup(viewMode, DebuggerState.lutNames.ToArray());
}
if (newViewMode != viewMode)
{
viewMode = newViewMode;
treeView = new DebuggerTreeView(treeViewState[viewMode], viewMode, searchString);
scrollpos = Vector2.zero;
}
var treeViewRect = EditorGUILayout.BeginVertical(GUILayout.ExpandHeight(true));
if (treeView != null)
{
treeView.OnGUI(treeViewRect);
}
EditorGUILayout.EndVertical();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ee07143892e1a224eb2d596269f30752
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
{
"name": "Unity.XR.OpenXR.Features.RuntimeDebugger.Editor",
"rootNamespace": "",
"references": [
"GUID:784a7033de40af04db4f8c4de440f481",
"GUID:4847341ff46394e83bb78fbd0652937e",
"GUID:96aa6ba065960476598f8f643e7252b6"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: dd9e9bbf90e916b4b890adf062c6c2a4
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,15 @@
#pragma once
#if defined(__CYGWIN32__)
#define UNITY_INTERFACE_API __stdcall
#define UNITY_INTERFACE_EXPORT __declspec(dllexport)
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(_WIN64) || defined(WINAPI_FAMILY)
#define UNITY_INTERFACE_API __stdcall
#define UNITY_INTERFACE_EXPORT __declspec(dllexport)
#elif defined(__MACH__) || defined(__ANDROID__) || defined(__linux__) || defined(LUMIN)
#define UNITY_INTERFACE_API
#define UNITY_INTERFACE_EXPORT __attribute__((visibility("default")))
#else
#define UNITY_INTERFACE_API
#define UNITY_INTERFACE_EXPORT
#endif

View File

@@ -0,0 +1,2 @@
DisableFormat: true
SortIncludes: false

View File

@@ -0,0 +1,485 @@
diff --git a/specification/scripts/creflectiongenerator.py b/specification/scripts/creflectiongenerator.py
index a1beca84a..fa08160ee 100644
--- a/specification/scripts/creflectiongenerator.py
+++ b/specification/scripts/creflectiongenerator.py
@@ -35,11 +35,13 @@ class CommandData:
class StructData:
"""Represents a OpenXR struct type"""
- def __init__(self, typeName, structTypeName, members, protect):
+ def __init__(self, typeName, structTypeName, members, arrays, protect, parent):
self.typeName = typeName
self.members = members
+ self.arrays = arrays
self.structTypeName = structTypeName
self.protect = protect
+ self.parent = parent
@property
def protect_value(self) -> bool:
@@ -64,12 +66,42 @@ class BitmaskData:
class EnumData:
"""Represents a OpenXR group enum type"""
- def __init__(self, typeName, typeNamePrefix, typeNameSuffix, enumTuples):
+ def __init__(self, typeName, typeNamePrefix, typeNameSuffix, enumTuples, protect):
self.typeName = typeName
self.typeNamePrefix = typeNamePrefix
self.typeNameSuffix = typeNameSuffix
self.enumTuples = enumTuples
+ self.protect = protect
+
+ @property
+ def protect_value(self):
+ return self.protect is not None
+
+ @property
+ def protect_string(self):
+ if self.protect:
+ return " && ".join("defined({})".format(x) for x in self.protect)
+
+class CCmd:
+ """Represents an OpenXR function"""
+
+ def __init__(self, cmdName, returnType, members, members_non_array, arrays, sig, protect):
+ self.cmdName = cmdName
+ self.returnType = returnType
+ self.members = members
+ self.members_non_array = members_non_array
+ self.arrays = arrays
+ self.sig = sig
+ self.protect = protect
+
+ @property
+ def protect_value(self):
+ return self.protect is not None
+ @property
+ def protect_string(self):
+ if self.protect:
+ return " && ".join("defined({})".format(x) for x in self.protect)
class CReflectionOutputGenerator(OutputGenerator):
"""Generate specified API interfaces in a specific style, such as a C header"""
@@ -81,6 +113,8 @@ class CReflectionOutputGenerator(OutputGenerator):
self.commands = []
self.enums = []
self.bitmasks = []
+ self.cmds = []
+ self.handles = []
self.protects = set()
self.template: Optional[JinjaTemplate] = None
self.parents = {}
@@ -97,14 +131,42 @@ class CReflectionOutputGenerator(OutputGenerator):
if x.protect == protect and x.structTypeName is not None]
return ret
+ def getCmdsForProtect(self, protect=None):
+ return [x for x in self.cmds
+ if x.protect == protect]
+
+ def getEnumsForProtect(self, protect=None):
+ return [x for x in self.enums
+ if x.protect == protect]
+
+ def getCmdsForProtect(self, protect=None):
+ return [x for x in self.cmds
+ if x.protect == protect]
+
+ def getEnumsForProtect(self, protect=None):
+ return [x for x in self.enums
+ if x.protect == protect]
+
def endFile(self):
assert self.template
assert self.registry
file_data = ''
+ # UNITY: specialization filter
+ self.cmds = [x for x in self.cmds if x.cmdName not in ('xrGetInstanceProcAddr')]
+ self.structs = [x for x in self.structs if x.typeName not in ('XrBaseInStructure', 'XrBaseOutStructure', 'XrEventDataBuffer', 'XrDebugUtilsMessengerCreateInfoEXT', 'XrSpatialGraphNodeSpaceCreateInfoMSFT', 'XrInteractionProfileAnalogThresholdVALVE', 'XrWorldMeshBufferML')]
+
unprotected_structs = self._get_structs_for_protect()
protected_structs = [(x, self._get_structs_for_protect(protect=x))
for x in sorted(self.protects)]
+ base_structs = list(set([x.parent for x in self.structs if x.parent is not None]))
+ basic_structs = [x for x in self.structs if x.structTypeName is None and x.typeName not in base_structs]
+
+ unprotected_cmds = self.getCmdsForProtect()
+ protected_cmds = [(x, self.getCmdsForProtect(x)) for x in sorted(self.protects)]
+
+ unprotected_enums = self.getEnumsForProtect()
+ protected_enums = [(x, self.getEnumsForProtect(x)) for x in sorted(self.protects)]
# drop empty collections
protected_structs = [(x, y) for x, y in protected_structs if y]
@@ -138,7 +200,15 @@ class CReflectionOutputGenerator(OutputGenerator):
unprotectedStructs=unprotected_structs,
protectedStructs=protected_structs,
structs=self.structs,
+ basic_structs=basic_structs,
+ base_structs=base_structs,
+ unprotectedCmds=unprotected_cmds,
+ protectedCmds=protected_cmds,
+ cmds=self.cmds,
+ unprotectedEnums=unprotected_enums,
+ protectedEnums=protected_enums,
enums=self.enums,
+ handles=self.handles,
bitmasks=self.bitmasks,
extensions=extensions,
polymorphic_struct_families=polymorphic_struct_families,
@@ -159,6 +229,8 @@ class CReflectionOutputGenerator(OutputGenerator):
# If the type is a struct type, generate it using the
# special-purpose generator.
self.genStruct(typeinfo, name, alias)
+ if category == 'handle':
+ self.handles.append(name)
parent_struct = typeElem.get('parentstruct')
if parent_struct is not None:
@@ -174,12 +246,75 @@ class CReflectionOutputGenerator(OutputGenerator):
self.commands.append(CommandData(name, self.featureName))
+ def genCmd(self, cmd, cmdinfo, alias):
+ OutputGenerator.genCmd(self, cmd, cmdinfo, alias)
+
+ if alias:
+ return
+
+ #print(f'{dump(cmd.elem)}')
+
+ ret = cmd.elem.find('proto').find('type').text
+ #print(ret)
+
+ members = []
+ members_non_array = []
+ sig = ''
+
+
+ arrays = {}
+ for m in cmd.elem.findall('./param[@len]'):
+ lenName = m.get('len')
+ if lenName != 'null-terminated':
+ if m.find('type').text == 'char' and 'null-terminated' not in lenName:
+ continue
+ lenName = lenName.replace(',null-terminated','')
+ print(f"{m.find('name').text} len={lenName}")
+ arrays[m.find('name').text] = lenName
+
+ for m in cmd.elem.iterfind('param'):
+ sig += ', ' + self.makeCParamDecl(m, 0)
+ member = m.find('name').text
+ members.append(m.find('name').text)
+ if member not in arrays:
+ members_non_array.append(m.find('name').text)
+
+ sig = sig[2:]
+
+ #import pprint
+ #pprint.pprint(members)
+
+ protect = set()
+ if self.featureExtraProtect:
+ protect.update(self.featureExtraProtect.split(','))
+ localProtect = cmd.elem.get('protect')
+ if localProtect:
+ protect.update(localProtect.split(','))
+
+ if protect:
+ protect = tuple(protect)
+ else:
+ protect = None
+
+ self.cmds.append(CCmd(cmd.elem.attrib['name'], ret, members, members_non_array, arrays, sig, protect))
+
+
def genStruct(self, typeinfo, typeName, alias):
OutputGenerator.genStruct(self, typeinfo, typeName, alias)
if alias:
return
+ arrays = {}
+ for m in typeinfo.elem.findall('./member[@len]'):
+ lenName = m.get('len')
+ if lenName != 'null-terminated':
+ if m.find('type').text == 'char' and 'null-terminated' not in lenName:
+ continue
+ lenName = lenName.replace(',null-terminated','')
+ print(f"{m.find('name').text} len={lenName}")
+ arrays[m.find('name').text] = lenName
+
structTypeEnum = None
members = []
for member in typeinfo.getMembers():
@@ -188,7 +323,8 @@ class CReflectionOutputGenerator(OutputGenerator):
if self.conventions.is_structure_type_member(memberType, memberName):
structTypeEnum = member.get("values")
- members.append(memberName)
+ if memberName not in arrays:
+ members.append(memberName)
protect = set()
if self.featureExtraProtect:
@@ -202,7 +338,9 @@ class CReflectionOutputGenerator(OutputGenerator):
else:
protect = None
- self.structs.append(StructData(typeName, structTypeEnum, members, protect))
+ parent = typeinfo.elem.get('parentstruct')
+
+ self.structs.append(StructData(typeName, structTypeEnum, members, arrays, protect, parent))
if protect:
self.protects.add(protect)
@@ -242,4 +380,16 @@ class CReflectionOutputGenerator(OutputGenerator):
continue
enumTuples.append((getElemName(elem), strVal))
- self.enums.append(EnumData(groupName, expandPrefix, expandSuffix, enumTuples))
+ protect = set()
+ if self.featureExtraProtect:
+ protect.update(self.featureExtraProtect.split(','))
+ localProtect = groupinfo.elem.get('protect')
+ if localProtect:
+ protect.update(localProtect.split(','))
+
+ if protect:
+ protect = tuple(protect)
+ else:
+ protect = None
+
+ self.enums.append(EnumData(groupName, expandPrefix, expandSuffix, enumTuples, protect))
diff --git a/specification/scripts/template_openxr_reflection.h b/specification/scripts/template_openxr_reflection.h
index 11ec318d1..9ddbbc8e1 100644
--- a/specification/scripts/template_openxr_reflection.h
+++ b/specification/scripts/template_openxr_reflection.h
@@ -12,7 +12,7 @@
**
*/
-#include "openxr.h"
+// #include "openxr.h"
/*
This file contains expansion macros (X Macros) for OpenXR enumerations and structures.
@@ -30,6 +30,13 @@ Example of how to use expansion macros to make an enum-to-string function:
XR_ENUM_STR(XrResult);
*/
+#define XR_LIST_HANDLES(_) \
+//# for handle in handles
+ _(/*{handle}*/) \
+//# endfor
+
+//## Preceding line intentionally left blank to absorb the trailing backslash
+
//# for enum in enums
#define XR_LIST_ENUM_/*{enum.typeName}*/(_) \
@@ -40,6 +47,39 @@ XR_ENUM_STR(XrResult);
//# endfor
+//## Used when making structure type macros
+/*% macro makeEnumTypes(typedEnums) -%*/
+//# for enum in typedEnums
+ _(/*{enum.typeName}*/) \
+//# endfor
+
+//## Preceding line intentionally left blank to absorb the trailing backslash
+/*% endmacro %*/
+
+#define XR_LIST_ENUM_TYPES_CORE(_) \
+/*{ makeEnumTypes(unprotectedEnums) }*/
+
+
+//# for protect, enumTypes in protectedEnums if enumTypes|length > 0
+
+/*{ protect_begin(enumTypes[0]) }*/
+#define XR_LIST_ENUM_TYPES_/*{protect | join("_")}*/(_) \
+/*{ makeEnumTypes(enumTypes) }*/
+#else
+#define XR_LIST_ENUM_TYPES_/*{protect | join("_")}*/(_)
+#endif
+
+//# endfor
+
+
+#define XR_LIST_ENUM_TYPES(_) \
+ XR_LIST_ENUM_TYPES_CORE(_) \
+//# for protect, enumTypes in protectedEnums if enumTypes|length > 0
+ XR_LIST_ENUM_TYPES_/*{protect | join("_")}*/(_) \
+//# endfor
+
+//## Preceding line intentionally left blank to absorb the trailing backslash
+
//# for bitmask in bitmasks
#define XR_LIST_BITS_/*{bitmask.typeName}*/(_)/*{" \\" if bitmask.maskTuples}*/
@@ -61,6 +101,16 @@ XR_ENUM_STR(XrResult);
//## Preceding line intentionally left blank to absorb the trailing backslash
//# endfor
+//# for struct in structs
+
+#define XR_LIST_STRUCT_ARRAYS_/*{struct.typeName}*/(_) \
+//# for memberName, lenName in struct.arrays.items()
+ _(/*{memberName}*/, /*{lenName}*/) \
+//# endfor
+
+//## Preceding line intentionally left blank to absorb the trailing backslash
+//# endfor
+
//## Used when making structure type macros
/*% macro makeStructTypes(typedStructs, funcName="_") -%*/
//# for struct in typedStructs
@@ -98,6 +148,62 @@ XR_ENUM_STR(XrResult);
//# endfor
+
+#define XR_LIST_BASE_STRUCTS(_) \
+//# for structTypeName in base_structs
+ _(/*{structTypeName}*/) \
+//# endfor
+
+//## Preceding line intentionally left blank to absorb the trailing backslash
+
+//# for structName in base_structs
+
+#define XR_LIST_BASE_STRUCT_TYPES_CORE_/*{structName}*/(_) \
+//# for struct in unprotectedStructs if struct.parent == structName
+ _(/*{struct.typeName}*/, /*{struct.structTypeName}*/) \
+//# endfor
+
+//## Preceding line intentionally left blank to absorb the trailing backslash
+
+//# for protect, structTypes in protectedStructs
+/*{ protect_begin(structTypes[0]) }*/
+#define XR_LIST_BASE_STRUCT_TYPES_/*{structName}*/_/*{protect | join("_")}*/(_) \
+//# for struct in structTypes if struct.parent == structName
+ _(/*{struct.typeName}*/, /*{struct.structTypeName}*/) \
+//# endfor
+
+//## Preceding line intentionally left blank to absorb the trailing backslash
+
+#else
+#define XR_LIST_BASE_STRUCT_TYPES_/*{structName}*/_/*{protect | join("_")}*/(_)
+#endif
+
+//# endfor
+
+//## Preceding line intentionally left blank to absorb the trailing backslash
+
+#define XR_LIST_BASE_STRUCT_TYPES_/*{structName}*/(_) \
+ XR_LIST_BASE_STRUCT_TYPES_CORE_/*{structName}*/(_) \
+//# for protect, structTypes in protectedStructs
+ XR_LIST_BASE_STRUCT_TYPES_/*{structName}*/_/*{protect | join("_")}*/(_) \
+//# endfor
+
+//## Preceding line intentionally left blank to absorb the trailing backslash
+
+//# endfor
+
+//## Preceding line intentionally left blank to absorb the trailing backslash
+
+
+
+#define XR_LIST_BASIC_STRUCTS(_) \
+//# for s in basic_structs
+ _(/*{s.typeName}*/) \
+//# endfor
+
+//## Preceding line intentionally left blank to absorb the trailing backslash
+
+
//## Preceding line intentionally left blank to absorb the trailing backslash
/// Calls your macro with the name and extension number of all known
@@ -126,4 +232,82 @@ XR_ENUM_STR(XrResult);
//## Preceding line intentionally left blank to absorb the trailing backslash
+
+//## Used when making cmd type macros
+/*% macro makeCmdTypes(cmds) -%*/
+//# for cmd in cmds
+ _(/*{cmd.cmdName}*/, /*{cmd.sig}*/) \
+//# endfor
+
+//## Preceding line intentionally left blank to absorb the trailing backslash
+/*% endmacro %*/
+
+#define XR_LIST_FUNCS_CORE(_) \
+/*{ makeCmdTypes(unprotectedCmds) }*/
+
+
+//# for protect, cmdTypes in protectedCmds if cmdTypes|length > 0
+
+/*{ protect_begin(cmdTypes[0]) }*/
+#define XR_LIST_FUNCS_/*{protect | join("_")}*/(_) \
+/*{ makeCmdTypes(cmdTypes) }*/
+#else
+#define XR_LIST_FUNCS_/*{protect | join("_")}*/(_)
+#endif
+
+//# endfor
+
+
+#define XR_LIST_FUNCS(_) \
+ XR_LIST_FUNCS_CORE(_) \
+//# for protect, cmdTypes in protectedCmds if cmdTypes|length > 0
+ XR_LIST_FUNCS_/*{protect | join("_")}*/(_) \
+//# endfor
+
+
+//## Preceding line intentionally left blank to absorb the trailing backslash
+
+//# for cmd in cmds
+
+#define XR_LIST_FUNC_/*{cmd.cmdName}*/(_) \
+//# for member in cmd.members_non_array
+ _(/*{member}*/) \
+//# endfor
+
+//## Preceding line intentionally left blank to absorb the trailing backslash
+
+//# endfor
+
+//# for cmd in cmds
+
+#define XR_LIST_FUNC_ARRAYS_/*{cmd.cmdName}*/(_) \
+//# for member, len_member in cmd.arrays.items()
+ _(/*{member}*/, /*{len_member}*/) \
+//# endfor
+
+//## Preceding line intentionally left blank to absorb the trailing backslash
+
+//# endfor
+
+//# for cmd in cmds
+
+#define XR_LIST_FUNC_PARAM_NAMES_/*{cmd.cmdName}*/(_) \
+ _(/*{', '.join(cmd.members)}*/) \
+
+//## Preceding line intentionally left blank to absorb the trailing backslash
+
+//# endfor
+
+//# for cmd in cmds
+#define XR_BEFORE_/*{cmd.cmdName}*/(funcName)
+//#endfor
+
+//## Preceding line intentionally left blank
+
+//# for cmd in cmds
+#define XR_AFTER_/*{cmd.cmdName}*/(funcName)
+//#endfor
+
+//## Preceding line intentionally left blank
+
#endif

View File

@@ -0,0 +1,41 @@
#pragma once
#ifdef XR_USE_PLATFORM_ANDROID
#include <jni.h>
#include <sys/system_properties.h>
#endif
#ifdef XR_USE_GRAPHICS_API_D3D11
#include <d3d11_4.h>
#endif
#ifdef XR_USE_GRAPHICS_API_D3D12
#include <d3d12.h>
#endif
#ifdef XR_USE_GRAPHICS_API_OPENGL
#if defined(XR_USE_PLATFORM_XLIB) || defined(XR_USE_PLATFORM_XCB)
#include <GL/glx.h>
#endif
#ifdef XR_USE_PLATFORM_XCB
#include <xcb/glx.h>
#endif
#ifdef XR_USE_PLATFORM_WIN32
#include <GL/gl.h>
#include <wingdi.h> // For HGLRC
#endif
#endif
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
#include <EGL/egl.h>
#endif
#ifdef XR_USE_GRAPHICS_API_VULKAN
#ifdef XR_USE_PLATFORM_WIN32
#define VK_USE_PLATFORM_WIN32_KHR
#endif
#if defined(XR_USE_PLATFORM_ANDROID) && !defined(XR_USE_UNITY)
#define VK_USE_PLATFORM_ANDROID_KHR
#endif
#include <vulkan/vulkan.h>
#endif

View File

@@ -0,0 +1,585 @@
#include <cassert>
#include <deque>
#include <sstream>
#include <string>
// Block-based dynamic allocator via ring-buffer.
// Ring buffer that stores "blocks" which dynamically grow and wrap.
// If a section grows large enough that it overlaps another section, the other section is forgotten.
// 8 bit alignment, bring your own synchronization.
// Must call Create first, and Destroy when done. Must create a section before writing.
// This probably isn't perfect.. some tests are at the bottom and it seems to be stable for the usecase.
struct RingBuf
{
enum OverflowMode
{
kOverflowModeWrap,
kOverflowModeTruncate,
kOverflowModeGrowDouble,
};
uint8_t* data;
uint32_t cacheSize;
OverflowMode overflowMode;
// must be pointer because of thread_local compiler bug
std::deque<uint32_t>* offsets;
void Create(uint32_t csize, OverflowMode overflowMode)
{
this->overflowMode = overflowMode;
if (data == nullptr)
{
data = (uint8_t*)malloc(csize);
cacheSize = csize;
offsets = new std::deque<uint32_t>();
}
Reset();
}
void SetOverflowMode(OverflowMode overflowMode)
{
this->overflowMode = overflowMode;
}
void Reset()
{
if (offsets != nullptr)
{
offsets->clear();
offsets->push_back(0);
}
}
void Destroy()
{
if (data != nullptr)
{
free(data);
data = nullptr;
delete (offsets);
cacheSize = 0;
}
}
void CreateNewBlock()
{
if (offsets != nullptr)
offsets->push_back(offsets->back());
}
void DropLastBlock()
{
if (offsets != nullptr)
offsets->pop_back();
}
uint8_t* GetForWrite(uint32_t size)
{
uint8_t* ret{nullptr};
if (offsets == nullptr)
return nullptr;
if (size > cacheSize && overflowMode == kOverflowModeWrap)
return nullptr;
if (offsets->size() < 2)
return nullptr;
uint32_t head = offsets->front();
uint32_t tail = offsets->back();
offsets->pop_back();
// need to wrap
if (tail + size > cacheSize)
{
if (overflowMode == kOverflowModeWrap)
{
// section grew larger than full buffer and overwrote itself, abort.
if (offsets->size() == 1)
return nullptr;
if (offsets->back() != tail)
offsets->push_back(tail);
uint32_t front = offsets->front();
while (front != 0)
{
offsets->pop_front();
front = offsets->front();
}
offsets->pop_front();
offsets->push_back(0);
head = offsets->front();
tail = offsets->back();
}
else if (overflowMode == kOverflowModeGrowDouble)
{
while (tail + size > cacheSize)
{
// grow
uint32_t newCacheSize = cacheSize * 2;
uint8_t* newData = (uint8_t*)malloc(newCacheSize);
memcpy(newData, data, cacheSize);
free(data);
data = newData;
cacheSize = newCacheSize;
}
}
else if (overflowMode == kOverflowModeTruncate)
{
return nullptr;
}
else
{
assert(0);
}
}
// need to free space / forget sections
if (tail <= head && head != 0)
{
while (tail + size > head)
{
offsets->pop_front();
head = offsets->front();
tail = offsets->back();
if (offsets->size() == 1)
break;
if (*(offsets->begin() + 1) == 0)
{
offsets->pop_front();
break;
}
}
}
offsets->push_back(tail + size);
ret = &data[tail];
return ret;
}
bool HasDataForRead()
{
return offsets != nullptr && offsets->size() > 1 && offsets[0] != offsets[1];
}
// returns true if there is more data to read
// in practice there may be two reads necessary to get all the data, from mid to end and from beginning to mid.
// reading is a one time operation - data is cleared after read.
bool GetForReadAndClear(uint8_t** ptr, uint32_t* size)
{
if (offsets == nullptr)
{
*ptr = nullptr;
*size = 0;
return false;
}
uint32_t head = offsets->front();
*ptr = &data[head];
uint32_t max = 0;
for (auto it = offsets->begin(); it != offsets->end();)
{
if (*it > max)
max = *it;
else if (max == 0)
{
++it;
continue;
}
else
break;
it = offsets->erase(it);
}
*size = max - head;
return offsets->size() > 1 && size != 0;
}
bool GetForRead(uint8_t** ptr, uint32_t* size)
{
if (overflowMode == kOverflowModeGrowDouble && offsets != nullptr)
{
*ptr = data;
*size = offsets->back();
return false;
}
*ptr = nullptr;
*size = 0;
return false;
}
bool MoveFrom(RingBuf& other)
{
if (other.HasDataForRead() && offsets != nullptr)
{
CreateNewBlock();
uint8_t* ptr{};
uint32_t size{};
bool more = false;
do
{
more = other.GetForReadAndClear(&ptr, &size);
uint8_t* dst = GetForWrite(size);
if (dst != nullptr)
{
memcpy(dst, ptr, size);
}
} while (more);
}
else
{
return false;
}
return true;
}
void Write(Command c)
{
uint8_t* wbuf = GetForWrite(sizeof(Command));
if (wbuf != nullptr)
{
*(Command*)wbuf = c;
}
}
void Write(LUT l)
{
uint8_t* wbuf = GetForWrite(sizeof(LUT));
if (wbuf != nullptr)
{
*(LUT*)wbuf = l;
}
}
void Write(std::thread::id id)
{
std::stringstream ss;
ss << id;
Write(ss.str());
}
void Write(const std::string& s)
{
uint8_t* wbuf = GetForWrite(sizeof(char) * ((uint32_t)s.size() + 1));
if (wbuf != nullptr)
{
memcpy(wbuf, s.c_str(), sizeof(char) * (uint32_t)s.size());
wbuf[s.size()] = 0;
}
}
void Write(float f)
{
uint8_t* wbuf = GetForWrite(sizeof(float));
if (wbuf != nullptr)
{
*(float*)wbuf = f;
}
}
void Write(int32_t i)
{
uint8_t* wbuf = GetForWrite(sizeof(int32_t));
if (wbuf != nullptr)
{
*(int32_t*)wbuf = i;
}
}
void Write(int64_t i)
{
uint8_t* wbuf = GetForWrite(sizeof(int64_t));
if (wbuf != nullptr)
{
*(int64_t*)wbuf = i;
}
}
void Write(uint32_t u)
{
uint8_t* wbuf = GetForWrite(sizeof(uint32_t));
if (wbuf != nullptr)
{
*(uint32_t*)wbuf = u;
}
}
void Write(uint64_t u)
{
uint8_t* wbuf = GetForWrite(sizeof(uint64_t));
if (wbuf != nullptr)
{
*(uint64_t*)wbuf = u;
}
}
};
//#define CACHE_SIZE 1024 * 1024 * 1
//
//int tests()
//{
// RingBuf buf{};
// buf.Create(CACHE_SIZE);
//
// uint8_t* ptr;
// uint32_t size;
//
// // Check that starts out empty
// assert(buf.GetForReadAndClear(&ptr, &size) == false);
// assert(size == 0);
// buf.Reset();
//
// // Write
// buf.CreateNewBlock();
// auto* wbuf = buf.GetForWrite(4);
// *(uint32_t*)wbuf = 0x12345678;
// assert(buf.GetForReadAndClear(&ptr, &size) == false);
// assert(size == 4);
// assert(*(uint32_t*)ptr == 0x12345678);
// buf.Reset();
//
// // Write 2
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(4);
// *(uint32_t*)wbuf = 0x12345678;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// *(uint64_t*)wbuf = 0x123456789abcdef0;
//
// assert(buf.GetForReadAndClear(&ptr, &size) == false);
// assert(size == 12);
// assert(*(uint32_t*)ptr == 0x12345678);
// assert(*(uint64_t*)(ptr + 4) == 0x123456789abcdef0);
// buf.Reset();
//
// // Full buf
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(CACHE_SIZE - 16);
//
// assert(buf.GetForReadAndClear(&ptr, &size) == false);
// assert(size == CACHE_SIZE);
// buf.Reset();
//
// // Wrap buf, perfectly lines up
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 1;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 2;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(CACHE_SIZE - 16);
// for (int i = 0; i < (CACHE_SIZE - 16); ++i)
// wbuf[i] = 3;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 4;
//
// assert(buf.GetForReadAndClear(&ptr, &size) == true);
// assert(size == CACHE_SIZE - 8);
// assert(ptr[0] == 2);
// assert(ptr[8] == 3);
// assert(ptr[size - 1] == 3);
//
// assert(buf.GetForReadAndClear(&ptr, &size) == false);
// assert(size == 8);
// assert(ptr[0] == 4);
// buf.Reset();
//
// // Wrap buf, not perfect
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 1;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 2;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(CACHE_SIZE - 20);
// for (int i = 0; i < (CACHE_SIZE - 20); ++i)
// wbuf[i] = 3;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 4;
//
// assert(buf.GetForReadAndClear(&ptr, &size) == true);
// assert(size == CACHE_SIZE - 12);
// assert(ptr[0] == 2);
// assert(ptr[8] == 3);
// assert(ptr[size - 1] == 3);
//
// assert(buf.GetForReadAndClear(&ptr, &size) == false);
// assert(size == 8);
// assert(ptr[0] == 4);
// buf.Reset();
//
// // Wrap, and fit more in previous first spot
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 1;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 2;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(CACHE_SIZE - 16);
// for (int i = 0; i < (CACHE_SIZE - 16); ++i)
// wbuf[i] = 3;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(4);
// for (int i = 0; i < 4; ++i)
// wbuf[i] = 4;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(4);
// for (int i = 0; i < 4; ++i)
// wbuf[i] = 5;
//
// assert(buf.GetForReadAndClear(&ptr, &size) == true);
// assert(size == CACHE_SIZE - 8);
// assert(ptr[0] == 2);
// assert(ptr[8] == 3);
// assert(ptr[size - 1] == 3);
//
// assert(buf.GetForReadAndClear(&ptr, &size) == false);
// assert(size == 8);
// assert(ptr[0] == 4);
// assert(ptr[4] == 5);
// buf.Reset();
//
// // Wrap, and consume first two entries
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 1;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 2;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(CACHE_SIZE - 16);
// for (int i = 0; i < (CACHE_SIZE - 16); ++i)
// wbuf[i] = 3;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(4);
// for (int i = 0; i < 4; ++i)
// wbuf[i] = 4;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 5;
//
// assert(buf.GetForReadAndClear(&ptr, &size) == true);
// assert(size == CACHE_SIZE - 8 - 8);
// assert(ptr[0] == 3);
// assert(ptr[size - 1] == 3);
//
// assert(buf.GetForReadAndClear(&ptr, &size) == false);
// assert(size == 12);
// assert(ptr[0] == 4);
// assert(ptr[4] == 5);
// buf.Reset();
//
// // Wrap, and consume first three entries
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 1;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 2;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(CACHE_SIZE - 16);
// for (int i = 0; i < (CACHE_SIZE - 16); ++i)
// wbuf[i] = 3;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(4);
// for (int i = 0; i < 4; ++i)
// wbuf[i] = 4;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 5;
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 6;
//
// assert(buf.GetForReadAndClear(&ptr, &size) == false);
// assert(size == 20);
// assert(ptr[0] == 4);
// assert(ptr[4] == 5);
// assert(ptr[12] == 6);
// buf.Reset();
//
// // Section wraps, perfectly
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 1;
// buf.CreateNewBlock();
// buf.GetForWrite(CACHE_SIZE);
// for (int i = 0; i < (CACHE_SIZE); ++i)
// wbuf[i] = 2;
//
// assert(buf.GetForReadAndClear(&ptr, &size) == false);
// assert(size == CACHE_SIZE);
// assert(ptr[0] == 2);
// assert(ptr[CACHE_SIZE - 1] == 2);
// buf.Reset();
//
// // Section wraps, not perfect
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 1;
// buf.CreateNewBlock();
// buf.GetForWrite(CACHE_SIZE - 16);
// for (int i = 0; i < (CACHE_SIZE - 16); ++i)
// wbuf[i] = 2;
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 2;
// wbuf = buf.GetForWrite(8);
// for (int i = 0; i < 8; ++i)
// wbuf[i] = 2;
//
// assert(buf.GetForReadAndClear(&ptr, &size) == true);
// assert(size == CACHE_SIZE - 8);
// assert(ptr[0] == 2);
// assert(ptr[size - 1] == 2);
//
// assert(buf.GetForReadAndClear(&ptr, &size) == false);
// assert(size == 8);
// assert(ptr[0] == 2);
// buf.Reset();
//
// // section wraps on itself
// buf.CreateNewBlock();
// wbuf = buf.GetForWrite(CACHE_SIZE - 8);
// wbuf = buf.GetForWrite(8);
//
// wbuf = buf.GetForWrite(8);
// assert(wbuf == nullptr);
//
// return 0;
//}

View File

@@ -0,0 +1,110 @@
#include "platform_includes.h"
#include <openxr/openxr_platform_defines.h>
// 32-bit builds don't have type-safe handles so we can't distinguish them
#define XR_TYPE_SAFE_HANDLES (XR_PTR_SIZE == 8)
#if !defined(XR_DEFINE_ATOM)
#if XR_TYPE_SAFE_HANDLES
#define XR_DEFINE_ATOM(object) typedef struct object##_T* object;
#else
#define XR_DEFINE_ATOM(object) typedef uint64_t object;
#endif
#endif
#define XR_NO_PROTOTYPES
#include <openxr/openxr.h>
#include <openxr/openxr_loader_negotiation.h>
#include <openxr/openxr_platform.h>
#include "openxr/openxr_reflection_full.h"
#include <cstdio>
#include <cstring>
#include <set>
#include <type_traits>
#include "api_exports.h"
#define CATCH_MISSING_TEMPLATES 0
static void SendString(const char* fieldName, const char* t);
template <typename T>
void SendToCSharp(const char* fieldname, T t)
#if !CATCH_MISSING_TEMPLATES
{
SendString(fieldname, "<Unknown>");
}
#else
;
#endif
template <typename T>
void SendToCSharp(const char* fieldname, T* t)
#if !CATCH_MISSING_TEMPLATES
{
char buf[32];
snprintf(buf, 32, "0x%p", t);
SendString(fieldname, buf);
}
#else
;
#endif
// Arrays of base structs
template <typename structType>
bool SendToCSharpBaseStructArray(const char* fieldname, structType t, int lenParam)
{
return false;
}
// Nullptrs
void SendToCSharpNullPtr(const char* fieldname)
{
SendString(fieldname, "nullptr");
}
// These includes are order-sensitive
// clang-format off
#include "serialize_data.h"
#include "serialize_data_access.h"
#include "serialize_primitives.h"
#include "serialize_enums.h"
#include "serialize_handles.h"
#include "serialize_atoms.h"
#include "serialize_nextptr.h"
#include "serialize_external.h"
#include "serialize_structs.h"
#include "serialize_todo.h"
#include "serialize_nextptr_impl.h"
#include "serialize_funcs_specialization.h"
#include "serialize_funcs.h"
// clang-format on
extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function)
{
StartFunctionCall("xrGetInstanceProcAddr");
SendToCSharp("instance", instance);
SendToCSharp("name", name);
SendToCSharp("function", "<func>");
XR_LIST_FUNCS(GEN_FUNC_LOAD)
EndFunctionCall("xrGetInstanceProcAddr", "UNKNOWN FUNC");
return orig_xrGetInstanceProcAddr(instance, name, function);
}
extern "C" PFN_xrGetInstanceProcAddr UNITY_INTERFACE_EXPORT XRAPI_PTR HookXrInstanceProcAddr(PFN_xrGetInstanceProcAddr func, uint32_t cacheSize, uint32_t perThreadCacheSize)
{
ResetLUT();
s_CacheSize = cacheSize;
s_PerThreadCacheSize = perThreadCacheSize;
s_MainDataStore.SetOverflowMode(RingBuf::kOverflowModeTruncate);
orig_xrGetInstanceProcAddr = func;
return xrGetInstanceProcAddr;
}

View File

@@ -0,0 +1,18 @@
#pragma once
#if XR_TYPE_SAFE_HANDLES
template <>
void SendToCSharp<>(const char* fieldname, XrSystemId t)
{
SendToCSharp(fieldname, (uint64_t)t);
}
template <>
void SendToCSharp<>(const char* fieldname, XrSystemId* t)
{
if (t == nullptr)
SendToCSharpNullPtr(fieldname);
else
SendToCSharp(fieldname, (uint64_t)*t);
}
#endif

View File

@@ -0,0 +1,314 @@
#pragma once
#include <cassert>
#include <deque>
#include <mutex>
#include <stdlib.h>
#include <string>
#include <thread>
enum Command
{
kStartFunctionCall,
kStartStruct,
kFloat,
kString,
kInt32,
kInt64,
kUInt32,
kUInt64,
kEndStruct,
kEndFunctionCall,
kCacheNotLargeEnough,
kLUTDefineTables,
kLUTEntryUpdateStart,
kLutEntryUpdateEnd,
kLUTLookup,
kEndData = 0xFFFFFFFF
};
enum LUT
{
kXrPath,
kXrAction,
kXrActionSet,
kXrSpace,
kLUTPadding = 0xFFFFFFFF,
};
static const char* const kLutNames[] = {
"XrPaths",
"XrActions",
"XrActionSets",
"XrSpaces",
};
#include "ringbuf.h"
static std::mutex s_DataMutex;
// These get set from c# in HookXrInstanceProcAddr
static uint32_t s_CacheSize = 0;
static uint32_t s_PerThreadCacheSize = 0;
static uint32_t s_LUTCacheSize = 256; // TODO:
// Accessing this must be protected with s_DataMutex.
// Only add into these at kEndFunctionCall to avoid mixing with other threads.
static RingBuf s_MainDataStore = {};
static RingBuf s_LUTDataStore = {};
// Thread local storage of serialized commands.
// On EndFunctionCall they'll be moved into the static storage w/ mutex lock.
thread_local RingBuf s_ThreadLocalDataStore = {};
static void StartFunctionCall(const char* funcName)
{
if (s_ThreadLocalDataStore.cacheSize != s_PerThreadCacheSize)
{
s_ThreadLocalDataStore.Destroy();
s_ThreadLocalDataStore.Create(s_PerThreadCacheSize, RingBuf::kOverflowModeWrap);
}
s_ThreadLocalDataStore.CreateNewBlock();
s_ThreadLocalDataStore.Write(kStartFunctionCall);
s_ThreadLocalDataStore.Write(std::this_thread::get_id());
s_ThreadLocalDataStore.Write(funcName);
}
static void StartStruct(const char* fieldName, const char* structName)
{
s_ThreadLocalDataStore.Write(kStartStruct);
s_ThreadLocalDataStore.Write(fieldName);
s_ThreadLocalDataStore.Write(structName);
}
static void SendFloat(const char* fieldName, float t)
{
s_ThreadLocalDataStore.Write(kFloat);
s_ThreadLocalDataStore.Write(fieldName);
s_ThreadLocalDataStore.Write(t);
}
static void SendString(const char* fieldName, const char* t)
{
s_ThreadLocalDataStore.Write(kString);
s_ThreadLocalDataStore.Write(fieldName);
s_ThreadLocalDataStore.Write(t);
}
static void SendInt32(const char* fieldName, int32_t t)
{
s_ThreadLocalDataStore.Write(kInt32);
s_ThreadLocalDataStore.Write(fieldName);
s_ThreadLocalDataStore.Write(t);
}
static void SendInt64(const char* fieldName, int64_t t)
{
s_ThreadLocalDataStore.Write(kInt64);
s_ThreadLocalDataStore.Write(fieldName);
s_ThreadLocalDataStore.Write(t);
}
static void SendUInt32(const char* fieldName, uint32_t t)
{
s_ThreadLocalDataStore.Write(kUInt32);
s_ThreadLocalDataStore.Write(fieldName);
s_ThreadLocalDataStore.Write(t);
}
static void SendUInt64(const char* fieldName, uint64_t t)
{
s_ThreadLocalDataStore.Write(kUInt64);
s_ThreadLocalDataStore.Write(fieldName);
s_ThreadLocalDataStore.Write(t);
}
static void EndStruct()
{
s_ThreadLocalDataStore.Write(kEndStruct);
}
static void EndFunctionCall(const char* funcName, const char* result)
{
s_ThreadLocalDataStore.Write(kEndFunctionCall);
s_ThreadLocalDataStore.Write(result);
{
std::lock_guard<std::mutex> guard(s_DataMutex);
if (s_MainDataStore.cacheSize != s_CacheSize)
{
s_MainDataStore.Destroy();
s_MainDataStore.Create(s_CacheSize, RingBuf::kOverflowModeTruncate);
}
if (!s_MainDataStore.MoveFrom(s_ThreadLocalDataStore))
{
s_MainDataStore.CreateNewBlock();
s_MainDataStore.Write(kCacheNotLargeEnough);
s_MainDataStore.Write(std::this_thread::get_id());
s_MainDataStore.Write(funcName);
s_MainDataStore.Write(result);
}
}
}
void ResetLUT()
{
s_LUTDataStore.Destroy();
s_LUTDataStore.Create(s_LUTCacheSize, RingBuf::kOverflowModeGrowDouble);
s_LUTDataStore.CreateNewBlock();
// Setup LUTS
s_LUTDataStore.Write(kLUTDefineTables);
uint32_t numLuts = (uint32_t)(sizeof(kLutNames) / sizeof(kLutNames[0]));
s_LUTDataStore.Write(numLuts);
for (uint32_t i = 0; i < numLuts; ++i)
{
s_LUTDataStore.Write(kLutNames[i]);
}
}
static void StoreInLUT(const char* funcName)
{
if (s_LUTDataStore.cacheSize < s_LUTCacheSize)
{
ResetLUT();
}
{
std::lock_guard<std::mutex> guard(s_DataMutex); // TODO: probably have a different mutex for LUT data store
s_LUTDataStore.MoveFrom(s_ThreadLocalDataStore);
}
// We hijacked the function that was already started.
// Start a function call for the real call which happens next.
StartFunctionCall(funcName);
}
static void SendXrPathCreate(const char* funcName, XrPath path, const char* string)
{
s_ThreadLocalDataStore.Reset();
s_ThreadLocalDataStore.CreateNewBlock();
s_ThreadLocalDataStore.Write(kLUTEntryUpdateStart);
s_ThreadLocalDataStore.Write(kXrPath);
s_ThreadLocalDataStore.Write((uint64_t)path);
s_ThreadLocalDataStore.Write(string);
StartStruct("", "");
EndStruct();
StoreInLUT(funcName);
}
static void SendXrPath(const char* fieldName, XrPath t)
{
s_ThreadLocalDataStore.Write(kLUTLookup);
s_ThreadLocalDataStore.Write(kXrPath);
s_ThreadLocalDataStore.Write(fieldName);
s_ThreadLocalDataStore.Write((uint64_t)t);
}
template <>
void SendToCSharp<>(const char*, const XrActionCreateInfo*);
static void SendXrActionCreate(const char* funcName, XrAction action, const XrActionCreateInfo* createInfo)
{
s_ThreadLocalDataStore.Reset();
s_ThreadLocalDataStore.CreateNewBlock();
s_ThreadLocalDataStore.Write(kLUTEntryUpdateStart);
s_ThreadLocalDataStore.Write(kXrAction);
s_ThreadLocalDataStore.Write((uint64_t)action);
s_ThreadLocalDataStore.Write(createInfo->actionName);
SendToCSharp("", createInfo);
StoreInLUT(funcName);
}
static void SendXrAction(const char* fieldName, XrAction t)
{
s_ThreadLocalDataStore.Write(kLUTLookup);
s_ThreadLocalDataStore.Write(kXrAction);
s_ThreadLocalDataStore.Write(fieldName);
s_ThreadLocalDataStore.Write((uint64_t)t);
}
template <>
void SendToCSharp<>(const char*, const XrActionSetCreateInfo*);
static void SendXrActionSetCreate(const char* funcName, XrActionSet actionSet, const XrActionSetCreateInfo* createInfo)
{
s_ThreadLocalDataStore.Reset();
s_ThreadLocalDataStore.CreateNewBlock();
s_ThreadLocalDataStore.Write(kLUTEntryUpdateStart);
s_ThreadLocalDataStore.Write(kXrActionSet);
s_ThreadLocalDataStore.Write((uint64_t)actionSet);
s_ThreadLocalDataStore.Write(createInfo->actionSetName);
SendToCSharp("", createInfo);
StoreInLUT(funcName);
}
static void SendXrActionSet(const char* fieldName, XrActionSet t)
{
s_ThreadLocalDataStore.Write(kLUTLookup);
s_ThreadLocalDataStore.Write(kXrActionSet);
s_ThreadLocalDataStore.Write(fieldName);
s_ThreadLocalDataStore.Write((uint64_t)t);
}
template <>
void SendToCSharp<>(const char*, const XrActionSpaceCreateInfo*);
static void SendXrActionSpaceCreate(const char* funcName, const XrActionSpaceCreateInfo* createInfo, XrSpace* space)
{
s_ThreadLocalDataStore.Reset();
s_ThreadLocalDataStore.CreateNewBlock();
s_ThreadLocalDataStore.Write(kLUTEntryUpdateStart);
s_ThreadLocalDataStore.Write(kXrSpace);
s_ThreadLocalDataStore.Write((uint64_t)*space);
s_ThreadLocalDataStore.Write("Action Space");
SendToCSharp("", createInfo);
StoreInLUT(funcName);
}
#define GENERATE_REFERENCE_SPACE_STRING(enumentry, enumvalue) \
case enumvalue: \
return #enumentry; \
break;
static const char* GetReferenceSpaceString(XrReferenceSpaceType t)
{
switch (t)
{
XR_LIST_ENUM_XrReferenceSpaceType(GENERATE_REFERENCE_SPACE_STRING) default : return "Reference Space Unknown";
}
}
template <>
void SendToCSharp<>(const char*, const XrReferenceSpaceCreateInfo*);
static void SendXrReferenceSpaceCreate(const char* funcName, const XrReferenceSpaceCreateInfo* createInfo, XrSpace* space)
{
s_ThreadLocalDataStore.Reset();
s_ThreadLocalDataStore.CreateNewBlock();
s_ThreadLocalDataStore.Write(kLUTEntryUpdateStart);
s_ThreadLocalDataStore.Write(kXrSpace);
s_ThreadLocalDataStore.Write((uint64_t)*space);
s_ThreadLocalDataStore.Write(GetReferenceSpaceString(createInfo->referenceSpaceType));
SendToCSharp("", createInfo);
StoreInLUT(funcName);
}
static void SendXrSpace(const char* fieldName, XrSpace t)
{
s_ThreadLocalDataStore.Write(kLUTLookup);
s_ThreadLocalDataStore.Write(kXrSpace);
s_ThreadLocalDataStore.Write(fieldName);
s_ThreadLocalDataStore.Write((uint64_t)t);
}

View File

@@ -0,0 +1,35 @@
#pragma once
#include <sstream>
extern "C" void UNITY_INTERFACE_EXPORT StartDataAccess()
{
s_DataMutex.lock();
}
extern "C" bool UNITY_INTERFACE_EXPORT GetDataForRead(uint8_t** ptr, uint32_t* size)
{
s_MainDataStore.SetOverflowMode(RingBuf::kOverflowModeWrap);
return s_MainDataStore.GetForReadAndClear(ptr, size);
}
extern "C" bool UNITY_INTERFACE_EXPORT GetLUTData(uint8_t** ptr, uint32_t* size, uint32_t offset)
{
bool ret = s_LUTDataStore.GetForRead(ptr, size);
if (*size <= offset)
{
*ptr = nullptr;
*size = 0;
return false;
}
*ptr = *ptr + offset;
*size = *size - offset;
s_LUTDataStore.DropLastBlock();
return ret;
}
extern "C" void UNITY_INTERFACE_EXPORT EndDataAccess()
{
s_MainDataStore.Reset();
s_DataMutex.unlock();
}

View File

@@ -0,0 +1,50 @@
#pragma once
#define SEND_TO_CSHARP_ENUM_INDIVIDUAL(enumentry, enumvalue) \
case enumvalue: \
SendToCSharp(fieldname, #enumentry); \
break;
#define SEND_TO_CSHARP_ENUMS(enumname) \
template <> \
void SendToCSharp<>(const char* fieldname, enumname t) \
{ \
switch (t) \
{ \
XR_LIST_ENUM_##enumname(SEND_TO_CSHARP_ENUM_INDIVIDUAL) default : SendToCSharp(fieldname, "UNKNOWN"); \
} \
}
XR_LIST_ENUM_TYPES(SEND_TO_CSHARP_ENUMS)
#define SEND_TO_CSHARP_ENUMS_PTR(enumname) \
template <> \
void SendToCSharp<>(const char* fieldname, enumname* t) \
{ \
if (t == nullptr) \
{ \
SendToCSharpNullPtr(fieldname); \
return; \
} \
\
switch (*t) \
{ \
XR_LIST_ENUM_##enumname(SEND_TO_CSHARP_ENUM_INDIVIDUAL) default : SendToCSharp(fieldname, "UNKNOWN"); \
} \
}
XR_LIST_ENUM_TYPES(SEND_TO_CSHARP_ENUMS_PTR)
#define XR_ENUM_CASE_STR(name, val) \
case name: \
return #name;
#define XR_ENUM_STR(enumType) \
const char* XrEnumStr(enumType e) \
{ \
switch (e) \
{ \
XR_LIST_ENUM_##enumType(XR_ENUM_CASE_STR) default : return "Unknown"; \
} \
}
XR_ENUM_STR(XrResult);

View File

@@ -0,0 +1,24 @@
#pragma once
#if XR_USE_GRAPHICS_API_D3D11
//struct ID3D11Texture2D;
template <>
void SendToCSharp<>(const char* fieldname, LUID t)
{
StartStruct(fieldname, "LUID");
char buf[32];
snprintf(buf, 32, "0x%x", t.LowPart);
SendToCSharp("LowPart", buf);
snprintf(buf, 32, "0x%x", t.HighPart);
SendToCSharp("HighPart", buf);
EndStruct();
}
template <>
void SendToCSharp<>(const char* fieldname, D3D_FEATURE_LEVEL t)
{
char buf[32];
snprintf(buf, 32, "0x%x", t);
SendToCSharp(fieldname, buf);
}
#endif

View File

@@ -0,0 +1,49 @@
#pragma once
// Begin Declaration of original functions
static PFN_xrGetInstanceProcAddr orig_xrGetInstanceProcAddr;
#define GEN_ORIG_FUNC_PTRS(f, ...) \
static PFN_##f orig_##f;
XR_LIST_FUNCS(GEN_ORIG_FUNC_PTRS)
// End Declaration of original functions
#define GEN_PARAMS(...) \
__VA_ARGS__
#define SEND_PARAM_TO_CSHARP(param) \
SendToCSharp(#param, param);
#define SEND_ARRAY_TO_CSHARP(param, lenParam) \
if (!SendToCSharpBaseStructArray(#param, param, lenParam)) \
{ \
for (uint32_t i = 0; i < lenParam; ++i) \
SendToCSharp(#param, param[i]); \
}
#define GEN_FUNCS(f, ...) \
extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR f(__VA_ARGS__) \
{ \
XR_BEFORE_##f(#f); \
StartFunctionCall(#f); \
XrResult result = orig_##f(XR_LIST_FUNC_PARAM_NAMES_##f(GEN_PARAMS)); \
XR_AFTER_##f(#f); \
uint32_t lastCount = 0; \
XR_LIST_FUNC_##f(SEND_PARAM_TO_CSHARP); \
XR_LIST_FUNC_ARRAYS_##f(SEND_ARRAY_TO_CSHARP); \
EndFunctionCall(#f, XrEnumStr(result)); \
return result; \
}
XR_LIST_FUNCS(GEN_FUNCS)
#define GEN_FUNC_LOAD(f, ...) \
if (strcmp(#f, name) == 0) \
{ \
auto ret = orig_xrGetInstanceProcAddr(instance, name, (PFN_xrVoidFunction*)&orig_##f); \
if (ret == XR_SUCCESS) \
*function = (PFN_xrVoidFunction)&f; \
EndFunctionCall(#f, XrEnumStr(ret)); \
return ret; \
}

View File

@@ -0,0 +1,57 @@
#pragma once
//XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrLoadControllerModelMSFT(XrSession session, XrControllerModelKeyMSFT modelKey, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, uint8_t* buffer)
#undef XR_BEFORE_xrLoadControllerModelMSFT
#define XR_BEFORE_xrLoadControllerModelMSFT(funcName) \
{ \
StartFunctionCall(funcName); \
XrResult result = orig_xrLoadControllerModelMSFT(session, modelKey, bufferCapacityInput, bufferCountOutput, buffer); \
SendToCSharp("session", session); \
SendToCSharp("modelKey", modelKey); \
SendToCSharp("bufferCapacityInput", bufferCapacityInput); \
SendToCSharp("bufferCountOutput", bufferCountOutput); \
SendToCSharp("buffer", "<TODO>"); \
EndFunctionCall("xrLoadControllerModelMSFT", XrEnumStr(result)); \
return result; \
}
//XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrStringToPath(XrInstance instance, const char* pathString, XrPath* path)
#undef XR_AFTER_xrStringToPath
#define XR_AFTER_xrStringToPath(funcName) \
{ \
/* Cache off path -> string for later lookup .. send it off to c# for UI */ \
if (XR_SUCCEEDED(result)) \
SendXrPathCreate(funcName, *path, pathString); \
}
//typedef XrResult (XRAPI_PTR *PFN_xrCreateAction)(XrActionSet actionSet, const XrActionCreateInfo* createInfo, XrAction* action);
#undef XR_AFTER_xrCreateAction
#define XR_AFTER_xrCreateAction(funcName) \
{ \
if (XR_SUCCEEDED(result)) \
SendXrActionCreate(funcName, *action, createInfo); \
}
//typedef XrResult (XRAPI_PTR *PFN_xrCreateActionSet)(XrInstance instance, const XrActionSetCreateInfo* createInfo, XrActionSet* actionSet);
#undef XR_AFTER_xrCreateActionSet
#define XR_AFTER_xrCreateActionSet(funcName) \
{ \
if (XR_SUCCEEDED(result)) \
SendXrActionSetCreate(funcName, *actionSet, createInfo); \
}
// typedef XrResult (XRAPI_PTR *PFN_xrCreateActionSpace)(XrSession session, const XrActionSpaceCreateInfo* createInfo, XrSpace* space);
#undef XR_AFTER_xrCreateActionSpace
#define XR_AFTER_xrCreateActionSpace(funcName) \
{ \
if (XR_SUCCEEDED(result)) \
SendXrActionSpaceCreate(funcName, createInfo, space); \
}
// typedef XrResult (XRAPI_PTR *PFN_xrCreateReferenceSpace)(XrSession session, const XrReferenceSpaceCreateInfo* createInfo, XrSpace* space);
#undef XR_AFTER_xrCreateReferenceSpace
#define XR_AFTER_xrCreateReferenceSpace(funcName) \
{ \
if (XR_SUCCEEDED(result)) \
SendXrReferenceSpaceCreate(funcName, createInfo, space); \
}

View File

@@ -0,0 +1,39 @@
#pragma once
// Handles are defined as uin64_t on 32-bit builds. They already have a template defined, so we need to exclude this block from 32-bit builds.
// (and remember that _WIN32 is defined on 64-bit windows builds)
#if XR_TYPE_SAFE_HANDLES
typedef struct XrActionSpecialize_t* XrActionSpecialize;
#define XrAction XrActionSpecialize
typedef struct XrActionSetSpecialize_t* XrActionSetSpecialize;
#define XrActionSet XrActionSetSpecialize
typedef struct XrSpaceSpecialize_t* XrSpaceSpecialize;
#define XrSpace XrSpaceSpecialize
#define SEND_TO_CSHARP_HANDLES(handlename) \
template <> \
void SendToCSharp<>(const char* fieldname, handlename t) \
{ \
SendToCSharp(fieldname, (uint64_t)t); \
}
XR_LIST_HANDLES(SEND_TO_CSHARP_HANDLES)
#define SEND_TO_CSHARP_HANDLES_PTRS(handlename) \
template <> \
void SendToCSharp<>(const char* fieldname, handlename* t) \
{ \
if (t != nullptr) \
SendToCSharp(fieldname, (uint64_t)*t); \
else \
SendToCSharp(fieldname, (uint64_t)0); \
}
XR_LIST_HANDLES(SEND_TO_CSHARP_HANDLES_PTRS)
#undef XrAction
#undef XrActionSet
#undef XrSpace
#endif

View File

@@ -0,0 +1,33 @@
#pragma once
template <>
void SendToCSharp<>(const char* fieldname, XrBaseOutStructure* t);
template <>
void SendToCSharp<>(const char* fieldname, XrBaseInStructure const* t);
template <>
void SendToCSharp<>(const char* fieldname, void* t)
{
if (t != nullptr && strcmp(fieldname, "next") == 0)
{
SendToCSharp(fieldname, reinterpret_cast<XrBaseOutStructure*>(t));
}
else
{
SendToCSharp(fieldname, (uint64_t)t);
}
}
template <>
void SendToCSharp<>(const char* fieldname, void const* t)
{
if (t != nullptr && strcmp(fieldname, "next") == 0)
{
SendToCSharp(fieldname, reinterpret_cast<XrBaseInStructure const*>(t));
}
else
{
SendToCSharp(fieldname, (uint64_t)t);
}
}

View File

@@ -0,0 +1,43 @@
#pragma once
#define SEND_NEXT_PTR(structname, structtype) \
case structtype: \
SendToCSharp("next", reinterpret_cast<structname*>(next)); \
break;
template <>
void SendToCSharp<>(const char* fieldname, XrBaseOutStructure* t)
{
auto* next = t;
do
{
switch (next->type)
{
XR_LIST_STRUCTURE_TYPES(SEND_NEXT_PTR)
default:
SendToCSharp("next", next->type);
continue;
}
} while ((next = static_cast<XrBaseOutStructure*>(next->next)) != nullptr);
}
#define SEND_NEXT_PTR_CONST(structname, structtype) \
case structtype: \
SendToCSharp("next", reinterpret_cast<structname const*>(next)); \
break;
template <>
void SendToCSharp<>(const char* fieldname, XrBaseInStructure const* t)
{
auto* next = t;
do
{
switch (next->type)
{
XR_LIST_STRUCTURE_TYPES(SEND_NEXT_PTR_CONST)
default:
SendToCSharp("next", next->type);
continue;
}
} while ((next = static_cast<XrBaseInStructure const*>(next->next)) != nullptr);
}

View File

@@ -0,0 +1,126 @@
#pragma once
template <>
void SendToCSharp<>(const char* fieldname, int32_t t)
{
SendInt32(fieldname, t);
}
template <>
void SendToCSharp<>(const char* fieldname, int32_t* t)
{
if (t != nullptr)
SendInt32(fieldname, *t);
else
SendToCSharpNullPtr(fieldname);
}
template <>
void SendToCSharp<>(const char* fieldname, int64_t t)
{
SendInt64(fieldname, t);
}
template <>
void SendToCSharp<>(const char* fieldname, uint32_t t)
{
SendUInt32(fieldname, t);
}
template <>
void SendToCSharp<>(const char* fieldname, uint32_t* t)
{
if (t != nullptr)
SendUInt32(fieldname, *t);
else
SendToCSharpNullPtr(fieldname);
}
template <>
void SendToCSharp<>(const char* fieldname, uint64_t t)
{
SendUInt64(fieldname, t);
}
template <>
void SendToCSharp<>(const char* fieldname, float t)
{
SendFloat(fieldname, t);
}
template <>
void SendToCSharp<>(const char* fieldname, float* t)
{
if (t != nullptr)
SendFloat(fieldname, *t);
else
SendToCSharpNullPtr(fieldname);
}
template <>
void SendToCSharp<>(const char* fieldname, char* t)
{
if (t != nullptr)
SendString(fieldname, t);
else
SendToCSharpNullPtr(fieldname);
}
template <>
void SendToCSharp<>(const char* fieldname, char const* t)
{
if (t != nullptr)
SendString(fieldname, t);
else
SendToCSharpNullPtr(fieldname);
}
#if XR_TYPE_SAFE_HANDLES
template <>
void SendToCSharp<>(const char* fieldname, XrPath t)
{
SendXrPath(fieldname, t);
}
template <>
void SendToCSharp<>(const char* fieldname, XrPath* t)
{
SendXrPath(fieldname, *t);
}
template <>
void SendToCSharp<>(const char* fieldname, XrAction t)
{
SendXrAction(fieldname, t);
}
template <>
void SendToCSharp<>(const char* fieldname, XrAction* t)
{
SendXrAction(fieldname, *t);
}
template <>
void SendToCSharp<>(const char* fieldname, XrActionSet t)
{
SendXrActionSet(fieldname, t);
}
template <>
void SendToCSharp<>(const char* fieldname, XrActionSet* t)
{
SendXrActionSet(fieldname, *t);
}
template <>
void SendToCSharp<>(const char* fieldname, XrSpace t)
{
SendXrSpace(fieldname, t);
}
template <>
void SendToCSharp<>(const char* fieldname, XrSpace* t)
{
SendXrSpace(fieldname, *t);
}
#endif

View File

@@ -0,0 +1,130 @@
#pragma once
#define SEND_TO_CSHARP_BASE_STRUCT_DECL(structType) \
template <> \
bool SendToCSharpBaseStructArray<structType*>(const char* fieldname, structType* t, int lenParam);
#define SEND_TO_CSHARP_BASE_STRUCT_PTR_DECL(structType) \
template <> \
bool SendToCSharpBaseStructArray<structType**>(const char* fieldname, structType** t, int lenParam);
#define SEND_TO_CSHARP_BASE_STRUCT_CONST_PTR_DECL(structType) \
template <> \
bool SendToCSharpBaseStructArray<structType const* const*>(const char* fieldname, structType const* const* t, int lenParam);
XR_LIST_BASE_STRUCTS(SEND_TO_CSHARP_BASE_STRUCT_DECL)
XR_LIST_BASE_STRUCTS(SEND_TO_CSHARP_BASE_STRUCT_PTR_DECL)
XR_LIST_BASE_STRUCTS(SEND_TO_CSHARP_BASE_STRUCT_CONST_PTR_DECL)
#define SEND_TO_CSHARP_STRUCT_DECL(structType, ...) \
template <> \
void SendToCSharp<>(const char* fieldname, structType t);
#define SEND_TO_CSHARP_STRUCT_PTR_DECL(structType, ...) \
template <> \
void SendToCSharp<>(const char* fieldname, structType* t);
#define SEND_TO_CSHARP_STRUCT_CONST_PTR(structType, ...) \
template <> \
void SendToCSharp<>(const char* fieldname, structType const* t);
// Fwd declare base structs
XR_LIST_BASE_STRUCTS(SEND_TO_CSHARP_STRUCT_DECL)
XR_LIST_BASE_STRUCTS(SEND_TO_CSHARP_STRUCT_PTR_DECL)
XR_LIST_BASE_STRUCTS(SEND_TO_CSHARP_STRUCT_CONST_PTR)
// Fwd declare basic structs
XR_LIST_BASIC_STRUCTS(SEND_TO_CSHARP_STRUCT_DECL)
XR_LIST_BASIC_STRUCTS(SEND_TO_CSHARP_STRUCT_PTR_DECL)
XR_LIST_BASIC_STRUCTS(SEND_TO_CSHARP_STRUCT_CONST_PTR)
// Fwd declare full structs
XR_LIST_STRUCTURE_TYPES(SEND_TO_CSHARP_STRUCT_DECL)
XR_LIST_STRUCTURE_TYPES(SEND_TO_CSHARP_STRUCT_PTR_DECL)
XR_LIST_STRUCTURE_TYPES(SEND_TO_CSHARP_STRUCT_CONST_PTR)
#define SEND_TO_CSHARP_INDIVIDUAL_FIELDS(structname) \
SendToCSharp(#structname, t.structname);
#define SEND_TO_CSHARP_INDIVIDUAL_FIELDS_PTR(structname) \
SendToCSharp(#structname, t->structname);
#define SEND_TO_CSHARP_ARRAYS(structname, structlen) \
if (!SendToCSharpBaseStructArray(#structname, t.structname, t.structlen)) \
{ \
for (uint32_t i = 0; i < t.structlen; ++i) \
SendToCSharp(#structname, t.structname[i]); \
}
#define SEND_TO_CSHARP_ARRAYS_PTR(structname, structlen) \
if (!SendToCSharpBaseStructArray(#structname, t->structname, t->structlen)) \
{ \
for (uint32_t i = 0; i < t->structlen; ++i) \
SendToCSharp(#structname, t->structname[i]); \
}
#define SEND_TO_CSHARP_STRUCTS(structname, ...) \
template <> \
void SendToCSharp<>(const char* fieldname, structname t) \
{ \
StartStruct(fieldname, #structname); \
XR_LIST_STRUCT_##structname(SEND_TO_CSHARP_INDIVIDUAL_FIELDS); \
XR_LIST_STRUCT_ARRAYS_##structname(SEND_TO_CSHARP_ARRAYS); \
EndStruct(); \
}
#define SEND_TO_CSHARP_STRUCTS_PTRS(structname, ...) \
template <> \
void SendToCSharp<>(const char* fieldname, structname* t) \
{ \
if (t == nullptr) \
{ \
SendToCSharpNullPtr(fieldname); \
return; \
} \
\
StartStruct(fieldname, #structname); \
XR_LIST_STRUCT_##structname(SEND_TO_CSHARP_INDIVIDUAL_FIELDS_PTR); \
XR_LIST_STRUCT_ARRAYS_##structname(SEND_TO_CSHARP_ARRAYS_PTR); \
EndStruct(); \
}
#define SEND_TO_CSHARP_STRUCTS_CONST_PTRS(structname, ...) \
template <> \
void SendToCSharp<>(const char* fieldname, const structname* t) \
{ \
if (t == nullptr) \
{ \
SendToCSharpNullPtr(fieldname); \
return; \
} \
\
StartStruct(fieldname, #structname); \
XR_LIST_STRUCT_##structname(SEND_TO_CSHARP_INDIVIDUAL_FIELDS_PTR); \
XR_LIST_STRUCT_ARRAYS_##structname(SEND_TO_CSHARP_ARRAYS_PTR); \
EndStruct(); \
}
// Basic Structs
XR_LIST_BASIC_STRUCTS(SEND_TO_CSHARP_STRUCTS)
XR_LIST_BASIC_STRUCTS(SEND_TO_CSHARP_STRUCTS_PTRS)
XR_LIST_BASIC_STRUCTS(SEND_TO_CSHARP_STRUCTS_CONST_PTRS)
// Full Structs
XR_LIST_STRUCTURE_TYPES(SEND_TO_CSHARP_STRUCTS)
XR_LIST_STRUCTURE_TYPES(SEND_TO_CSHARP_STRUCTS_PTRS)
XR_LIST_STRUCTURE_TYPES(SEND_TO_CSHARP_STRUCTS_CONST_PTRS)
#include "serialize_structs_base.h"

View File

@@ -0,0 +1,188 @@
#pragma once
#define BASE_TO_TYPE(typeName, typeType) \
case typeType: \
{ \
typeName* realType = reinterpret_cast<typeName*>(&t); \
SendToCSharp(fieldname, realType); \
} \
break;
#define SEND_TO_CSHARP_BASE_STRUCT(structType) \
template <> \
void SendToCSharp<>(const char* fieldname, structType t) \
{ \
switch (t.type) \
{ \
XR_LIST_BASE_STRUCT_TYPES_##structType(BASE_TO_TYPE); \
case XR_TYPE_UNKNOWN: \
default: \
SendToCSharp(fieldname, "<Unknown>"); \
break; \
} \
}
XR_LIST_BASE_STRUCTS(SEND_TO_CSHARP_BASE_STRUCT)
#define BASE_TO_TYPE_PTR(typeName, typeType) \
case typeType: \
{ \
typeName* realType = reinterpret_cast<typeName*>(t); \
SendToCSharp(fieldname, realType); \
} \
break;
#define SEND_TO_CSHARP_BASE_STRUCT_PTR(structType) \
template <> \
void SendToCSharp<>(const char* fieldname, structType* t) \
{ \
if (t == nullptr) \
{ \
SendToCSharpNullPtr(fieldname); \
return; \
} \
\
switch (t->type) \
{ \
XR_LIST_BASE_STRUCT_TYPES_##structType(BASE_TO_TYPE_PTR); \
case XR_TYPE_UNKNOWN: \
default: \
SendToCSharp(fieldname, t->type); \
break; \
} \
}
XR_LIST_BASE_STRUCTS(SEND_TO_CSHARP_BASE_STRUCT_PTR)
#define BASE_TO_TYPE_CONST_PTR(typeName, typeType) \
case typeType: \
{ \
typeName const* realType = reinterpret_cast<typeName const*>(t); \
SendToCSharp(fieldname, realType); \
} \
break;
#define SEND_TO_CSHARP_BASE_STRUCT_CONST_PTR(structType) \
template <> \
void SendToCSharp<>(const char* fieldname, structType const* t) \
{ \
if (t == nullptr) \
{ \
SendToCSharpNullPtr(fieldname); \
return; \
} \
\
switch (t->type) \
{ \
XR_LIST_BASE_STRUCT_TYPES_##structType(BASE_TO_TYPE_CONST_PTR); \
case XR_TYPE_UNKNOWN: \
default: \
SendToCSharp(fieldname, "<Unknown>"); \
break; \
} \
}
XR_LIST_BASE_STRUCTS(SEND_TO_CSHARP_BASE_STRUCT_CONST_PTR)
// If we serializing a base struct array such as XrSwapchainImageBaseHeader,
// we can only loop over the array of the real type.
#define BASE_TO_TYPE_ARRAY(typeName, typeType) \
case typeType: \
{ \
typeName* realTypeArray = reinterpret_cast<typeName*>(t); \
for (int i = 0; i < lenParam; ++i) \
SendToCSharp(fieldname, realTypeArray[i]); \
} \
break;
#define SEND_TO_CSHARP_BASE_STRUCT_ARRAY(structType) \
template <> \
bool SendToCSharpBaseStructArray<structType*>(const char* fieldname, structType* t, int lenParam) \
{ \
if (t == nullptr) \
{ \
SendToCSharpNullPtr(fieldname); \
return true; \
} \
\
switch (t[0].type) \
{ \
XR_LIST_BASE_STRUCT_TYPES_##structType(BASE_TO_TYPE_ARRAY); \
case XR_TYPE_UNKNOWN: \
default: \
SendToCSharp(fieldname, "<Unknown>"); \
break; \
} \
return true; \
}
XR_LIST_BASE_STRUCTS(SEND_TO_CSHARP_BASE_STRUCT_ARRAY)
#define BASE_TO_TYPE_ARRAY_PTR(typeName, typeType) \
case typeType: \
{ \
typeName** realTypeArray = reinterpret_cast<typeName**>(t); \
SendToCSharp(fieldname, realTypeArray[i]); \
} \
break;
#define SEND_TO_CSHARP_BASE_STRUCT_ARRAY_PTR(structType) \
template <> \
bool SendToCSharpBaseStructArray<structType**>(const char* fieldname, structType** t, int lenParam) \
{ \
if (t == nullptr || t[0] == nullptr) \
{ \
SendToCSharpNullPtr(fieldname); \
return true; \
} \
\
for (int i = 0; i < lenParam; ++i) \
{ \
switch (t[i]->type) \
{ \
XR_LIST_BASE_STRUCT_TYPES_##structType(BASE_TO_TYPE_ARRAY_PTR); \
case XR_TYPE_UNKNOWN: \
default: \
SendToCSharp(fieldname, "<Unknown>"); \
break; \
} \
} \
return true; \
}
XR_LIST_BASE_STRUCTS(SEND_TO_CSHARP_BASE_STRUCT_ARRAY_PTR)
#define BASE_TO_TYPE_ARRAY_CONST_PTR(typeName, typeType) \
case typeType: \
{ \
typeName const* const* realTypeArray = reinterpret_cast<typeName const* const*>(t); \
SendToCSharp(fieldname, realTypeArray[i]); \
} \
break;
#define SEND_TO_CSHARP_BASE_STRUCT_ARRAY_CONST_PTR(structType) \
template <> \
bool SendToCSharpBaseStructArray<structType const* const*>(const char* fieldname, structType const* const* t, int lenParam) \
{ \
if (t == nullptr || t[0] == nullptr) \
{ \
SendToCSharpNullPtr(fieldname); \
return true; \
} \
\
for (int i = 0; i < lenParam; ++i) \
{ \
switch (t[i]->type) \
{ \
XR_LIST_BASE_STRUCT_TYPES_##structType(BASE_TO_TYPE_ARRAY_CONST_PTR); \
case XR_TYPE_UNKNOWN: \
default: \
SendToCSharp(fieldname, "<Unknown>"); \
break; \
} \
} \
return true; \
}
XR_LIST_BASE_STRUCTS(SEND_TO_CSHARP_BASE_STRUCT_ARRAY_CONST_PTR)

View File

@@ -0,0 +1,32 @@
#pragma once
template <>
void SendToCSharp<>(const char* fieldname, XrEventDataBuffer* t)
{
XrEventDataBaseHeader* evt = reinterpret_cast<XrEventDataBaseHeader*>(t);
SendToCSharp("varying", evt);
}
template <>
void SendToCSharp<>(const char* fieldname, XrDebugUtilsMessengerCreateInfoEXT const* t)
{
SendToCSharp(fieldname, "<TODO: XrDebugUtilsMessengerCreateInfoEXT>");
}
template <>
void SendToCSharp<>(const char* fieldname, XrSpatialGraphNodeSpaceCreateInfoMSFT const* t)
{
SendToCSharp(fieldname, "<TODO: XrSpatialGraphNodeSpaceCreateInfoMSFT>");
}
template <>
void SendToCSharp<>(const char* fieldname, XrInteractionProfileAnalogThresholdVALVE* t)
{
SendToCSharp(fieldname, "<TODO: XrInteractionProfileAnalogThresholdVALVE>");
}
template <>
void SendToCSharp<>(const char* fieldname, XrWorldMeshBufferML* t)
{
SendToCSharp(fieldname, "<TODO: XrWorldMeshBufferML>");
}

View File

@@ -0,0 +1,109 @@
using System;
using System.Runtime.InteropServices;
using UnityEditor;
using UnityEngine.Networking.PlayerConnection;
#if UNITY_EDITOR
using UnityEditor.XR.OpenXR.Features;
using UnityEditor.XR.OpenXR.Features.RuntimeDebugger;
#endif
namespace UnityEngine.XR.OpenXR.Features.RuntimeDebugger
{
/// <summary>
/// A runtime debugger feature. Intercepts all OpenXR calls and forwards them over player connection to an editor window.
/// </summary>
#if UNITY_EDITOR
[OpenXRFeature(UiName = "Runtime Debugger",
BuildTargetGroups = new[] {BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android},
Company = "Unity",
Desc = "Enables debugging of OpenXR calls and dumping runtime info.",
DocumentationLink = "",
FeatureId = "com.unity.openxr.features.runtimedebugger",
OpenxrExtensionStrings = "",
Version = "1")]
#endif
public class RuntimeDebuggerOpenXRFeature : OpenXRFeature
{
internal static readonly Guid kEditorToPlayerRequestDebuggerOutput = new Guid("B3E6DED1-C6C7-411C-BE58-86031A0877E7");
internal static readonly Guid kPlayerToEditorSendDebuggerOutput = new Guid("B3E6DED1-C6C7-411C-BE58-86031A0877E8");
/// <summary>
/// Size of main-thread cache on device for runtime debugger in bytes.
/// </summary>
public UInt32 cacheSize = 1024 * 1024;
/// <summary>
/// Size of per-thread cache on device for runtime debugger in bytes.
/// </summary>
public UInt32 perThreadCacheSize = 50 * 1024;
private UInt32 lutOffset = 0;
/// <inheritdoc/>
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
{
#if !UNITY_EDITOR
PlayerConnection.instance.Register(kEditorToPlayerRequestDebuggerOutput, RecvMsg);
#endif
// Reset
Native_StartDataAccess();
Native_EndDataAccess();
lutOffset = 0;
return Native_HookGetInstanceProcAddr(func, cacheSize, perThreadCacheSize);
}
internal void RecvMsg(MessageEventArgs args)
{
Native_StartDataAccess();
// LUT for actions / handles
Native_GetLUTData(out var lutPtr, out var lutSize, lutOffset);
byte[] lutData = new Byte[lutSize];
if (lutSize > 0)
{
lutOffset = lutSize;
Marshal.Copy(lutPtr, lutData, 0, (int)lutSize);
}
// ring buffer on native side, so might get two chunks of data
Native_GetDataForRead(out var ptr1, out var size1);
Native_GetDataForRead(out var ptr2, out var size2);
byte[] data = new byte[size1 + size2];
if (size1 > 0)
Marshal.Copy(ptr1, data, 0, (int)size1);
if (size2 > 0)
Marshal.Copy(ptr2, data, (int)size1, (int)size2);
Native_EndDataAccess();
#if !UNITY_EDITOR
PlayerConnection.instance.Send(kPlayerToEditorSendDebuggerOutput, lutData);
PlayerConnection.instance.Send(kPlayerToEditorSendDebuggerOutput, data);
#else
DebuggerState.OnMessageEvent(new MessageEventArgs() {playerId = 0, data = lutData});
DebuggerState.OnMessageEvent(new MessageEventArgs() { playerId = 0, data = data});
#endif
}
private const string Library = "openxr_runtime_debugger";
[DllImport(Library, EntryPoint = "HookXrInstanceProcAddr")]
private static extern IntPtr Native_HookGetInstanceProcAddr(IntPtr func, UInt32 cacheSize, UInt32 perThreadCacheSize);
[DllImport(Library, EntryPoint = "GetDataForRead")]
[return: MarshalAs(UnmanagedType.U1)]
private static extern bool Native_GetDataForRead(out IntPtr ptr, out UInt32 size);
[DllImport(Library, EntryPoint = "GetLUTData")]
private static extern void Native_GetLUTData(out IntPtr ptr, out UInt32 size, UInt32 offset);
[DllImport(Library, EntryPoint = "StartDataAccess")]
private static extern void Native_StartDataAccess();
[DllImport(Library, EntryPoint = "EndDataAccess")]
private static extern void Native_EndDataAccess();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 056125dd64c0ed540b40a4af74f7b495
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
{
"name": "Unity.XR.OpenXR.Features.RuntimeDebugger",
"rootNamespace": "",
"references": [
"Unity.XR.OpenXR",
"Unity.XR.OpenXR.Editor"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
""
],
"autoReferenced": false,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 784a7033de40af04db4f8c4de440f481
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 91198f1ea5384e828a74bd7bdc66afb3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 263aafbdc7e14fe09a16e55bf1be8a81
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,82 @@
fileFormatVersion: 2
guid: c46bc5d3bad84f108dc4405b097380e5
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
'': Any
second:
enabled: 0
settings:
Exclude Android: 0
Exclude Editor: 1
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
- first:
Android: Android
second:
enabled: 1
settings:
CPU: ARM64
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 61db20b39b784897b126852067c5815a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,82 @@
fileFormatVersion: 2
guid: cb2f8fd312e042178b4a8fe9207d1152
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
'': Any
second:
enabled: 0
settings:
Exclude Android: 0
Exclude Editor: 1
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
- first:
Android: Android
second:
enabled: 1
settings:
CPU: X86_64
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dd2fb41e02bd4a298fcfefffe0e0ccef
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,92 @@
fileFormatVersion: 2
guid: 32cc0620a1d7401694b572ba1df2bd28
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 0
Exclude Linux64: 1
Exclude OSXUniversal: 0
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 1
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: OSX
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: OSXUniversal
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
iPhone: iOS
second:
enabled: 0
settings:
AddToEmbeddedBinaries: false
CPU: AnyCPU
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 65905945bf17474786eeab33b4d9a4f2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 529aa76aac4a4923909c689fe65e8e27
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,81 @@
fileFormatVersion: 2
guid: 5acbca3f877e4a0d99aa2a1279d20e86
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 1
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
Exclude WindowsStoreApps: 0
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: None
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 1
settings:
CPU: ARM
DontProcess: false
PlaceholderPath:
SDK: UWP
ScriptingBackend: AnyScriptingBackend
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6f987c5ad0c74022bbdb94dcdaaed9cd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,81 @@
fileFormatVersion: 2
guid: ea3b2e779884423f9f4080e9b4942503
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 1
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
Exclude WindowsStoreApps: 0
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: None
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 1
settings:
CPU: ARM64
DontProcess: false
PlaceholderPath:
SDK: UWP
ScriptingBackend: AnyScriptingBackend
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1439eb8c9e3a4467983d65eebc86dbca
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,81 @@
fileFormatVersion: 2
guid: 4b155abe2cde470cb1bd71e62036a0e4
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 1
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
Exclude WindowsStoreApps: 0
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: None
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 1
settings:
CPU: X64
DontProcess: false
PlaceholderPath:
SDK: UWP
ScriptingBackend: AnyScriptingBackend
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a354aa937e924d57b3217da49aaf4c9a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 484bfb2da6b648cba1e36eb02dad9680
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,81 @@
fileFormatVersion: 2
guid: f4e82c52b69445c28d34c7172248a92d
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 0
Exclude Linux64: 0
Exclude OSXUniversal: 0
Exclude Win: 1
Exclude Win64: 0
Exclude WindowsStoreApps: 1
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
CPU: x86_64
DefaultValueInitialized: true
OS: Windows
- first:
Standalone: Linux64
second:
enabled: 1
settings:
CPU: x86_64
- first:
Standalone: OSXUniversal
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win64
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
DontProcess: false
PlaceholderPath:
SDK: AnySDK
ScriptingBackend: AnyScriptingBackend
userData:
assetBundleName:
assetBundleVariant: