diff --git a/CHANGELOG.md b/CHANGELOG.md index 52de7ab..513ebd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,19 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [1.0.3] - 2021-03-12 -* Removed preview tag from documentation and UI +## [1.1.1] - 2021-04-06 +* Oculus controller profile now exposes both grip and aim poses. +* Fixed issue where OpenXR layouts were not visible in the InputSystem bindings dialog. +* `OpenXRSettings.renderMode` and `OpenXrSettings.depthSubmissionMode` can now be changed via script outside of play mode. +* Fix for managed stripping levels of Medium and High +* Fixed bugs in `XR_KHR_VULKAN_ENABLE2` extension support +* Added support for `XR_VARJO_QUAD_VIEWS` extension +* Added `XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT` and `XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT` bits to the composition layer flags +* Added `XrSecondaryViewConfigurationSwapchainCreateInfoMSFT` to to `XrSwapchainCreateInfo` when using a secondary view +* MockRuntime First Person Observer View support +* MockRuntime input support +* MockRuntime vulkan_enable2 support +* MockRuntime d3d11_enable support ## [1.0.2] - 2021-02-04 * Resolve further release verification issues. @@ -41,4 +52,3 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [0.1.1-preview.1] - 2020-12-16 ### This is the first release of *OpenXR Plugin \*. - diff --git a/ConformanceAutomation/HelperTypes.cs b/ConformanceAutomation/HelperTypes.cs index ac41624..dfa39ff 100644 --- a/ConformanceAutomation/HelperTypes.cs +++ b/ConformanceAutomation/HelperTypes.cs @@ -33,14 +33,14 @@ public XrVector3f(float x, float y, float z) { this.x = x; this.y = y; - this.z = z; + this.z = -z; } public XrVector3f(Vector3 value) { x = value.x; y = value.y; - z = value.z; + z = -value.z; } }; @@ -53,16 +53,16 @@ struct XrQuaternionf public XrQuaternionf(float x, float y, float z, float w) { - this.x = x; - this.y = y; + this.x = -x; + this.y = -y; this.z = z; this.w = w; } public XrQuaternionf(Quaternion quaternion) { - this.x = quaternion.x; - this.y = quaternion.y; + this.x = -quaternion.x; + this.y = -quaternion.y; this.z = quaternion.z; this.w = quaternion.w; } diff --git a/ConformanceAutomation/Native~/conformance_automation_ext.cpp b/ConformanceAutomation/Native~/conformance_automation_ext.cpp index c2e8c86..c7892f3 100644 --- a/ConformanceAutomation/Native~/conformance_automation_ext.cpp +++ b/ConformanceAutomation/Native~/conformance_automation_ext.cpp @@ -29,12 +29,18 @@ static IUnityXRTrace* s_Trace = nullptr; extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API script_xrSetInputDeviceActiveEXT(XrSession session, XrPath interactionProfile, XrPath topLevelPath, XrBool32 isActive) { + if (nullptr == unity_xrSetInputDeviceActiveEXT) + return false; + return XR_SUCCESS == unity_xrSetInputDeviceActiveEXT(session, interactionProfile, topLevelPath, isActive); } extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API script_xrSetInputDeviceStateBoolEXT(XrSession session, XrPath topLevelPath, XrPath inputSourcePath, XrBool32 state) { + if (nullptr == unity_xrSetInputDeviceStateBoolEXT) + return false; + XrResult result = unity_xrSetInputDeviceStateBoolEXT(session, topLevelPath, inputSourcePath, state); std::string traceString = "[ConformanceAutomationExt] - script_xrSetInputDeviceStateBoolEXT XrResult is "; @@ -51,18 +57,27 @@ script_xrSetInputDeviceStateBoolEXT(XrSession session, XrPath topLevelPath, XrPa extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API script_xrSetInputDeviceStateFloatEXT(XrSession session, XrPath topLevelPath, XrPath inputSourcePath, float state) { + if (nullptr == unity_xrSetInputDeviceStateFloatEXT) + return false; + return XR_SUCCESS == unity_xrSetInputDeviceStateFloatEXT(session, topLevelPath, inputSourcePath, state); } extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API script_xrSetInputDeviceStateVector2fEXT(XrSession session, XrPath topLevelPath, XrPath inputSourcePath, XrVector2f state) { + if (nullptr == unity_xrSetInputDeviceStateVector2fEXT) + return false; + return XR_SUCCESS == unity_xrSetInputDeviceStateVector2fEXT(session, topLevelPath, inputSourcePath, state); } extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API script_xrSetInputDeviceLocationEXT(XrSession session, XrPath topLevelPath, XrPath inputSourcePath, XrSpace space, XrPosef pose) { + if (nullptr == unity_xrSetInputDeviceLocationEXT) + return false; + return XR_SUCCESS == unity_xrSetInputDeviceLocationEXT(session, topLevelPath, inputSourcePath, space, pose); } diff --git a/ConformanceAutomation/android.meta b/ConformanceAutomation/android.meta index aa0eda2..b54e5f6 100644 --- a/ConformanceAutomation/android.meta +++ b/ConformanceAutomation/android.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c98cc07052b944ce904353b643bbad8f +guid: 2aa7ee54b09b45fd9cdea9a3bbf7ab7b folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/ConformanceAutomation/android/arm64.meta b/ConformanceAutomation/android/arm64.meta index 287f8a8..ef0ef75 100644 --- a/ConformanceAutomation/android/arm64.meta +++ b/ConformanceAutomation/android/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 1ce3aefbd4c948b9831d723f64e991f8 +guid: d102457ccbf4469a861c7139b038c8a5 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/ConformanceAutomation/android/arm64/ConformanceAutomationExt.so b/ConformanceAutomation/android/arm64/ConformanceAutomationExt.so index 28e3006..4b64613 100644 Binary files a/ConformanceAutomation/android/arm64/ConformanceAutomationExt.so and b/ConformanceAutomation/android/arm64/ConformanceAutomationExt.so differ diff --git a/ConformanceAutomation/android/arm64/ConformanceAutomationExt.so.meta b/ConformanceAutomation/android/arm64/ConformanceAutomationExt.so.meta index f991c35..ab224aa 100644 --- a/ConformanceAutomation/android/arm64/ConformanceAutomationExt.so.meta +++ b/ConformanceAutomation/android/arm64/ConformanceAutomationExt.so.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c24d598df34c4e50a80578015a7926a9 +guid: da5c575fd4044850931cbba0a92be9d0 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/ConformanceAutomation/universalwindows.meta b/ConformanceAutomation/universalwindows.meta index f2f8c3c..14ad338 100644 --- a/ConformanceAutomation/universalwindows.meta +++ b/ConformanceAutomation/universalwindows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 7c68e4a26cce48eb970b6d4532bebb00 +guid: 98cc959e5f7142aba8f0d8e2cc939f81 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/ConformanceAutomation/universalwindows/arm32.meta b/ConformanceAutomation/universalwindows/arm32.meta new file mode 100644 index 0000000..d2ed6e0 --- /dev/null +++ b/ConformanceAutomation/universalwindows/arm32.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ef393be752de461a892f09f3e4b8dc12 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ConformanceAutomation/universalwindows/arm32/ConformanceAutomationExt.dll b/ConformanceAutomation/universalwindows/arm32/ConformanceAutomationExt.dll new file mode 100644 index 0000000..0a1c325 Binary files /dev/null and b/ConformanceAutomation/universalwindows/arm32/ConformanceAutomationExt.dll differ diff --git a/ConformanceAutomation/universalwindows/arm32/ConformanceAutomationExt.dll.meta b/ConformanceAutomation/universalwindows/arm32/ConformanceAutomationExt.dll.meta new file mode 100644 index 0000000..fa8f652 --- /dev/null +++ b/ConformanceAutomation/universalwindows/arm32/ConformanceAutomationExt.dll.meta @@ -0,0 +1,81 @@ +fileFormatVersion: 2 +guid: 56e352c556b7406c8561aab0acfe8fa0 +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: diff --git a/ConformanceAutomation/universalwindows/arm64.meta b/ConformanceAutomation/universalwindows/arm64.meta index c9e92c4..e8d6bba 100644 --- a/ConformanceAutomation/universalwindows/arm64.meta +++ b/ConformanceAutomation/universalwindows/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8ffe0e7287f240978fd29561be5cf889 +guid: 8ac89febe32549dbb2d66abeb89508be folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/ConformanceAutomation/universalwindows/arm64/ConformanceAutomationExt.dll b/ConformanceAutomation/universalwindows/arm64/ConformanceAutomationExt.dll index 84507cb..5b733fe 100644 Binary files a/ConformanceAutomation/universalwindows/arm64/ConformanceAutomationExt.dll and b/ConformanceAutomation/universalwindows/arm64/ConformanceAutomationExt.dll differ diff --git a/ConformanceAutomation/universalwindows/arm64/ConformanceAutomationExt.dll.meta b/ConformanceAutomation/universalwindows/arm64/ConformanceAutomationExt.dll.meta index 1b8714e..f5cde0b 100644 --- a/ConformanceAutomation/universalwindows/arm64/ConformanceAutomationExt.dll.meta +++ b/ConformanceAutomation/universalwindows/arm64/ConformanceAutomationExt.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: fb7d8ed8a4984241bdb0afa38964a3f8 +guid: 85156a1d90ac4f08baf8410f857dbb5b PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/ConformanceAutomation/universalwindows/x64.meta b/ConformanceAutomation/universalwindows/x64.meta index 6359d73..08fac26 100644 --- a/ConformanceAutomation/universalwindows/x64.meta +++ b/ConformanceAutomation/universalwindows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: daf7d5927bab47a6b3da3ec6f90ac037 +guid: 1be2643db1a9466d80e888b49442492c folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/ConformanceAutomation/universalwindows/x64/ConformanceAutomationExt.dll b/ConformanceAutomation/universalwindows/x64/ConformanceAutomationExt.dll index 72a1e11..b69f5b9 100644 Binary files a/ConformanceAutomation/universalwindows/x64/ConformanceAutomationExt.dll and b/ConformanceAutomation/universalwindows/x64/ConformanceAutomationExt.dll differ diff --git a/ConformanceAutomation/universalwindows/x64/ConformanceAutomationExt.dll.meta b/ConformanceAutomation/universalwindows/x64/ConformanceAutomationExt.dll.meta index 8c8c653..ba266f9 100644 --- a/ConformanceAutomation/universalwindows/x64/ConformanceAutomationExt.dll.meta +++ b/ConformanceAutomation/universalwindows/x64/ConformanceAutomationExt.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3d3927b207b74774b5c1406d243d0374 +guid: b9dba9912440471abc059a5929da2446 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/ConformanceAutomation/windows.meta b/ConformanceAutomation/windows.meta index 1dc57a9..0e7ef51 100644 --- a/ConformanceAutomation/windows.meta +++ b/ConformanceAutomation/windows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f3e26f37a573463381c5747aae6c7be8 +guid: c970b5608fde4d58ad0b092561d82a2b folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/ConformanceAutomation/windows/x64.meta b/ConformanceAutomation/windows/x64.meta index 07d8e60..1e15ffd 100644 --- a/ConformanceAutomation/windows/x64.meta +++ b/ConformanceAutomation/windows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2900a390c09b487abdfc769533573f26 +guid: 59774ef1f71b42d7a918c6be0f5b4e5d folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/ConformanceAutomation/windows/x64/ConformanceAutomationExt.dll b/ConformanceAutomation/windows/x64/ConformanceAutomationExt.dll index c1ae2f8..1322ed6 100644 Binary files a/ConformanceAutomation/windows/x64/ConformanceAutomationExt.dll and b/ConformanceAutomation/windows/x64/ConformanceAutomationExt.dll differ diff --git a/ConformanceAutomation/windows/x64/ConformanceAutomationExt.dll.meta b/ConformanceAutomation/windows/x64/ConformanceAutomationExt.dll.meta index d50a103..3cf9e54 100644 --- a/ConformanceAutomation/windows/x64/ConformanceAutomationExt.dll.meta +++ b/ConformanceAutomation/windows/x64/ConformanceAutomationExt.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 106e2dab2ea448b18ef8779ac6848f0e +guid: 4432efbc4d394e4b9a27672fd8825866 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/Documentation~/features.md b/Documentation~/features.md index 9ea11d4..01bd256 100644 --- a/Documentation~/features.md +++ b/Documentation~/features.md @@ -28,7 +28,7 @@ A feature must also provide an `OpenXRFeature` attribute when running in the Edi ```c# #if UNITY_EDITOR - [Unity.XR.OpenXR.Editor.OpenXRFeature(UiName = "Example Intercept Create Session", + [UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Example Intercept Create Session", BuildTargetGroups = new []{BuildTargetGroup.Standalone, BuildTargetGroup.WSA}, Company = "Unity", Desc = "Example feature extension showing how to intercept a single OpenXR function.", @@ -191,11 +191,15 @@ Any native libraries included in the same directory or a subdirectory of your fe ### Intercepting OpenXR function calls -To intercept OpenXR function calls, override `OpenXRFeature.xrGetInstanceProcAddr`. Returning a different function pointer allows intercepting any OpenXR method. For an example, see `InterceptCreateSessionExt`. +To intercept OpenXR function calls, override `OpenXRFeature.HookGetInstanceProcAddr`. Returning a different function pointer allows intercepting any OpenXR method. For an example, see the `Intercept Feature` sample. + +### Calling OpenXR functions from a feature + +To call an OpenXR function within a feature you first need to retreive a pointer to the function. To do this use the `OpenXRFeature.xrGetInstanceProcAddr` function pointer to request a pointer to the function you want to call. Using `OpenXRFeature.xrGetInstanceProcAddr` to retrieve the function pointer ensures that any intercepted calls set up by features using `OpenXRFeature.HookGetInstanceProcAddr` will be included. ### Providing a Unity subsystem implementation -`OpenXRFeature` provides several XR Loader callbacks where you can manage the lifecycle of Unity subsystems. For an example meshing subsystem feature, see the `MeshingTeapotExt` example (and the package sample that corresponds with it). +`OpenXRFeature` provides several XR Loader callbacks where you can manage the lifecycle of Unity subsystems. For an example meshing subsystem feature, see the `Meshing Subsystem Feature` sample. Note that a `UnitySubsystemsManifest.json` file is required in order for Unity to discover any subsystems you define. At the moment, there are several restrictions around this file: diff --git a/MockDriver/MockDriver.cs b/MockDriver/MockDriver.cs index 410c51a..6a6f7c0 100644 --- a/MockDriver/MockDriver.cs +++ b/MockDriver/MockDriver.cs @@ -29,6 +29,13 @@ internal class MockDriver : OpenXRFeature /// public const string featureId = "com.unity.openxr.feature.mockdriver"; + public delegate void EndFrameDelegate (); + + public static event EndFrameDelegate onEndFrame; + + [AOT.MonoPInvokeCallback(typeof(EndFrameDelegate))] + private static void ReceiveEndFrame() => onEndFrame?.Invoke(); + /// protected override bool OnInstanceCreate(ulong instance) { @@ -39,6 +46,9 @@ protected override bool OnInstanceCreate(ulong instance) } InitializeNative(xrGetInstanceProcAddr, instance, 0ul, 0ul); + + MockDriver_RegisterEndFrameCallback(ReceiveEndFrame); + return true; } @@ -48,6 +58,14 @@ protected override void OnInstanceDestroy(ulong xrInstance) ShutdownNative(0); } + internal enum XrViewConfigurationType + { + PrimaryMono = 1, + PrimaryStereo = 2, + PrimaryQuadVarjo = 1000037000, + SecondaryMonoFirstPersonObserver = 1000054000 + } + [Flags] internal enum XrSpaceLocationFlags { @@ -223,5 +241,11 @@ internal enum XrReferenceSpaceType [DllImport(extLib, EntryPoint = "MockDriver_GetEndFrameStats")] internal static extern XrResult GetEndFrameStats(out int primaryLayerCount, out int secondaryLayerCount); + + [DllImport(extLib, EntryPoint = "MockDriver_ActivateSecondaryView")] + internal static extern XrResult ActivateSecondaryView(XrViewConfigurationType viewConfigurationType, bool activate); + + [DllImport(extLib, EntryPoint = "MockDriver_RegisterEndFrameCallback")] + private static extern XrResult MockDriver_RegisterEndFrameCallback (EndFrameDelegate callback); } } diff --git a/MockDriver/Native~/mock_driver/Include/openxr_mock_driver.h b/MockDriver/Native~/mock_driver/Include/openxr_mock_driver.h index 982a319..4440f65 100644 --- a/MockDriver/Native~/mock_driver/Include/openxr_mock_driver.h +++ b/MockDriver/Native~/mock_driver/Include/openxr_mock_driver.h @@ -46,4 +46,14 @@ typedef XrResult(XRAPI_PTR* PFN_xrSetViewPoseUNITY)(int viewIndex, XrPosef pose, // typedef XrResult(XRAPI_PTR* PFN_xrGetEndFrameStatsUNITY)(int* primaryLayerCount, int* secondaryLayerCount); +// Activate a secondary view configuration +// +typedef XrResult(XRAPI_PTR* PFN_xrActivateSecondaryViewUNITY)(XrViewConfigurationType viewConfigurationType, bool activate); + +// Register a callback that gets called after every end frame +// +typedef void (*PFN_EndFrameCallback)(); + +typedef XrResult(XRAPI_PTR* PFN_xrRegisterEndFrameCallback)(PFN_EndFrameCallback callback); + #endif //OPENXR_MOCK_DRIVER \ No newline at end of file diff --git a/MockDriver/Native~/mock_driver/mock_driver.cpp b/MockDriver/Native~/mock_driver/mock_driver.cpp index 45e2ec3..3e098e8 100644 --- a/MockDriver/Native~/mock_driver/mock_driver.cpp +++ b/MockDriver/Native~/mock_driver/mock_driver.cpp @@ -30,6 +30,8 @@ struct DriverContext PFN_xrSetSpacePoseUNITY xrSetSpacePoseUNITY; PFN_xrSetViewPoseUNITY xrSetViewPoseUNITY; PFN_xrGetEndFrameStatsUNITY xrGetEndFrameStatsUNITY; + PFN_xrActivateSecondaryViewUNITY xrActivateSecondaryViewUNITY; + PFN_xrRegisterEndFrameCallback xrRegisterEndFrameCallbackUNITY; } s_DriverContext{}; extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API @@ -44,6 +46,8 @@ script_initialize(PFN_xrGetInstanceProcAddr xrGetInstanceProcAddr, XrInstance in CHECK_XRCMD(xrGetInstanceProcAddr(instance, "xrSetSpacePoseUNITY", (PFN_xrVoidFunction*)&s_DriverContext.xrSetSpacePoseUNITY)); CHECK_XRCMD(xrGetInstanceProcAddr(instance, "xrSetViewPoseUNITY", (PFN_xrVoidFunction*)&s_DriverContext.xrSetViewPoseUNITY)); CHECK_XRCMD(xrGetInstanceProcAddr(instance, "xrGetEndFrameStatsUNITY", (PFN_xrVoidFunction*)&s_DriverContext.xrGetEndFrameStatsUNITY)); + CHECK_XRCMD(xrGetInstanceProcAddr(instance, "xrActivateSecondaryViewUNITY", (PFN_xrVoidFunction*)&s_DriverContext.xrActivateSecondaryViewUNITY)); + CHECK_XRCMD(xrGetInstanceProcAddr(instance, "xrRegisterEndFrameCallbackUNITY", (PFN_xrVoidFunction*)&s_DriverContext.xrRegisterEndFrameCallbackUNITY)); } extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API @@ -127,4 +131,20 @@ extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR MockDriver_GetEndFrameStats return XR_ERROR_EXTENSION_NOT_PRESENT; } +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR MockDriver_ActivateSecondaryView(XrViewConfigurationType viewConfigurationType, bool activate) +{ + if (s_DriverContext.xrActivateSecondaryViewUNITY) + return s_DriverContext.xrActivateSecondaryViewUNITY(viewConfigurationType, activate); + + return XR_ERROR_EXTENSION_NOT_PRESENT; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR MockDriver_RegisterEndFrameCallback(PFN_EndFrameCallback callback) +{ + if (s_DriverContext.xrRegisterEndFrameCallbackUNITY) + return s_DriverContext.xrRegisterEndFrameCallbackUNITY(callback); + + return XR_ERROR_EXTENSION_NOT_PRESENT; +} + #undef DEBUG_LOG_EVERY_FUNC_CALL diff --git a/MockDriver/windows.meta b/MockDriver/windows.meta index 70387e2..4d779eb 100644 --- a/MockDriver/windows.meta +++ b/MockDriver/windows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e4281ce891024100a68fea873562b085 +guid: 0093503a42c14c88a5c40c0db0285b92 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/MockDriver/windows/x64.meta b/MockDriver/windows/x64.meta index 325d331..5f8dc01 100644 --- a/MockDriver/windows/x64.meta +++ b/MockDriver/windows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 7636329fb1504601aa51873d343ae561 +guid: 2e247e5e3e6c4dd19b0cb1982735aff9 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/MockDriver/windows/x64/mock_driver.dll b/MockDriver/windows/x64/mock_driver.dll index 1273071..07f2fc0 100644 Binary files a/MockDriver/windows/x64/mock_driver.dll and b/MockDriver/windows/x64/mock_driver.dll differ diff --git a/MockDriver/windows/x64/mock_driver.dll.meta b/MockDriver/windows/x64/mock_driver.dll.meta index 79bd6ed..e118131 100644 --- a/MockDriver/windows/x64/mock_driver.dll.meta +++ b/MockDriver/windows/x64/mock_driver.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8c05af59db9f46109a611882ef4908d7 +guid: 0fae10090bc646bba635fca2ac74e09a PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/MockRuntime/MockRuntime.cs b/MockRuntime/MockRuntime.cs index e4bf001..1e03281 100644 --- a/MockRuntime/MockRuntime.cs +++ b/MockRuntime/MockRuntime.cs @@ -20,7 +20,7 @@ namespace UnityEngine.XR.OpenXR.Features.Mock DocumentationLink = "https://docs.unity3d.com/Packages/com.unity.xr.openxr@0.1/manual/index.html", CustomRuntimeLoaderBuildTargets = new [] { UnityEditor.BuildTarget.StandaloneWindows64, UnityEditor.BuildTarget.StandaloneOSX }, OpenxrExtensionStrings = MockRuntime.XR_UNITY_null_gfx, - Version = "0.0.1", + Version = "0.0.2", FeatureId = featureId)] #endif internal class MockRuntime : OpenXRFeature diff --git a/MockRuntime/Native~/openxr_loader/Extensions/mock_conformance_automation.cpp b/MockRuntime/Native~/openxr_loader/Extensions/mock_conformance_automation.cpp new file mode 100644 index 0000000..8580b73 --- /dev/null +++ b/MockRuntime/Native~/openxr_loader/Extensions/mock_conformance_automation.cpp @@ -0,0 +1,101 @@ +#include "../mock.h" + +#define CHECK_EXT() \ + if (nullptr == s_ext) \ + return XR_ERROR_FUNCTION_UNSUPPORTED; + +struct ConformanceAutomation +{ + std::map states; + + MockInputState& GetState(XrPath path, XrActionType actionType) + { + auto it = states.find(path); + if (it != states.end()) + return it->second; + + MockInputState& state = states[path]; + state.type = actionType; + return state; + } +}; + +static ConformanceAutomation* s_ext = nullptr; + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrSetInputDeviceActiveEXT(XrSession session, XrPath interactionProfile, XrPath topLevelPath, XrBool32 isActive) +{ + LOG_FUNC(); + CHECK_EXT(); + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrSetInputDeviceStateBoolEXT(XrSession session, XrPath topLevelPath, XrPath inputSourcePath, XrBool32 state) +{ + LOG_FUNC(); + CHECK_EXT(); + s_ext->GetState(inputSourcePath, XR_ACTION_TYPE_BOOLEAN_INPUT).Set(state); + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrSetInputDeviceStateFloatEXT(XrSession session, XrPath topLevelPath, XrPath inputSourcePath, float state) +{ + LOG_FUNC(); + CHECK_EXT(); + CHECK_SESSION(session); + s_ext->GetState(inputSourcePath, XR_ACTION_TYPE_FLOAT_INPUT).Set(state); + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrSetInputDeviceStateVector2fEXT(XrSession session, XrPath topLevelPath, XrPath inputSourcePath, XrVector2f state) +{ + LOG_FUNC(); + CHECK_EXT(); + s_ext->GetState(inputSourcePath, XR_ACTION_TYPE_VECTOR2F_INPUT).Set(state); + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrSetInputDeviceLocationEXT(XrSession session, XrPath topLevelPath, XrPath inputSourcePath, XrSpace space, XrPosef pose) +{ + LOG_FUNC(); + CHECK_EXT(); + s_ext->GetState(inputSourcePath, XR_ACTION_TYPE_POSE_INPUT).Set(space, pose); + return XR_SUCCESS; +} + +void ConformanceAutomation_Create() +{ + // Make sure there is no orphaned extension + ConformanceAutomation_Destroy(); + + s_ext = new ConformanceAutomation(); +} + +void ConformanceAutomation_Destroy() +{ + delete s_ext; + s_ext = nullptr; +} + +XrResult ConformanceAutomation_GetInstanceProcAddr(const char* name, PFN_xrVoidFunction* function) +{ + CHECK_EXT(); + GET_PROC_ADDRESS(xrSetInputDeviceActiveEXT) + GET_PROC_ADDRESS(xrSetInputDeviceStateBoolEXT) + GET_PROC_ADDRESS(xrSetInputDeviceStateFloatEXT) + GET_PROC_ADDRESS(xrSetInputDeviceStateVector2fEXT) + GET_PROC_ADDRESS(xrSetInputDeviceLocationEXT) + return XR_ERROR_FUNCTION_UNSUPPORTED; +} + +XrResult ConformanceAutomation_GetInputState(MockInputState* state) +{ + CHECK_EXT(); + + auto it = s_ext->states.find(state->path); + if (it == s_ext->states.end()) + return XR_ERROR_HANDLE_INVALID; + + state->CopyValue(it->second); + + return XR_SUCCESS; +} diff --git a/MockRuntime/Native~/openxr_loader/Extensions/mock_d3d11_enable.cpp b/MockRuntime/Native~/openxr_loader/Extensions/mock_d3d11_enable.cpp new file mode 100644 index 0000000..35b6fbb --- /dev/null +++ b/MockRuntime/Native~/openxr_loader/Extensions/mock_d3d11_enable.cpp @@ -0,0 +1,182 @@ +#include "../mock.h" + +#if defined(XR_USE_GRAPHICS_API_D3D11) + +struct MockD3D11 +{ + ID3D11Device* device = nullptr; +}; + +static MockD3D11 s_MockD3D11 = {}; + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetD3D11GraphicsRequirementsKHR( + XrInstance instance, + XrSystemId systemId, + XrGraphicsRequirementsD3D11KHR* graphicsRequirements) +{ + IDXGIAdapter* pAdapter; + std::vector vAdapters; + IDXGIFactory* pFactory = NULL; + + // Create a DXGIFactory object. + if (FAILED(CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&pFactory))) + return XR_ERROR_RUNTIME_FAILURE; + + XrResult xrresult = XR_ERROR_RUNTIME_FAILURE; + graphicsRequirements->adapterLuid = {}; + + for (UINT i = 0; pFactory->EnumAdapters(i, &pAdapter) != DXGI_ERROR_NOT_FOUND; ++i) + { + DXGI_ADAPTER_DESC desc = {}; + pAdapter->GetDesc(&desc); + graphicsRequirements->adapterLuid = desc.AdapterLuid; + xrresult = XR_SUCCESS; + break; + } + + if (pFactory) + pFactory->Release(); + + return xrresult; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR MockD3D11_xrEnumerateSwapchainFormats(XrSession session, uint32_t formatCapacityInput, uint32_t* formatCountOutput, int64_t* formats) +{ + if (nullptr == formatCountOutput) + return XR_ERROR_VALIDATION_FAILURE; + + *formatCountOutput = 6; + + if (formatCapacityInput == 0) + return XR_SUCCESS; + + if (nullptr == formats) + return XR_ERROR_VALIDATION_FAILURE; + + if (formatCapacityInput < 6) + return XR_ERROR_SIZE_INSUFFICIENT; + + formats[0] = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + formats[1] = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + formats[2] = DXGI_FORMAT_R8G8B8A8_UNORM; + formats[3] = DXGI_FORMAT_B8G8R8A8_UNORM; + formats[4] = DXGI_FORMAT_D16_UNORM; + formats[5] = DXGI_FORMAT_D32_FLOAT_S8X24_UINT; + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR MockD3D11_xrCreateSwapchain(XrSession session, const XrSwapchainCreateInfo* createInfo, XrSwapchain* swapchain) +{ + LOG_FUNC(); + + if (nullptr == s_MockD3D11.device) + return XR_ERROR_RUNTIME_FAILURE; + + ID3D11Texture2D* texture; + D3D11_TEXTURE2D_DESC desc = {}; + desc.Width = createInfo->width; + desc.Height = createInfo->height; + desc.MipLevels = createInfo->mipCount; + desc.ArraySize = createInfo->arraySize; + desc.SampleDesc.Count = createInfo->sampleCount; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + + if (createInfo->usageFlags & XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) + { + if (createInfo->format == DXGI_FORMAT_D16_UNORM) + desc.Format = DXGI_FORMAT_R16_TYPELESS; + else + desc.Format = DXGI_FORMAT_R32G8X24_TYPELESS; + + desc.BindFlags |= D3D11_BIND_DEPTH_STENCIL; + } + else + { + desc.BindFlags |= D3D11_BIND_RENDER_TARGET; + desc.Format = DXGI_FORMAT_R8G8B8A8_TYPELESS; + desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; + } + + if (FAILED(s_MockD3D11.device->CreateTexture2D(&desc, NULL, &texture))) + return XR_ERROR_RUNTIME_FAILURE; + + *swapchain = (XrSwapchain)texture; + + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR MockD3D11_xrDestroySwapChain(XrSwapchain swapchain) +{ + ID3D11Texture2D* texture = (ID3D11Texture2D*)swapchain; + texture->Release(); + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR MockD3D11_xrEnumerateSwapchainImages(XrSwapchain swapchain, uint32_t imageCapacityInput, uint32_t* imageCountOutput, XrSwapchainImageBaseHeader* images) +{ + LOG_FUNC(); + + *imageCountOutput = 1; + + if (images == nullptr) + return XR_SUCCESS; + + if (imageCapacityInput < *imageCountOutput) + return XR_ERROR_VALIDATION_FAILURE; + + XrSwapchainImageD3D11KHR* d3d11images = (XrSwapchainImageD3D11KHR*)images; + d3d11images[0].texture = (ID3D11Texture2D*)swapchain; + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR MockD3D11_xrAcquireSwapchainImage(XrSwapchain swapchain, const XrSwapchainImageAcquireInfo* acquireInfo, uint32_t* index) +{ + LOG_FUNC(); + + *index = 0; + + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR MockD3D11_xrReleaseSwapchainImage(XrSwapchain swapchain, const XrSwapchainImageReleaseInfo* releaseInfo) +{ + LOG_FUNC(); + return XR_SUCCESS; +} + +/// +/// Hook xrCreateSession to get the necessary D3D11 handles +/// +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrCreateSession(XrInstance instance, const XrSessionCreateInfo* createInfo, XrSession* session); +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR MockD3D11_xrCreateSession(XrInstance instance, const XrSessionCreateInfo* createInfo, XrSession* session) +{ + s_MockD3D11 = {}; + + if (createInfo->next != nullptr) + { + XrGraphicsBindingD3D11KHR* bindings = (XrGraphicsBindingD3D11KHR*)createInfo->next; + if (bindings->type == XR_TYPE_GRAPHICS_BINDING_D3D11_KHR) + { + s_MockD3D11.device = bindings->device; + } + } + + return xrCreateSession(instance, createInfo, session); +} + +XrResult MockD3D11_GetInstanceProcAddr(const char* name, PFN_xrVoidFunction* function) +{ + GET_PROC_ADDRESS_REMAP(xrCreateSession, MockD3D11_xrCreateSession) + GET_PROC_ADDRESS_REMAP(xrEnumerateSwapchainFormats, MockD3D11_xrEnumerateSwapchainFormats) + GET_PROC_ADDRESS_REMAP(xrCreateSwapchain, MockD3D11_xrCreateSwapchain) + GET_PROC_ADDRESS_REMAP(xrDestroySwapChain, MockD3D11_xrDestroySwapChain) + GET_PROC_ADDRESS_REMAP(xrEnumerateSwapchainImages, MockD3D11_xrEnumerateSwapchainImages) + GET_PROC_ADDRESS_REMAP(xrAcquireSwapchainImage, MockD3D11_xrAcquireSwapchainImage) + GET_PROC_ADDRESS_REMAP(xrReleaseSwapchainImage, MockD3D11_xrReleaseSwapchainImage) + GET_PROC_ADDRESS(xrGetD3D11GraphicsRequirementsKHR) + return XR_ERROR_FUNCTION_UNSUPPORTED; +} + +#endif diff --git a/MockRuntime/Native~/openxr_loader/mock_driver_extension.cpp b/MockRuntime/Native~/openxr_loader/Extensions/mock_driver_extension.cpp similarity index 59% rename from MockRuntime/Native~/openxr_loader/mock_driver_extension.cpp rename to MockRuntime/Native~/openxr_loader/Extensions/mock_driver_extension.cpp index 67b873e..0217ca4 100644 --- a/MockRuntime/Native~/openxr_loader/mock_driver_extension.cpp +++ b/MockRuntime/Native~/openxr_loader/Extensions/mock_driver_extension.cpp @@ -1,49 +1,41 @@ -#include "IUnityInterface.h" -#include "XR/IUnityXRTrace.h" - -#define DEBUG_LOG_EVERY_FUNC_CALL 1 -#define DEBUG_TRACE 1 - -#include "mock.h" -#include "mock_state.h" - -#include "mock_driver_extension.h" - -#include +#include "../mock.h" extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrRequestExitSession(XrSession session); extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrTransitionMockToStateUNITY(XrSession session, XrSessionState requestedState, bool forceTransition) { LOG_FUNC(); + CHECK_SESSION(session); + TRACE("[Mock] [Driver] Transition request to state %d with force %s\n", requestedState, forceTransition ? "TRUE" : "FALSE"); - if (!forceTransition && !IsStateTransitionValid(session, requestedState)) + if (!forceTransition && !s_runtime->IsStateTransitionValid(requestedState)) { TRACE("[Mock] [Driver] Failed to request state. Was transition valid: %s with force %s\n", - IsStateTransitionValid(session, requestedState) ? "TRUE" : "FALSE", + s_runtime->IsStateTransitionValid(requestedState) ? "TRUE" : "FALSE", forceTransition ? "TRUE" : "FALSE"); return XR_ERROR_VALIDATION_FAILURE; } TRACE("[Mock] [Driver] Transitioning to requested state %d\n", requestedState); - ChangeSessionState(session, requestedState); + s_runtime->ChangeSessionState(requestedState); return XR_SUCCESS; } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrSetReturnCodeForFunctionUNITY(const char* functionName, XrResult result) { LOG_FUNC(); - SetExpectedResultForFunction(functionName, result); + s_runtime->SetExpectedResultForFunction(functionName, result); return XR_SUCCESS; } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrRequestExitSessionUNITY(XrSession session) { LOG_FUNC(); - if (IsSessionAtCurrentState(session, XR_SESSION_STATE_READY) || - IsSessionAtCurrentState(session, XR_SESSION_STATE_SYNCHRONIZED) || - IsSessionAtCurrentState(session, XR_SESSION_STATE_VISIBLE) || - IsSessionAtCurrentState(session, XR_SESSION_STATE_FOCUSED)) + CHECK_SESSION(session); + if (s_runtime->IsSessionState(XR_SESSION_STATE_READY) || + s_runtime->IsSessionState(XR_SESSION_STATE_SYNCHRONIZED) || + s_runtime->IsSessionState(XR_SESSION_STATE_VISIBLE) || + s_runtime->IsSessionState(XR_SESSION_STATE_FOCUSED)) return xrRequestExitSession(session); return XR_ERROR_VALIDATION_FAILURE; @@ -51,40 +43,59 @@ extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrRequestExitSessionUNITY(X extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrSetBlendModeUNITY(XrEnvironmentBlendMode blendMode) { - SetMockBlendMode(blendMode); + CHECK_RUNTIME(); + s_runtime->SetMockBlendMode(blendMode); return XR_SUCCESS; } +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrActivateSecondaryViewUNITY(XrViewConfigurationType viewConfigurationType, bool activate) +{ + CHECK_RUNTIME(); + return s_runtime->ActivateSecondaryView(viewConfigurationType, activate); +} + XrResult xrSetReferenceSpaceBoundsRectUNITY(XrSession session, XrReferenceSpaceType referenceSpace, const XrExtent2Df bounds) { LOG_FUNC(); - SetExtentsForReferenceSpace(session, referenceSpace, bounds); + CHECK_SESSION(session); + s_runtime->SetExtentsForReferenceSpace(referenceSpace, bounds); return XR_SUCCESS; } XrResult xrCauseInstanceLossUNITY(XrInstance instance) { - return CauseInstanceLoss(instance); + CHECK_INSTANCE(instance); + s_runtime->CauseInstanceLoss(); + return XR_SUCCESS; } XrResult xrSetSpacePoseUNITY(XrPosef pose, XrSpaceLocationFlags locationFlags) { - SetSpacePose(pose, locationFlags); + CHECK_RUNTIME(); + s_runtime->SetSpacePose(pose, locationFlags); return XR_SUCCESS; } XrResult xrSetViewPoseUNITY(int viewIndex, XrPosef pose, XrFovf fov, XrViewStateFlags viewStateFlags) { - SetViewPose(viewIndex, pose, fov, viewStateFlags); + CHECK_RUNTIME(); + s_runtime->SetViewPose(viewIndex, pose, fov, viewStateFlags); return XR_SUCCESS; } XrResult xrGetEndFrameStatsUNITY(int* primaryLayerCount, int* secondaryLayerCount) { - return GetEndFrameStats(primaryLayerCount, secondaryLayerCount); + CHECK_RUNTIME(); + return s_runtime->GetEndFrameStats(primaryLayerCount, secondaryLayerCount); +} + +XrResult xrRegisterEndFrameCallbackUNITY(PFN_EndFrameCallback callback) +{ + CHECK_RUNTIME(); + return s_runtime->RegisterEndFrameCallback(callback); } -XrResult mock_driver_xrGetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function) +XrResult MockDriver_GetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function) { #define LOOKUP(funcName) \ if (strcmp(#funcName, name) == 0) \ @@ -97,11 +108,13 @@ XrResult mock_driver_xrGetInstanceProcAddr(XrInstance instance, const char* name LOOKUP(xrSetReturnCodeForFunctionUNITY) LOOKUP(xrRequestExitSessionUNITY) LOOKUP(xrSetBlendModeUNITY) + LOOKUP(xrActivateSecondaryViewUNITY) LOOKUP(xrSetReferenceSpaceBoundsRectUNITY) LOOKUP(xrCauseInstanceLossUNITY); LOOKUP(xrSetSpacePoseUNITY); LOOKUP(xrSetViewPoseUNITY); LOOKUP(xrGetEndFrameStatsUNITY); + LOOKUP(xrRegisterEndFrameCallbackUNITY); #undef LOOKUP diff --git a/MockRuntime/Native~/openxr_loader/Extensions/mock_msft_first_person_observer.cpp b/MockRuntime/Native~/openxr_loader/Extensions/mock_msft_first_person_observer.cpp new file mode 100644 index 0000000..500e2bd --- /dev/null +++ b/MockRuntime/Native~/openxr_loader/Extensions/mock_msft_first_person_observer.cpp @@ -0,0 +1,21 @@ +#include "../mock.h" + +XrResult MockRuntime::MSFTFirstPersonObserver_Init() +{ + MockViewConfiguration firstPersonConfig = {}; + firstPersonConfig.primary = false; + firstPersonConfig.enabled = false; + firstPersonConfig.active = false; + firstPersonConfig.stateFlags = XR_VIEW_STATE_ORIENTATION_TRACKED_BIT | + XR_VIEW_STATE_ORIENTATION_VALID_BIT | + XR_VIEW_STATE_POSITION_TRACKED_BIT | + XR_VIEW_STATE_POSITION_VALID_BIT; + firstPersonConfig.views = { + {viewConfigurations[XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO].views[0].configuration, + {{0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 2.0f, 0.0f}}, + {-0.995535672f, 0.995566666f, 0.954059243f, -0.954661012f}}}; + + viewConfigurations[XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT] = firstPersonConfig; + + return XR_SUCCESS; +} \ No newline at end of file diff --git a/MockRuntime/Native~/openxr_loader/Extensions/mock_msft_secondary_view_configuration.cpp b/MockRuntime/Native~/openxr_loader/Extensions/mock_msft_secondary_view_configuration.cpp new file mode 100644 index 0000000..b5a5690 --- /dev/null +++ b/MockRuntime/Native~/openxr_loader/Extensions/mock_msft_secondary_view_configuration.cpp @@ -0,0 +1,102 @@ +#include "../mock.h" + +XrResult MockRuntime::MSFTSecondaryViewConfiguration_BeginSession(const XrSessionBeginInfo* beginInfo) +{ + // Check for secondary view configuration + const XrSecondaryViewConfigurationSessionBeginInfoMSFT* secondaryViewConfiguration = + FindNextPointerType(beginInfo, XR_TYPE_SECONDARY_VIEW_CONFIGURATION_SESSION_BEGIN_INFO_MSFT); + + if (nullptr == secondaryViewConfiguration) + return XR_SUCCESS; + + if (secondaryViewConfiguration->viewConfigurationCount == 0) + return XR_ERROR_VALIDATION_FAILURE; + + if (secondaryViewConfiguration->enabledViewConfigurationTypes == nullptr) + return XR_ERROR_VALIDATION_FAILURE; + + secondaryViewConfigurationStates.reserve(secondaryViewConfiguration->viewConfigurationCount); + for (uint32_t i = 0; i < secondaryViewConfiguration->viewConfigurationCount; i++) + { + MockViewConfiguration* viewConfiguration = GetMockViewConfiguration(secondaryViewConfiguration->enabledViewConfigurationTypes[i]); + if (nullptr == viewConfiguration) + return XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED; + + if (viewConfiguration->primary || viewConfiguration->enabled) + return XR_ERROR_VALIDATION_FAILURE; + + viewConfiguration->enabled = true; + + XrSecondaryViewConfigurationStateMSFT viewState = {XR_TYPE_SECONDARY_VIEW_CONFIGURATION_STATE_MSFT}; + viewState.viewConfigurationType = secondaryViewConfiguration->enabledViewConfigurationTypes[i]; + viewState.active = viewConfiguration->active; + secondaryViewConfigurationStates.push_back(viewState); + } + + return XR_SUCCESS; +} + +XrResult MockRuntime::MSFTSecondaryViewConfiguration_WaitFrame(const XrFrameWaitInfo* frameWaitInfo, XrFrameState* frameState) +{ + // Check for secondary view configuration + XrSecondaryViewConfigurationFrameStateMSFT* secondaryFrameState = + FindNextPointerType(frameState, XR_TYPE_SECONDARY_VIEW_CONFIGURATION_FRAME_STATE_MSFT); + + if (secondaryFrameState == nullptr) + return XR_SUCCESS; + + if (secondaryFrameState->viewConfigurationCount == 0 || secondaryFrameState->viewConfigurationStates == nullptr) + return XR_ERROR_VALIDATION_FAILURE; + + if (secondaryFrameState->viewConfigurationCount != (uint32_t)secondaryViewConfigurationStates.size()) + return XR_ERROR_VALIDATION_FAILURE; + + for (auto& viewConfiguration : secondaryViewConfigurationStates) + viewConfiguration.active = GetMockViewConfiguration(viewConfiguration.viewConfigurationType)->active; + + memcpy(secondaryFrameState->viewConfigurationStates, secondaryViewConfigurationStates.data(), secondaryViewConfigurationStates.size() * sizeof(XrSecondaryViewConfigurationStateMSFT)); + + return XR_SUCCESS; +} + +XrResult MockRuntime::MSFTSecondaryViewConfiguration_EndFrame(const XrFrameEndInfo* frameEndInfo) +{ + const XrSecondaryViewConfigurationFrameEndInfoMSFT* secondaryFrameEndInfo = + FindNextPointerType(frameEndInfo, XR_TYPE_SECONDARY_VIEW_CONFIGURATION_FRAME_END_INFO_MSFT); + + if (secondaryFrameEndInfo == nullptr) + return XR_SUCCESS; + + if (secondaryFrameEndInfo->viewConfigurationCount == 0 || secondaryFrameEndInfo->viewConfigurationLayersInfo == nullptr) + return XR_ERROR_VALIDATION_FAILURE; + + for (uint32_t i = 0; i < secondaryFrameEndInfo->viewConfigurationCount; i++) + { + auto& info = secondaryFrameEndInfo->viewConfigurationLayersInfo[0]; + MockViewConfiguration* viewConfiguration = GetMockViewConfiguration(info.viewConfigurationType); + if (viewConfiguration->primary) + return XR_ERROR_LAYER_INVALID; + if (!viewConfiguration->enabled) + return XR_ERROR_SECONDARY_VIEW_CONFIGURATION_TYPE_NOT_ENABLED_MSFT; + if (!viewConfiguration->active) + continue; + + secondaryLayersRendered += info.layerCount; + } + + return XR_SUCCESS; +} + +XrResult MockRuntime::ActivateSecondaryView(XrViewConfigurationType viewConfigurationType, bool active) +{ + MockViewConfiguration* viewConfiguration = GetMockViewConfiguration(viewConfigurationType); + if (nullptr == viewConfiguration || (IsSessionRunning() && !viewConfiguration->enabled)) + return XR_ERROR_SECONDARY_VIEW_CONFIGURATION_TYPE_NOT_ENABLED_MSFT; + + if (viewConfiguration->primary) + return XR_ERROR_VALIDATION_FAILURE; + + viewConfiguration->active = active; + + return XR_SUCCESS; +} diff --git a/MockRuntime/Native~/openxr_loader/Extensions/mock_vulkan_enable2.cpp b/MockRuntime/Native~/openxr_loader/Extensions/mock_vulkan_enable2.cpp new file mode 100644 index 0000000..2314a2a --- /dev/null +++ b/MockRuntime/Native~/openxr_loader/Extensions/mock_vulkan_enable2.cpp @@ -0,0 +1,310 @@ +#include "../mock.h" + +#if defined(XR_USE_GRAPHICS_API_VULKAN) + +struct MockVulkan +{ + VkInstance instance = VK_NULL_HANDLE; + VkPhysicalDevice phsyicalDevice = VK_NULL_HANDLE; + VkDevice device = VK_NULL_HANDLE; + VkQueue queue = VK_NULL_HANDLE; + VkPhysicalDeviceMemoryProperties memoryProperties = {}; + + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = nullptr; + PFN_vkAllocateMemory vkAllocateMemory = nullptr; + PFN_vkCreateImage vkCreateImage = nullptr; + PFN_vkCreateDevice vkCreateDevice = nullptr; + PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices = nullptr; + PFN_vkBindImageMemory vkBindImageMemory = nullptr; + PFN_vkDestroyImage vkDestroyImage = nullptr; + PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements = nullptr; + PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties = nullptr; +}; + +static MockVulkan s_MockVulkan = {}; + +static bool GetVulkanMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties, uint32_t* index) +{ + for (uint32_t i = 0; i < s_MockVulkan.memoryProperties.memoryTypeCount; i++, typeBits >>= 1) + { + // Only consider memory types included in the typeBits mask + if ((typeBits & 1) == 0) + continue; + + // If all requested properties are a match then return this memory type + if ((s_MockVulkan.memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) + { + *index = i; + return true; + } + } + + return false; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetVulkanInstanceExtensionsKHR(XrInstance instance, XrSystemId systemId, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer) +{ + if (nullptr == bufferCountOutput) + return XR_ERROR_VALIDATION_FAILURE; + + *bufferCountOutput = 0; + + if (bufferCapacityInput > 0 && buffer == nullptr) + return XR_ERROR_VALIDATION_FAILURE; + + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetVulkanDeviceExtensionsKHR(XrInstance instance, XrSystemId systemId, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer) +{ + if (nullptr == bufferCountOutput) + return XR_ERROR_VALIDATION_FAILURE; + + *bufferCountOutput = 0; + + if (bufferCapacityInput > 0 && buffer == nullptr) + return XR_ERROR_VALIDATION_FAILURE; + + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetVulkanGraphicsDeviceKHR(XrInstance instance, XrSystemId systemId, VkInstance vkInstance, VkPhysicalDevice* vkPhysicalDevice) +{ + *vkPhysicalDevice = s_MockVulkan.phsyicalDevice; + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrCreateVulkanInstanceKHR( + XrInstance instance, + const XrVulkanInstanceCreateInfoKHR* createInfo, + VkInstance* vulkanInstance, + VkResult* vulkanResult) +{ + s_MockVulkan = {}; + s_MockVulkan.vkGetInstanceProcAddr = createInfo->pfnGetInstanceProcAddr; + + if (s_MockVulkan.vkGetInstanceProcAddr == nullptr) + return XR_ERROR_RUNTIME_FAILURE; + + PFN_vkCreateInstance vkCreateInstance = (PFN_vkCreateInstance)s_MockVulkan.vkGetInstanceProcAddr(nullptr, "vkCreateInstance"); + if (nullptr == vkCreateInstance) + return XR_ERROR_RUNTIME_FAILURE; + + if (VK_SUCCESS != vkCreateInstance(createInfo->vulkanCreateInfo, createInfo->vulkanAllocator, vulkanInstance)) + return XR_ERROR_RUNTIME_FAILURE; + +#define VK_GET_PROC_ADDR(func) s_MockVulkan.func = (PFN_##func)s_MockVulkan.vkGetInstanceProcAddr(*vulkanInstance, #func); + VK_GET_PROC_ADDR(vkCreateImage) + VK_GET_PROC_ADDR(vkCreateDevice) + VK_GET_PROC_ADDR(vkDestroyImage) + VK_GET_PROC_ADDR(vkGetPhysicalDeviceMemoryProperties) + VK_GET_PROC_ADDR(vkGetImageMemoryRequirements) + VK_GET_PROC_ADDR(vkAllocateMemory) + VK_GET_PROC_ADDR(vkBindImageMemory) + VK_GET_PROC_ADDR(vkEnumeratePhysicalDevices) +#undef VK_GET_PROC_ADDR + + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrCreateVulkanDeviceKHR(XrInstance instance, const XrVulkanDeviceCreateInfoKHR* createInfo, VkDevice* device, VkResult* result) +{ + *result = s_MockVulkan.vkCreateDevice(createInfo->vulkanPhysicalDevice, createInfo->vulkanCreateInfo, createInfo->vulkanAllocator, device); + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetVulkanGraphicsRequirements2KHR(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsVulkanKHR* graphicsRequirements) +{ + graphicsRequirements->minApiVersionSupported = VK_MAKE_VERSION(0, 0, 0); + graphicsRequirements->maxApiVersionSupported = VK_MAKE_VERSION(255, 255, 255); + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetVulkanGraphicsDevice2KHR( + XrInstance instance, + const XrVulkanGraphicsDeviceGetInfoKHR* getInfo, + VkPhysicalDevice* vulkanPhysicalDevice) +{ + if (nullptr == s_MockVulkan.vkEnumeratePhysicalDevices) + { + *vulkanPhysicalDevice = nullptr; + return XR_SUCCESS; + } + + uint32_t physicalDeviceCount = 0; + s_MockVulkan.vkEnumeratePhysicalDevices(getInfo->vulkanInstance, &physicalDeviceCount, nullptr); + if (physicalDeviceCount == 0) + return XR_ERROR_RUNTIME_FAILURE; + + std::vector physicalDevices; + physicalDevices.resize(physicalDeviceCount); + s_MockVulkan.vkEnumeratePhysicalDevices(getInfo->vulkanInstance, &physicalDeviceCount, physicalDevices.data()); + if (physicalDeviceCount != (uint32_t)physicalDevices.size()) + return XR_ERROR_RUNTIME_FAILURE; + + *vulkanPhysicalDevice = physicalDevices[0]; + + return XR_SUCCESS; +} + +/// +/// Hook xrCreateSession to get the necessary vulkan handles +/// +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrCreateSession(XrInstance instance, const XrSessionCreateInfo* createInfo, XrSession* session); +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR MockVulkan_xrCreateSession(XrInstance instance, const XrSessionCreateInfo* createInfo, XrSession* session) +{ + if (createInfo->next != nullptr) + { + XrGraphicsBindingVulkanKHR* bindings = (XrGraphicsBindingVulkanKHR*)createInfo->next; + if (bindings->type == XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR) + { + s_MockVulkan.device = bindings->device; + s_MockVulkan.instance = bindings->instance; + s_MockVulkan.phsyicalDevice = bindings->physicalDevice; + + if (s_MockVulkan.vkGetPhysicalDeviceMemoryProperties == nullptr) + return XR_ERROR_RUNTIME_FAILURE; + + s_MockVulkan.vkGetPhysicalDeviceMemoryProperties(s_MockVulkan.phsyicalDevice, &s_MockVulkan.memoryProperties); + } + } + + return xrCreateSession(instance, createInfo, session); +} + +static VkImage CreateSwapchainImage(const XrSwapchainCreateInfo* createInfo) +{ + VkImageCreateInfo imageCreateInfo = {}; + imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = (VkFormat)createInfo->format; + imageCreateInfo.mipLevels = createInfo->mipCount; + imageCreateInfo.arrayLayers = createInfo->arraySize; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCreateInfo.extent.width = createInfo->width; + imageCreateInfo.extent.height = createInfo->height; + imageCreateInfo.extent.depth = 1; + imageCreateInfo.usage = 0; + + if (createInfo->usageFlags & XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT) + imageCreateInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + if (createInfo->usageFlags & XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) + imageCreateInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + + VkImage vkimage; + s_MockVulkan.vkCreateImage(s_MockVulkan.device, &imageCreateInfo, nullptr, &vkimage); + + // Allocate memory to back the destination image + VkMemoryRequirements memRequirements; + s_MockVulkan.vkGetImageMemoryRequirements(s_MockVulkan.device, vkimage, &memRequirements); + + VkDeviceMemory imageMemory; + VkMemoryAllocateInfo memAllocInfo = {}; + memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memAllocInfo.allocationSize = memRequirements.size; + + if (!GetVulkanMemoryTypeIndex(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAllocInfo.memoryTypeIndex)) + return VK_NULL_HANDLE; + + s_MockVulkan.vkAllocateMemory(s_MockVulkan.device, &memAllocInfo, nullptr, &imageMemory); + s_MockVulkan.vkBindImageMemory(s_MockVulkan.device, vkimage, imageMemory, 0); + + return vkimage; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR MockVulkan_xrCreateSwapchain(XrSession session, const XrSwapchainCreateInfo* createInfo, XrSwapchain* swapchain) +{ + LOG_FUNC(); + + *swapchain = (XrSwapchain)CreateSwapchainImage(createInfo); + + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR MockVulkan_xrDestroySwapChain(XrSwapchain swapchain) +{ + s_MockVulkan.vkDestroyImage(s_MockVulkan.device, (VkImage)swapchain, nullptr); + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR MockVulkan_xrAcquireSwapchainImage(XrSwapchain swapchain, const XrSwapchainImageAcquireInfo* acquireInfo, uint32_t* index) +{ + LOG_FUNC(); + + *index = 0; + + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR MockVulkan_xrReleaseSwapchainImage(XrSwapchain swapchain, const XrSwapchainImageReleaseInfo* releaseInfo) +{ + LOG_FUNC(); + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR MockVulkan_xrEnumerateSwapchainFormats(XrSession session, uint32_t formatCapacityInput, uint32_t* formatCountOutput, int64_t* formats) +{ + if (nullptr == formatCountOutput) + return XR_ERROR_VALIDATION_FAILURE; + + *formatCountOutput = 3; + + if (formatCapacityInput == 0) + return XR_SUCCESS; + + if (nullptr == formats) + return XR_ERROR_VALIDATION_FAILURE; + + if (formatCapacityInput < 3) + return XR_ERROR_SIZE_INSUFFICIENT; + + formats[0] = VK_FORMAT_R8G8B8A8_SRGB; + formats[1] = VK_FORMAT_D16_UNORM; + formats[2] = VK_FORMAT_D24_UNORM_S8_UINT; + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR MockVulkan_xrEnumerateSwapchainImages(XrSwapchain swapchain, uint32_t imageCapacityInput, uint32_t* imageCountOutput, XrSwapchainImageBaseHeader* images) +{ + LOG_FUNC(); + + *imageCountOutput = 1; + + if (images == nullptr) + return XR_SUCCESS; + + if (imageCapacityInput < *imageCountOutput) + return XR_ERROR_VALIDATION_FAILURE; + + XrSwapchainImageVulkanKHR* vkimages = (XrSwapchainImageVulkanKHR*)images; + vkimages[0].image = (VkImage)swapchain; + return XR_SUCCESS; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrEndFrame(XrSession session, const XrFrameEndInfo* frameEndInfo); +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR MockVulkan_xrEndFrame(XrSession session, const XrFrameEndInfo* frameEndInfo) +{ + return xrEndFrame(session, frameEndInfo); +} + +XrResult MockVulkan_GetInstanceProcAddr(const char* name, PFN_xrVoidFunction* function) +{ + GET_PROC_ADDRESS_REMAP(xrCreateSession, MockVulkan_xrCreateSession) + GET_PROC_ADDRESS_REMAP(xrCreateSwapchain, MockVulkan_xrCreateSwapchain) + GET_PROC_ADDRESS_REMAP(xrDestroySwapChain, MockVulkan_xrDestroySwapChain) + GET_PROC_ADDRESS_REMAP(xrEnumerateSwapchainFormats, MockVulkan_xrEnumerateSwapchainFormats) + GET_PROC_ADDRESS_REMAP(xrEnumerateSwapchainImages, MockVulkan_xrEnumerateSwapchainImages) + GET_PROC_ADDRESS_REMAP(xrAcquireSwapchainImage, MockVulkan_xrAcquireSwapchainImage) + GET_PROC_ADDRESS_REMAP(xrReleaseSwapchainImage, MockVulkan_xrReleaseSwapchainImage) + GET_PROC_ADDRESS_REMAP(xrEndFrame, MockVulkan_xrEndFrame) + GET_PROC_ADDRESS(xrCreateVulkanInstanceKHR) + GET_PROC_ADDRESS(xrGetVulkanGraphicsRequirements2KHR) + GET_PROC_ADDRESS(xrGetVulkanGraphicsDevice2KHR) + GET_PROC_ADDRESS(xrCreateVulkanDeviceKHR) + return XR_ERROR_FUNCTION_UNSUPPORTED; +} + +#endif // XR_USE_GRAPHICS_API_VULKAN diff --git a/MockRuntime/Native~/openxr_loader/mock.h b/MockRuntime/Native~/openxr_loader/mock.h index 9dc40b5..8729cbc 100644 --- a/MockRuntime/Native~/openxr_loader/mock.h +++ b/MockRuntime/Native~/openxr_loader/mock.h @@ -14,14 +14,10 @@ struct IUnityXRTrace; extern IUnityXRTrace* s_Trace; -#if DEBUG_LOG_EVERY_FUNC_CALL -#define LOG_FUNC() s_Trace->Trace(kXRLogTypeDebug, "[Mock] %s\n", __FUNCTION__) -#else -#define LOG_FUNC() -#endif +#define DEBUG_TRACE 1 #if DEBUG_TRACE -#define TRACE(STRING, ...) s_Trace->Trace(kXRLogTypeDebug, STRING, ##__VA_ARGS__) +#define TRACE(STRING, ...) s_Trace->Trace(kXRLogTypeDebug, "[Mock] " STRING, ##__VA_ARGS__) #else #define TRACE(STRING, ...) #endif @@ -29,3 +25,64 @@ extern IUnityXRTrace* s_Trace; #define XR_NO_PROTOTYPES #include "openxr/openxr.h" #include "openxr/openxr_platform.h" +#include + +#include "XR/IUnityXRTrace.h" + +#include +#include +#include +#include +#include + +#include "openxr/openxr_reflection.h" + +#include "enums_to_string.h" +#include "openxr_utils.h" + +class MockRuntime; +extern MockRuntime* s_runtime; + +#define GET_PROC_ADDRESS(funcName) \ + if (strcmp(#funcName, name) == 0) \ + { \ + *function = (PFN_xrVoidFunction)&funcName; \ + return XR_SUCCESS; \ + } + +#define GET_PROC_ADDRESS_REMAP(funcName, funcProc) \ + if (strcmp(#funcName, name) == 0) \ + { \ + *function = (PFN_xrVoidFunction)&funcProc; \ + return XR_SUCCESS; \ + } + +#define CHECK_RUNTIME() \ + if (s_runtime == nullptr) \ + return XR_ERROR_HANDLE_INVALID; +#define CHECK_INSTANCE(instance) \ + if (s_runtime == nullptr || s_runtime->GetInstance() != instance) \ + return XR_ERROR_HANDLE_INVALID; +#define CHECK_SESSION(session) \ + if (s_runtime == nullptr || s_runtime->GetSession() != session) \ + return XR_ERROR_HANDLE_INVALID; +#define CHECK_SUCCESS(body) \ + { \ + XrResult result = (body); \ + if (result != XR_SUCCESS) \ + return result; \ + } + +#define DEBUG_LOG_EVERY_FUNC_CALL 0 + +#if DEBUG_LOG_EVERY_FUNC_CALL +#define LOG_FUNC() s_Trace->Trace(kXRLogTypeDebug, "[Mock] %s\n", __FUNCTION__) +#else +#define LOG_FUNC() +#endif + +#include "openxr_mock_driver.h" + +#include "mock_extensions.h" +#include "mock_input_state.h" +#include "mock_runtime.h" diff --git a/MockRuntime/Native~/openxr_loader/mock_driver_extension.h b/MockRuntime/Native~/openxr_loader/mock_driver_extension.h deleted file mode 100644 index 4b5d190..0000000 --- a/MockRuntime/Native~/openxr_loader/mock_driver_extension.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -XrResult mock_driver_xrGetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function); diff --git a/MockRuntime/Native~/openxr_loader/mock_extensions.h b/MockRuntime/Native~/openxr_loader/mock_extensions.h new file mode 100644 index 0000000..8cadb5e --- /dev/null +++ b/MockRuntime/Native~/openxr_loader/mock_extensions.h @@ -0,0 +1,33 @@ +#pragma once + +// XR_UNITY_mock_driver + +XrResult MockDriver_GetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function); + +// XR_EXT_conformance_automation + +struct ConformanceAutomation; +class MockInputState; + +void ConformanceAutomation_Create(); +void ConformanceAutomation_Destroy(); +XrResult ConformanceAutomation_GetInstanceProcAddr(const char* name, PFN_xrVoidFunction* function); +XrResult ConformanceAutomation_GetInputState(MockInputState* state); + +// XR_KHR_win32_convert_performance_counter_time + +#if defined(XR_USE_PLATFORM_WIN32) +XrResult MockWin32ConvertPerformanceCounterTime_GetInstanceProcAddr(const char* name, PFN_xrVoidFunction* function); +#endif + +// XR_KHR_VULKAN_ENABLE2 + +#if defined(XR_USE_GRAPHICS_API_VULKAN) +XrResult MockVulkan_GetInstanceProcAddr(const char* name, PFN_xrVoidFunction* function); +#endif + +// XR_KHR_D3D11_ENABLE + +#if defined(XR_USE_GRAPHICS_API_D3D11) +XrResult MockD3D11_GetInstanceProcAddr(const char* name, PFN_xrVoidFunction* function); +#endif diff --git a/MockRuntime/Native~/openxr_loader/mock_input_state.cpp b/MockRuntime/Native~/openxr_loader/mock_input_state.cpp new file mode 100644 index 0000000..4983bc6 --- /dev/null +++ b/MockRuntime/Native~/openxr_loader/mock_input_state.cpp @@ -0,0 +1,185 @@ +#include "mock.h" + +void MockInputState::Reset() +{ + switch (type) + { + case XR_ACTION_TYPE_BOOLEAN_INPUT: + value.boolValue = false; + break; + + case XR_ACTION_TYPE_FLOAT_INPUT: + value.floatValue = false; + break; + + case XR_ACTION_TYPE_VECTOR2F_INPUT: + value.vectorValue = {0, 0}; + break; + + case XR_ACTION_TYPE_POSE_INPUT: + value.locationValue.pose = {{0, 0, 0, 1}, {0, 0, 0}}; + value.locationValue.space = XR_NULL_HANDLE; + break; + + default: + break; + } +} + +void MockInputState::Set(float v) +{ + switch (type) + { + case XR_ACTION_TYPE_FLOAT_INPUT: + value.floatValue = v; + break; + + case XR_ACTION_TYPE_BOOLEAN_INPUT: + value.boolValue = v != 0.0f; + break; + + default: + value.floatValue = 0.0f; + break; + } +} + +void MockInputState::Set(XrBool32 v) +{ + switch (type) + { + case XR_ACTION_TYPE_BOOLEAN_INPUT: + value.boolValue = v; + break; + + case XR_ACTION_TYPE_FLOAT_INPUT: + value.floatValue = v ? 1.0f : 0.0f; + break; + + default: + value.boolValue = false; + break; + } +} + +void MockInputState::Set(XrVector2f v) +{ + if (type != XR_ACTION_TYPE_VECTOR2F_INPUT) + { + Reset(); + return; + } + + value.vectorValue = v; +} + +void MockInputState::Set(XrSpace space, XrPosef pose) +{ + if (type != XR_ACTION_TYPE_POSE_INPUT) + { + Reset(); + return; + } + + value.locationValue.space = space; + value.locationValue.pose = pose; +} + +float MockInputState::GetFloat() const +{ + switch (type) + { + case XR_ACTION_TYPE_BOOLEAN_INPUT: + return (float)value.boolValue; + + case XR_ACTION_TYPE_FLOAT_INPUT: + return value.floatValue; + + default: + break; + } + + return 0.0f; +} + +XrBool32 MockInputState::GetBoolean() const +{ + switch (type) + { + case XR_ACTION_TYPE_BOOLEAN_INPUT: + return value.boolValue; + + case XR_ACTION_TYPE_FLOAT_INPUT: + return value.floatValue != 0.0f; + + default: + break; + } + + return false; +} + +XrVector2f MockInputState::GetVector2() const +{ + if (type == XR_ACTION_TYPE_VECTOR2F_INPUT) + return value.vectorValue; + + return XrVector2f(); +} + +XrSpace MockInputState::GetLocationSpace() const +{ + if (type == XR_ACTION_TYPE_POSE_INPUT) + return value.locationValue.space; + + return XR_NULL_HANDLE; +} + +XrPosef MockInputState::GetLocationPose() const +{ + if (type == XR_ACTION_TYPE_POSE_INPUT) + return value.locationValue.pose; + + return XrPosef(); +} + +bool MockInputState::IsCompatibleType(XrActionType actionType) const +{ + switch (type) + { + case XR_ACTION_TYPE_BOOLEAN_INPUT: + case XR_ACTION_TYPE_FLOAT_INPUT: + return actionType == XR_ACTION_TYPE_FLOAT_INPUT || actionType == XR_ACTION_TYPE_BOOLEAN_INPUT; + + default: + break; + } + + return IsType(actionType); +} + +void MockInputState::CopyValue(const MockInputState& state) +{ + switch (type) + { + case XR_ACTION_TYPE_BOOLEAN_INPUT: + value.boolValue = state.GetBoolean(); + break; + + case XR_ACTION_TYPE_FLOAT_INPUT: + value.floatValue = state.GetFloat(); + break; + + case XR_ACTION_TYPE_VECTOR2F_INPUT: + value.vectorValue = state.GetVector2(); + break; + + case XR_ACTION_TYPE_POSE_INPUT: + value.locationValue.space = state.GetLocationSpace(); + value.locationValue.pose = state.GetLocationPose(); + break; + + default: + break; + } +} diff --git a/MockRuntime/Native~/openxr_loader/mock_input_state.h b/MockRuntime/Native~/openxr_loader/mock_input_state.h new file mode 100644 index 0000000..98f6496 --- /dev/null +++ b/MockRuntime/Native~/openxr_loader/mock_input_state.h @@ -0,0 +1,44 @@ +#pragma once + +class MockInputState +{ +public: + XrPath interactionProfile; + XrPath path; + XrActionType type; + + bool IsType(XrActionType actionType) const + { + return type == actionType; + } + + bool IsCompatibleType(XrActionType actionType) const; + + void Reset(); + + void Set(float value); + void Set(XrBool32 value); + void Set(XrVector2f value); + void Set(XrSpace space, XrPosef pose); + + void CopyValue(const MockInputState& state); + + float GetFloat() const; + XrBool32 GetBoolean() const; + XrVector2f GetVector2() const; + XrSpace GetLocationSpace() const; + XrPosef GetLocationPose() const; + +private: + union { + XrBool32 boolValue; + float floatValue; + XrVector2f vectorValue; + struct + { + XrSpace space; + XrPosef pose; + } locationValue; + + } value; +}; diff --git a/MockRuntime/Native~/openxr_loader/mock_loader.cpp b/MockRuntime/Native~/openxr_loader/mock_loader.cpp index 622209b..b0861d6 100644 --- a/MockRuntime/Native~/openxr_loader/mock_loader.cpp +++ b/MockRuntime/Native~/openxr_loader/mock_loader.cpp @@ -1,25 +1,8 @@ -#include "IUnityInterface.h" -#include "XR/IUnityXRTrace.h" -#include -#include -#include -#include - -#define DEBUG_LOG_EVERY_FUNC_CALL 1 - #include "mock.h" -#include "mock_driver_extension.h" -#include "mock_state.h" -#include "openxr_mock_driver.h" +#include -XR_DEFINE_HANDLE(MockHandle) -static uint64_t s_HandleCounter = 0; -static uint64_t GetNextHandleId() -{ - return ++s_HandleCounter; -} - -static std::vector s_PathStrings; +IUnityXRTrace* s_Trace = nullptr; +MockRuntime* s_runtime = nullptr; #define XR_UNITY_mock_test_SPEC_VERSION 123 #define XR_UNITY_MOCK_TEST_EXTENSION_NAME "XR_UNITY_mock_test" @@ -27,14 +10,22 @@ static std::vector s_PathStrings; #define XR_UNITY_null_gfx_SPEC_VERSION 1 #define XR_UNITY_NULL_GFX_EXTENSION_NAME "XR_UNITY_null_gfx" -#define CHECK_EXPECTED_RESULT(...) \ - std::vector expectedResults{__VA_ARGS__}; \ - XrResult expectedResult = GetExpectedResultForFunction(__FUNCTION__); \ - if (std::find(expectedResults.begin(), expectedResults.end(), expectedResult) == expectedResults.end()) \ - return expectedResult; +#define ENUM_TO_STR(name, val) \ + case val: \ + strncpy(buffer, #name, XR_MAX_RESULT_STRING_SIZE - 1); \ + break; + +#define CHECK_EXPECTED_RESULT(...) \ + std::vector expectedResults{__VA_ARGS__}; \ + if (s_runtime != nullptr) \ + { \ + XrResult expectedResult = s_runtime->GetExpectedResultForFunction(__FUNCTION__); \ + if (std::find(expectedResults.begin(), expectedResults.end(), expectedResult) == expectedResults.end()) \ + return expectedResult; \ + } // clang-format off -static XrExtensionProperties s_Extensions[] = { + static XrExtensionProperties s_Extensions[] = { { XR_TYPE_EXTENSION_PROPERTIES, nullptr, @@ -58,7 +49,63 @@ static XrExtensionProperties s_Extensions[] = { nullptr, XR_KHR_VISIBILITY_MASK_EXTENSION_NAME, XR_KHR_visibility_mask_SPEC_VERSION + }, + { + XR_TYPE_EXTENSION_PROPERTIES, + nullptr, + XR_EXT_CONFORMANCE_AUTOMATION_EXTENSION_NAME, + XR_EXT_conformance_automation_SPEC_VERSION + }, + { + XR_TYPE_EXTENSION_PROPERTIES, + nullptr, + XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME, + XR_KHR_composition_layer_depth_SPEC_VERSION + }, + { + XR_TYPE_EXTENSION_PROPERTIES, + nullptr, + XR_VARJO_QUAD_VIEWS_EXTENSION_NAME, + XR_VARJO_quad_views_SPEC_VERSION + }, + { + XR_TYPE_EXTENSION_PROPERTIES, + nullptr, + XR_MSFT_SECONDARY_VIEW_CONFIGURATION_EXTENSION_NAME, + XR_MSFT_secondary_view_configuration_SPEC_VERSION + }, + { + XR_TYPE_EXTENSION_PROPERTIES, + nullptr, + XR_MSFT_FIRST_PERSON_OBSERVER_EXTENSION_NAME, + XR_MSFT_first_person_observer_SPEC_VERSION } +#if defined(XR_USE_GRAPHICS_API_VULKAN) + ,{ + XR_TYPE_EXTENSION_PROPERTIES, + nullptr, + XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME, + XR_KHR_vulkan_enable2_SPEC_VERSION + } +#endif + +#if defined(XR_USE_PLATFORM_WIN32) + ,{ + XR_TYPE_EXTENSION_PROPERTIES, + nullptr, + XR_KHR_WIN32_CONVERT_PERFORMANCE_COUNTER_TIME_EXTENSION_NAME, + XR_KHR_win32_convert_performance_counter_time_SPEC_VERSION + } +#endif + +#if defined(XR_USE_GRAPHICS_API_D3D11) + ,{ + XR_TYPE_EXTENSION_PROPERTIES, + nullptr, + XR_KHR_D3D11_ENABLE_EXTENSION_NAME, + XR_KHR_D3D11_enable_SPEC_VERSION + } +#endif }; // clang-format on @@ -72,19 +119,21 @@ extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrEnumerateApiLayerProperti extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrEnumerateInstanceExtensionProperties(const char* layerName, uint32_t propertyCapacityInput, uint32_t* propertyCountOutput, XrExtensionProperties* properties) { LOG_FUNC(); - if (properties == nullptr) - { - *propertyCountOutput = sizeof(s_Extensions) / sizeof(XrExtensionProperties); - } - else + + *propertyCountOutput = sizeof(s_Extensions) / sizeof(XrExtensionProperties); + + if (propertyCapacityInput == 0) + return XR_SUCCESS; + if (propertyCapacityInput < *propertyCountOutput) + return XR_ERROR_VALIDATION_FAILURE; + + uint32_t count = 0; + while (count < *propertyCountOutput) { - uint32_t count = 0; - while (count < *propertyCountOutput) - { - properties[count] = s_Extensions[count]; - ++count; - } + properties[count] = s_Extensions[count]; + ++count; } + return XR_SUCCESS; } @@ -92,30 +141,90 @@ extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrCreateInstance(const XrIn { LOG_FUNC(); + // Destroy any existing runtime if there is one + if (s_runtime != nullptr) + { + delete s_runtime; + s_runtime = nullptr; + } + *instance = (XrInstance)1; - bool supportsDriverExtension = false; - bool wantsNullGfx = false; + MockRuntimeCreateFlags flags = 0; + for (uint32_t i = 0; i < createInfo->enabledExtensionCount; ++i) { - if (strncmp(XR_UNITY_MOCK_TEST_EXTENSION_NAME, createInfo->enabledExtensionNames[i], sizeof(XR_UNITY_MOCK_TEST_EXTENSION_NAME)) == 0) + const char* extension = createInfo->enabledExtensionNames[i]; + if (strncmp(XR_UNITY_MOCK_TEST_EXTENSION_NAME, extension, sizeof(XR_UNITY_MOCK_TEST_EXTENSION_NAME)) == 0) { *instance = (XrInstance)10; + continue; + } + + if (strncmp(XR_UNITY_MOCK_DRIVER_EXTENSION_NAME, extension, sizeof(XR_UNITY_MOCK_DRIVER_EXTENSION_NAME)) == 0) + { + flags |= MR_CREATE_DRIVER_EXT; + continue; + } + + if ((flags & MR_CREATE_ALL_GFX_EXT) == 0) + { +#if defined(XR_USE_GRAPHICS_API_VULKAN) + if (strncmp(XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME, extension, sizeof(XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME)) == 0) + { + flags |= MR_CREATE_VULKAN_GFX_EXT; + continue; + } +#endif +#if defined(XR_USE_GRAPHICS_API_D3D11) + if (strncmp(XR_KHR_D3D11_ENABLE_EXTENSION_NAME, extension, sizeof(XR_KHR_D3D11_ENABLE_EXTENSION_NAME)) == 0) + { + flags |= MR_CREATE_D3D11_GFX_EXT; + continue; + } +#endif + if (strncmp(XR_UNITY_NULL_GFX_EXTENSION_NAME, extension, sizeof(XR_UNITY_NULL_GFX_EXTENSION_NAME)) == 0) + { + flags |= MR_CREATE_NULL_GFX_EXT; + continue; + } + } + + // Conformance Automation + if (strncmp(XR_EXT_CONFORMANCE_AUTOMATION_EXTENSION_NAME, extension, sizeof(XR_EXT_CONFORMANCE_AUTOMATION_EXTENSION_NAME)) == 0) + { + flags |= MR_CREATE_CONFORMANCE_AUTOMATION_EXT; + continue; + } + + if (strncmp(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME, extension, sizeof(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME)) == 0) + { + flags |= MR_CREATE_COMPOSITION_LAYER_DEPTH_EXT; + continue; + } + + if (strncmp(XR_VARJO_QUAD_VIEWS_EXTENSION_NAME, extension, sizeof(XR_VARJO_QUAD_VIEWS_EXTENSION_NAME)) == 0) + { + flags |= MR_CREATE_VARJO_QUAD_VIEWS_EXT; + continue; } - if (strncmp(XR_UNITY_MOCK_DRIVER_EXTENSION_NAME, createInfo->enabledExtensionNames[i], sizeof(XR_UNITY_MOCK_TEST_EXTENSION_NAME)) == 0) + if (strncmp(XR_MSFT_SECONDARY_VIEW_CONFIGURATION_EXTENSION_NAME, extension, sizeof(XR_MSFT_SECONDARY_VIEW_CONFIGURATION_EXTENSION_NAME)) == 0) { - supportsDriverExtension = true; + flags |= MR_CREATE_MSFT_SECONDARY_VIEW_CONFIGURATION_EXT; + continue; } - if (strncmp(XR_UNITY_NULL_GFX_EXTENSION_NAME, createInfo->enabledExtensionNames[i], sizeof(XR_UNITY_MOCK_TEST_EXTENSION_NAME)) == 0) + if (strncmp(XR_MSFT_FIRST_PERSON_OBSERVER_EXTENSION_NAME, extension, sizeof(XR_MSFT_FIRST_PERSON_OBSERVER_EXTENSION_NAME)) == 0) { - wantsNullGfx = true; + flags |= MR_CREATE_MSFT_FIRST_PERSON_OBSERVER_EXT; + continue; } } - AddInstance(*instance); - SetSupportingDriverExtension(*instance, supportsDriverExtension); - SetNullGfx(wantsNullGfx); + if ((flags & MR_CREATE_ALL_GFX_EXT) == 0) + flags |= MR_CREATE_NULL_GFX_EXT; + + s_runtime = new MockRuntime(*instance, flags); return XR_SUCCESS; } @@ -123,15 +232,20 @@ extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrCreateInstance(const XrIn extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrDestroyInstance(XrInstance instance) { LOG_FUNC(); - RemoveInstance(instance); + CHECK_INSTANCE(instance); + + delete s_runtime; + s_runtime = nullptr; + return XR_SUCCESS; } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetInstanceProperties(XrInstance instance, XrInstanceProperties* instanceProperties) { LOG_FUNC(); + CHECK_INSTANCE(instance); - instanceProperties->runtimeVersion = XR_MAKE_VERSION(0, 0, 1); + instanceProperties->runtimeVersion = XR_MAKE_VERSION(0, 0, 2); strncpy(instanceProperties->runtimeName, "Unity Mock Runtime", XR_MAX_RUNTIME_NAME_SIZE); return XR_SUCCESS; @@ -140,25 +254,42 @@ extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetInstanceProperties(XrI extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrPollEvent(XrInstance instance, XrEventDataBuffer* eventData) { LOG_FUNC(); - - return GetNextEvent(instance, eventData); + CHECK_INSTANCE(instance); + return s_runtime->GetNextEvent(eventData); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrResultToString(XrInstance instance, XrResult value, char buffer[XR_MAX_RESULT_STRING_SIZE]) { LOG_FUNC(); + CHECK_INSTANCE(instance); + + switch (value) + { + XR_LIST_ENUM_XrResult(ENUM_TO_STR) default : strncpy(buffer, ((value < 0 ? "XR_UNKNOWN_FAILURE_" : "XR_UNKNOWN_SUCCESS_") + std::to_string(value)).c_str(), XR_MAX_RESULT_STRING_SIZE - 1); + break; + } + return XR_SUCCESS; } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrStructureTypeToString(XrInstance instance, XrStructureType value, char buffer[XR_MAX_STRUCTURE_NAME_SIZE]) { LOG_FUNC(); + CHECK_INSTANCE(instance); + + switch (value) + { + XR_LIST_ENUM_XrStructureType(ENUM_TO_STR) default : strncpy(buffer, ("XR_UNKNOWN_STRUCTURE_TYPE_" + std::to_string(value)).c_str(), XR_MAX_RESULT_STRING_SIZE - 1); + break; + } + return XR_SUCCESS; } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetSystem(XrInstance instance, const XrSystemGetInfo* getInfo, XrSystemId* systemId) { LOG_FUNC(); + CHECK_INSTANCE(instance); *systemId = (XrSystemId)2; return XR_SUCCESS; } @@ -166,43 +297,28 @@ extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetSystem(XrInstance inst extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetSystemProperties(XrInstance instance, XrSystemId systemId, XrSystemProperties* properties) { LOG_FUNC(); + CHECK_INSTANCE(instance); + properties->vendorId = 0xFEFE; + properties->systemId = (XrSystemId)2; + properties->graphicsProperties.maxLayerCount = XR_MIN_COMPOSITION_LAYERS_SUPPORTED; return XR_SUCCESS; } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrEnumerateEnvironmentBlendModes(XrInstance instance, XrSystemId systemId, XrViewConfigurationType viewConfigurationType, uint32_t environmentBlendModeCapacityInput, uint32_t* environmentBlendModeCountOutput, XrEnvironmentBlendMode* environmentBlendModes) { LOG_FUNC(); - - if (viewConfigurationType != XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO) - { - return XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED; - } - - *environmentBlendModeCountOutput = 1; - - if (environmentBlendModeCapacityInput == 0) - return XR_SUCCESS; - if (environmentBlendModeCapacityInput < *environmentBlendModeCountOutput) - return XR_ERROR_VALIDATION_FAILURE; - - environmentBlendModes[0] = GetMockBlendMode(); - - return XR_SUCCESS; + CHECK_INSTANCE(instance); + return s_runtime->EnumerateEnvironmentBlendModes(systemId, viewConfigurationType, environmentBlendModeCapacityInput, environmentBlendModeCountOutput, environmentBlendModes); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrCreateSession(XrInstance instance, const XrSessionCreateInfo* createInfo, XrSession* session) { LOG_FUNC(); + CHECK_INSTANCE(instance); CHECK_EXPECTED_RESULT(XR_SUCCESS); + CHECK_SUCCESS(s_runtime->CreateSession(createInfo)); - *session = (XrSession)3; - SetMockSession(instance, session); - - ChangeSessionState(*session, XR_SESSION_STATE_IDLE); - - // TODO: STATEMANAGEMENT: If users presence is enabled then we need to do this transition at the point - // where the user is actually known to exist (i.e. presence sensor is triggered.) - ChangeSessionStateFrom(*session, XR_SESSION_STATE_IDLE, XR_SESSION_STATE_READY); + *session = s_runtime->GetSession(); return XR_SUCCESS; } @@ -210,17 +326,15 @@ extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrCreateSession(XrInstance extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrDestroySession(XrSession session) { LOG_FUNC(); + CHECK_SESSION(session); CHECK_EXPECTED_RESULT(XR_SUCCESS); - - SetSessionRunning(session, false); - SetMockSession(GetInstanceFromSession(session), XR_NULL_HANDLE); - - return XR_SUCCESS; + return s_runtime->DestroySession(); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrEnumerateReferenceSpaces(XrSession session, uint32_t spaceCapacityInput, uint32_t* spaceCountOutput, XrReferenceSpaceType* spaces) { LOG_FUNC(); + CHECK_SESSION(session); if (!spaceCountOutput) return XR_ERROR_VALIDATION_FAILURE; @@ -244,43 +358,30 @@ extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrEnumerateReferenceSpaces( extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrCreateReferenceSpace(XrSession session, const XrReferenceSpaceCreateInfo* createInfo, XrSpace* space) { LOG_FUNC(); - - switch (createInfo->referenceSpaceType) - { - case XR_REFERENCE_SPACE_TYPE_LOCAL: - *space = (XrSpace)4; - break; - case XR_REFERENCE_SPACE_TYPE_STAGE: - *space = (XrSpace)5; - break; - case XR_REFERENCE_SPACE_TYPE_VIEW: - *space = (XrSpace)6; - break; - default: - break; - } - - return XR_SUCCESS; + CHECK_SESSION(session); + return s_runtime->CreateReferenceSpace(createInfo, space); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetReferenceSpaceBoundsRect(XrSession session, XrReferenceSpaceType referenceSpaceType, XrExtent2Df* bounds) { LOG_FUNC(); - - return (GetExtentsForReferenceSpace(session, referenceSpaceType, bounds)) ? XR_SUCCESS : XR_SPACE_BOUNDS_UNAVAILABLE; + CHECK_SESSION(session); + return s_runtime->GetReferenceSpaceBoundsRect(referenceSpaceType, bounds); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrCreateActionSpace(XrSession session, const XrActionSpaceCreateInfo* createInfo, XrSpace* space) { LOG_FUNC(); - return XR_SUCCESS; + CHECK_SESSION(session); + return s_runtime->CreateActionSpace(createInfo, space); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrLocateSpace(XrSpace space, XrSpace baseSpace, XrTime time, XrSpaceLocation* location) { LOG_FUNC(); + CHECK_RUNTIME(); CHECK_EXPECTED_RESULT(XR_SUCCESS); - return LocateSpace(space, baseSpace, time, location); + return s_runtime->LocateSpace(space, baseSpace, time, location); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrDestroySpace(XrSpace space) @@ -293,17 +394,8 @@ extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrDestroySpace(XrSpace spac extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrEnumerateViewConfigurations(XrInstance instance, XrSystemId systemId, uint32_t viewConfigurationTypeCapacityInput, uint32_t* viewConfigurationTypeCountOutput, XrViewConfigurationType* viewConfigurationTypes) { LOG_FUNC(); - - *viewConfigurationTypeCountOutput = 1; - - if (viewConfigurationTypeCapacityInput == 0) - return XR_SUCCESS; - if (viewConfigurationTypeCapacityInput < *viewConfigurationTypeCountOutput) - return XR_ERROR_VALIDATION_FAILURE; - - viewConfigurationTypes[0] = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; - - return XR_SUCCESS; + CHECK_INSTANCE(instance); + return s_runtime->EnumerateViewConfigurations(systemId, viewConfigurationTypeCapacityInput, viewConfigurationTypeCountOutput, viewConfigurationTypes); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetViewConfigurationProperties(XrInstance instance, XrSystemId systemId, XrViewConfigurationType viewConfigurationType, XrViewConfigurationProperties* configurationProperties) @@ -315,25 +407,8 @@ extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetViewConfigurationPrope extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrEnumerateViewConfigurationViews(XrInstance instance, XrSystemId systemId, XrViewConfigurationType viewConfigurationType, uint32_t viewCapacityInput, uint32_t* viewCountOutput, XrViewConfigurationView* views) { LOG_FUNC(); - - *viewCountOutput = 2; - - if (viewCapacityInput == 0) - return XR_SUCCESS; - if (viewCapacityInput < *viewCountOutput) - return XR_ERROR_VALIDATION_FAILURE; - - // Matches unity's mock hmd which is modeled after vive. - views[0].recommendedImageRectWidth = 1512; - views[0].maxImageRectWidth = 1512 * 2; - views[0].recommendedImageRectHeight = 1680; - views[0].maxImageRectHeight = 1680 * 2; - views[0].recommendedSwapchainSampleCount = 1; - views[0].maxSwapchainSampleCount = 1; - - views[1] = views[0]; - - return XR_SUCCESS; + CHECK_INSTANCE(instance); + return s_runtime->EnumerateViewConfigurationViews(systemId, viewConfigurationType, viewCapacityInput, viewCountOutput, views); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrEnumerateSwapchainFormats(XrSession session, uint32_t formatCapacityInput, uint32_t* formatCountOutput, int64_t* formats) @@ -407,53 +482,33 @@ extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrReleaseSwapchainImage(XrS extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrBeginSession(XrSession session, const XrSessionBeginInfo* beginInfo) { LOG_FUNC(); + CHECK_SESSION(session); CHECK_EXPECTED_RESULT(XR_SUCCESS); - - SetSessionRunning(session, true); - - ChangeSessionStateFrom(session, XR_SESSION_STATE_READY, XR_SESSION_STATE_SYNCHRONIZED); - ChangeSessionStateFrom(session, XR_SESSION_STATE_SYNCHRONIZED, XR_SESSION_STATE_VISIBLE); - ChangeSessionStateFrom(session, XR_SESSION_STATE_VISIBLE, XR_SESSION_STATE_FOCUSED); - - return XR_SUCCESS; + return s_runtime->BeginSession(beginInfo); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrEndSession(XrSession session) { LOG_FUNC(); + CHECK_SESSION(session); CHECK_EXPECTED_RESULT(XR_SUCCESS, XR_ERROR_SESSION_NOT_STOPPING); - SetSessionRunning(session, false); - ChangeSessionStateFrom(session, XR_SESSION_STATE_STOPPING, XR_SESSION_STATE_IDLE); - - if (HasExitBeenRequested()) - { - SetExitRequestState(false); - ChangeSessionStateFrom(session, XR_SESSION_STATE_IDLE, XR_SESSION_STATE_EXITING); - } - - return expectedResult; + return s_runtime->EndSession(); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrRequestExitSession(XrSession session) { LOG_FUNC(); + CHECK_SESSION(session); CHECK_EXPECTED_RESULT(XR_SUCCESS); - - ChangeSessionStateFrom(session, XR_SESSION_STATE_FOCUSED, XR_SESSION_STATE_VISIBLE); - ChangeSessionStateFrom(session, XR_SESSION_STATE_VISIBLE, XR_SESSION_STATE_SYNCHRONIZED); - ChangeSessionStateFrom(session, XR_SESSION_STATE_SYNCHRONIZED, XR_SESSION_STATE_STOPPING); - - SetExitRequestState(true); - return XR_SUCCESS; + return s_runtime->RequestExitSession(); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrWaitFrame(XrSession session, const XrFrameWaitInfo* frameWaitInfo, XrFrameState* frameState) { LOG_FUNC(); - frameState->predictedDisplayPeriod = 16666000; - frameState->shouldRender = IsNullGfx(); - return XR_SUCCESS; + CHECK_SESSION(session); + return s_runtime->WaitFrame(frameWaitInfo, frameState); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrBeginFrame(XrSession session, const XrFrameBeginInfo* frameBeginInfo) @@ -465,214 +520,142 @@ extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrBeginFrame(XrSession sess extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrEndFrame(XrSession session, const XrFrameEndInfo* frameEndInfo) { LOG_FUNC(); - return EndFrame(session, frameEndInfo); + CHECK_SESSION(session); + return s_runtime->EndFrame(frameEndInfo); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrLocateViews(XrSession session, const XrViewLocateInfo* viewLocateInfo, XrViewState* viewState, uint32_t viewCapacityInput, uint32_t* viewCountOutput, XrView* views) { LOG_FUNC(); - return LocateViews(session, viewLocateInfo, viewState, viewCapacityInput, viewCountOutput, views); + CHECK_SESSION(session); + return s_runtime->LocateViews(viewLocateInfo, viewState, viewCapacityInput, viewCountOutput, views); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrStringToPath(XrInstance instance, const char* pathString, XrPath* path) { LOG_FUNC(); - - if (strlen(pathString) >= XR_MAX_PATH_LENGTH) - return XR_ERROR_PATH_COUNT_EXCEEDED; - - for (int i = 0llu; i < s_PathStrings.size(); i++) - { - if (strncmp(pathString, s_PathStrings[i].c_str(), XR_MAX_PATH_LENGTH) == 0) - { - // Path is incremented by 1 because the invalid path Id is 0. - *path = (XrPath)(i + 1); - return XR_SUCCESS; - } - } - - s_PathStrings.push_back(std::string(pathString)); - *path = (XrPath)s_PathStrings.size(); - return XR_SUCCESS; + CHECK_INSTANCE(instance); + return s_runtime->StringToPath(pathString, path); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrPathToString(XrInstance instance, XrPath path, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer) { LOG_FUNC(); - - // Path is 1 greater than the index because 0 is reserved as an invalid path. - size_t pathIdx = ((size_t)path) - 1; - if (pathIdx < 0 && pathIdx >= s_PathStrings.size()) - { - *bufferCountOutput = 0; - return XR_ERROR_PATH_INVALID; - } - - std::string& pathString = s_PathStrings[pathIdx]; - - if (pathString.size() >= bufferCapacityInput) - { - *bufferCountOutput = 0; - return XR_ERROR_SIZE_INSUFFICIENT; - } - - uint32_t strSize = (uint32_t)pathString.size(); - memcpy(buffer, pathString.c_str(), strSize); - - buffer[strSize] = '\0'; - *bufferCountOutput = strSize + 1; - - return XR_SUCCESS; + CHECK_INSTANCE(instance); + return s_runtime->PathToString(path, bufferCapacityInput, bufferCountOutput, buffer); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrCreateActionSet(XrInstance instance, const XrActionSetCreateInfo* createInfo, XrActionSet* actionSet) { LOG_FUNC(); - *actionSet = reinterpret_cast(GetNextHandleId()); - return XR_SUCCESS; + CHECK_INSTANCE(instance); + return s_runtime->CreateActionSet(createInfo, actionSet); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrDestroyActionSet(XrActionSet actionSet) { LOG_FUNC(); - return XR_SUCCESS; + CHECK_RUNTIME(); + return s_runtime->DestroyActionSet(actionSet); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrCreateAction(XrActionSet actionSet, const XrActionCreateInfo* createInfo, XrAction* action) { LOG_FUNC(); - *action = reinterpret_cast(GetNextHandleId()); - return XR_SUCCESS; + CHECK_RUNTIME(); + return s_runtime->CreateAction(actionSet, createInfo, action); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrDestroyAction(XrAction action) { LOG_FUNC(); - return XR_SUCCESS; + CHECK_RUNTIME(); + return s_runtime->DestroyAction(action); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrSuggestInteractionProfileBindings(XrInstance instance, const XrInteractionProfileSuggestedBinding* suggestedBindings) { LOG_FUNC(); - return XR_SUCCESS; + CHECK_INSTANCE(instance); + return s_runtime->SuggestInteractionProfileBindings(suggestedBindings); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrAttachSessionActionSets(XrSession session, const XrSessionActionSetsAttachInfo* attachInfo) { LOG_FUNC(); - return XR_SUCCESS; + CHECK_SESSION(session); + return s_runtime->AttachSessionActionSets(attachInfo); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetCurrentInteractionProfile(XrSession session, XrPath topLevelUserPath, XrInteractionProfileState* interactionProfile) { LOG_FUNC(); - return XR_SUCCESS; + CHECK_SESSION(session); + return s_runtime->GetCurrentInteractionProfile(topLevelUserPath, interactionProfile); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetActionStateBoolean(XrSession session, const XrActionStateGetInfo* getInfo, XrActionStateBoolean* state) { LOG_FUNC(); - return XR_SUCCESS; + CHECK_SESSION(session); + return s_runtime->GetActionStateBoolean(getInfo, state); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetActionStateFloat(XrSession session, const XrActionStateGetInfo* getInfo, XrActionStateFloat* state) { LOG_FUNC(); - return XR_SUCCESS; + CHECK_SESSION(session); + return s_runtime->GetActionStateFloat(getInfo, state); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetActionStateVector2f(XrSession session, const XrActionStateGetInfo* getInfo, XrActionStateVector2f* state) { LOG_FUNC(); - return XR_SUCCESS; + CHECK_SESSION(session); + return s_runtime->GetActionStateVector2f(getInfo, state); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetActionStatePose(XrSession session, const XrActionStateGetInfo* getInfo, XrActionStatePose* state) { LOG_FUNC(); - return XR_SUCCESS; + CHECK_SESSION(session); + return s_runtime->GetActionStatePose(getInfo, state); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrSyncActions(XrSession session, const XrActionsSyncInfo* syncInfo) { LOG_FUNC(); - return XR_SUCCESS; + CHECK_SESSION(session); + return s_runtime->SyncActions(syncInfo); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrEnumerateBoundSourcesForAction(XrSession session, const XrBoundSourcesForActionEnumerateInfo* enumerateInfo, uint32_t sourceCapacityInput, uint32_t* sourceCountOutput, XrPath* sources) { LOG_FUNC(); - *sourceCountOutput = 0; - return XR_SUCCESS; + CHECK_SESSION(session) + return s_runtime->EnumerateBoundSourcesForAction(enumerateInfo, sourceCapacityInput, sourceCountOutput, sources); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetInputSourceLocalizedName(XrSession session, const XrInputSourceLocalizedNameGetInfo* getInfo, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer) { LOG_FUNC(); - *bufferCountOutput = 0; - return XR_SUCCESS; + CHECK_SESSION(session); + return s_runtime->GetInputSourceLocalizedName(getInfo, bufferCapacityInput, bufferCountOutput, buffer); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrApplyHapticFeedback(XrSession session, const XrHapticActionInfo* hapticActionInfo, const XrHapticBaseHeader* hapticFeedback) { LOG_FUNC(); - return XR_SUCCESS; + CHECK_SESSION(session); + return s_runtime->ApplyHapticFeedback(hapticActionInfo, hapticFeedback); } extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrStopHapticFeedback(XrSession session, const XrHapticActionInfo* hapticActionInfo) { LOG_FUNC(); - return XR_SUCCESS; -} - -#ifdef _WIN32 -extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrConvertWin32PerformanceCounterToTimeKHR(XrInstance instance, const LARGE_INTEGER* performanceCounter, XrTime* time) -{ - LOG_FUNC(); - return XR_SUCCESS; -} - -extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrConvertTimeToWin32PerformanceCounterKHR(XrInstance instance, XrTime time, LARGE_INTEGER* performanceCounter) -{ - LOG_FUNC(); - return XR_SUCCESS; -} - -extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetD3D11GraphicsRequirementsKHR(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D11KHR* graphicsRequirements) -{ - LOG_FUNC(); - return XR_SUCCESS; + CHECK_SESSION(session); + return s_runtime->StopHapticFeedback(hapticActionInfo); } -#endif - -// extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetOpenGLESGraphicsRequirementsKHR(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLESKHR* graphicsRequirements) -// { -// return XR_SUCCESS; -// } - -#ifdef XR_USE_GRAPHICS_API_VULKAN -extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetVulkanInstanceExtensionsKHR(XrInstance instance, XrSystemId systemId, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer) -{ - *bufferCountOutput = 0; - return XR_SUCCESS; -} - -extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetVulkanDeviceExtensionsKHR(XrInstance instance, XrSystemId systemId, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer) -{ - *bufferCountOutput = 0; - return XR_SUCCESS; -} - -extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetVulkanGraphicsDeviceKHR(XrInstance instance, XrSystemId systemId, VkInstance vkInstance, VkPhysicalDevice* vkPhysicalDevice) -{ - return XR_SUCCESS; -} - -extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetVulkanGraphicsRequirementsKHR(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsVulkanKHR* graphicsRequirements) -{ - return XR_SUCCESS; -} -#endif extern uint32_t s_VisibilityMaskVerticesSizes[2][3]; extern uint32_t s_VisibilityMaskIndicesSizes[2][3]; @@ -700,93 +683,65 @@ extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetVisibilityMaskKHR(XrSe extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrGetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function) { LOG_FUNC(); -#define LOOKUP(funcName) \ - if (strcmp(#funcName, name) == 0) \ - { \ - *function = (PFN_xrVoidFunction)&funcName; \ - return XR_SUCCESS; \ - } - - LOOKUP(xrEnumerateApiLayerProperties) - LOOKUP(xrEnumerateInstanceExtensionProperties) - LOOKUP(xrCreateInstance) - LOOKUP(xrDestroyInstance) - LOOKUP(xrGetInstanceProperties) - LOOKUP(xrPollEvent) - LOOKUP(xrResultToString) - LOOKUP(xrStructureTypeToString) - LOOKUP(xrGetSystem) - LOOKUP(xrGetSystemProperties) - LOOKUP(xrEnumerateEnvironmentBlendModes) - LOOKUP(xrCreateSession) - LOOKUP(xrDestroySession) - LOOKUP(xrEnumerateReferenceSpaces) - LOOKUP(xrCreateReferenceSpace) - LOOKUP(xrGetReferenceSpaceBoundsRect) - LOOKUP(xrCreateActionSpace) - LOOKUP(xrLocateSpace) - LOOKUP(xrDestroySpace) - LOOKUP(xrEnumerateViewConfigurations) - LOOKUP(xrGetViewConfigurationProperties) - LOOKUP(xrEnumerateViewConfigurationViews) - LOOKUP(xrEnumerateSwapchainFormats) - LOOKUP(xrCreateSwapchain) - LOOKUP(xrDestroySwapchain) - LOOKUP(xrEnumerateSwapchainImages) - LOOKUP(xrAcquireSwapchainImage) - LOOKUP(xrWaitSwapchainImage) - LOOKUP(xrReleaseSwapchainImage) - LOOKUP(xrBeginSession) - LOOKUP(xrEndSession) - LOOKUP(xrRequestExitSession) - LOOKUP(xrWaitFrame) - LOOKUP(xrBeginFrame) - LOOKUP(xrEndFrame) - LOOKUP(xrLocateViews) - LOOKUP(xrStringToPath) - LOOKUP(xrPathToString) - LOOKUP(xrCreateActionSet) - LOOKUP(xrDestroyActionSet) - LOOKUP(xrCreateAction) - LOOKUP(xrDestroyAction) - LOOKUP(xrSuggestInteractionProfileBindings) - LOOKUP(xrAttachSessionActionSets) - LOOKUP(xrGetCurrentInteractionProfile) - LOOKUP(xrGetActionStateBoolean) - LOOKUP(xrGetActionStateFloat) - LOOKUP(xrGetActionStateVector2f) - LOOKUP(xrGetActionStatePose) - LOOKUP(xrSyncActions) - LOOKUP(xrEnumerateBoundSourcesForAction) - LOOKUP(xrGetInputSourceLocalizedName) - LOOKUP(xrApplyHapticFeedback) - LOOKUP(xrStopHapticFeedback) - LOOKUP(xrGetVisibilityMaskKHR) - -#ifdef _WIN32 - LOOKUP(xrConvertWin32PerformanceCounterToTimeKHR) - LOOKUP(xrConvertTimeToWin32PerformanceCounterKHR) - - LOOKUP(xrGetD3D11GraphicsRequirementsKHR) -#endif - - if (IsSupportingDriverExtension(instance)) - { - auto ret = mock_driver_xrGetInstanceProcAddr(instance, name, function); - if (ret != XR_ERROR_FUNCTION_UNSUPPORTED) - return ret; - } - - // LOOKUP(xrGetOpenGLESGraphicsRequirementsKHR) -#ifdef XR_USE_GRAPHICS_API_VULKAN - LOOKUP(xrGetVulkanInstanceExtensionsKHR) - LOOKUP(xrGetVulkanDeviceExtensionsKHR) - LOOKUP(xrGetVulkanGraphicsDeviceKHR) - LOOKUP(xrGetVulkanGraphicsRequirementsKHR) -#endif + if (s_runtime != nullptr && XR_SUCCESS == s_runtime->GetInstanceProcAddr(name, function)) + return XR_SUCCESS; -#undef LOOKUP + GET_PROC_ADDRESS(xrEnumerateApiLayerProperties) + GET_PROC_ADDRESS(xrEnumerateInstanceExtensionProperties) + GET_PROC_ADDRESS(xrCreateInstance) + GET_PROC_ADDRESS(xrDestroyInstance) + GET_PROC_ADDRESS(xrGetInstanceProperties) + GET_PROC_ADDRESS(xrPollEvent) + GET_PROC_ADDRESS(xrResultToString) + GET_PROC_ADDRESS(xrStructureTypeToString) + GET_PROC_ADDRESS(xrGetSystem) + GET_PROC_ADDRESS(xrGetSystemProperties) + GET_PROC_ADDRESS(xrEnumerateEnvironmentBlendModes) + GET_PROC_ADDRESS(xrCreateSession) + GET_PROC_ADDRESS(xrDestroySession) + GET_PROC_ADDRESS(xrEnumerateReferenceSpaces) + GET_PROC_ADDRESS(xrCreateReferenceSpace) + GET_PROC_ADDRESS(xrGetReferenceSpaceBoundsRect) + GET_PROC_ADDRESS(xrCreateActionSpace) + GET_PROC_ADDRESS(xrLocateSpace) + GET_PROC_ADDRESS(xrDestroySpace) + GET_PROC_ADDRESS(xrEnumerateViewConfigurations) + GET_PROC_ADDRESS(xrGetViewConfigurationProperties) + GET_PROC_ADDRESS(xrEnumerateViewConfigurationViews) + GET_PROC_ADDRESS(xrEnumerateSwapchainFormats) + GET_PROC_ADDRESS(xrCreateSwapchain) + GET_PROC_ADDRESS(xrDestroySwapchain) + GET_PROC_ADDRESS(xrEnumerateSwapchainImages) + GET_PROC_ADDRESS(xrAcquireSwapchainImage) + GET_PROC_ADDRESS(xrWaitSwapchainImage) + GET_PROC_ADDRESS(xrReleaseSwapchainImage) + GET_PROC_ADDRESS(xrBeginSession) + GET_PROC_ADDRESS(xrEndSession) + GET_PROC_ADDRESS(xrRequestExitSession) + GET_PROC_ADDRESS(xrWaitFrame) + GET_PROC_ADDRESS(xrBeginFrame) + GET_PROC_ADDRESS(xrEndFrame) + GET_PROC_ADDRESS(xrLocateViews) + GET_PROC_ADDRESS(xrStringToPath) + GET_PROC_ADDRESS(xrPathToString) + GET_PROC_ADDRESS(xrCreateActionSet) + GET_PROC_ADDRESS(xrDestroyActionSet) + GET_PROC_ADDRESS(xrCreateAction) + GET_PROC_ADDRESS(xrDestroyAction) + GET_PROC_ADDRESS(xrSuggestInteractionProfileBindings) + GET_PROC_ADDRESS(xrAttachSessionActionSets) + GET_PROC_ADDRESS(xrGetCurrentInteractionProfile) + GET_PROC_ADDRESS(xrGetActionStateBoolean) + GET_PROC_ADDRESS(xrGetActionStateFloat) + GET_PROC_ADDRESS(xrGetActionStateVector2f) + GET_PROC_ADDRESS(xrGetActionStatePose) + GET_PROC_ADDRESS(xrSyncActions) + GET_PROC_ADDRESS(xrEnumerateBoundSourcesForAction) + GET_PROC_ADDRESS(xrGetInputSourceLocalizedName) + GET_PROC_ADDRESS(xrApplyHapticFeedback) + GET_PROC_ADDRESS(xrStopHapticFeedback) + GET_PROC_ADDRESS(xrGetVisibilityMaskKHR) return XR_ERROR_FUNCTION_UNSUPPORTED; } @@ -795,3 +750,11 @@ extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API SetXRTrace(IUnityXRTr { s_Trace = trace; } + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrNegotiateLoaderRuntimeInterface(const XrNegotiateLoaderInfo* loaderInfo, XrNegotiateRuntimeRequest* runtimeRequest) +{ + runtimeRequest->getInstanceProcAddr = xrGetInstanceProcAddr; + runtimeRequest->runtimeApiVersion = XR_CURRENT_API_VERSION; + runtimeRequest->runtimeInterfaceVersion = 1; + return XR_SUCCESS; +} diff --git a/MockRuntime/Native~/openxr_loader/mock_runtime.cpp b/MockRuntime/Native~/openxr_loader/mock_runtime.cpp new file mode 100644 index 0000000..4e91d28 --- /dev/null +++ b/MockRuntime/Native~/openxr_loader/mock_runtime.cpp @@ -0,0 +1,1526 @@ +#include "mock.h" +#include + +MockRuntime::MockRuntime(XrInstance instance, MockRuntimeCreateFlags flags) +{ + this->instance = instance; + session = XR_NULL_HANDLE; + nextHandle = 1; + createFlags = flags; + currentState = XR_SESSION_STATE_UNKNOWN; + + endFrameCallback = nullptr; + + isRunning = false; + exitSessionRequested = false; + actionSetsAttached = false; + activeInteractionProfile = nullptr; + + // Default pose is identity pose + spacePoseOverriden = false; + spacePose = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f}}; + spaceLocationFlags = + XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | + XR_SPACE_LOCATION_POSITION_VALID_BIT | + XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT | + XR_SPACE_LOCATION_POSITION_TRACKED_BIT; + + XrViewStateFlags defaultStateFlags = + XR_VIEW_STATE_ORIENTATION_TRACKED_BIT | + XR_VIEW_STATE_ORIENTATION_VALID_BIT | + XR_VIEW_STATE_POSITION_TRACKED_BIT | + XR_VIEW_STATE_POSITION_VALID_BIT; + + XrViewConfigurationView defaultViewConfig{XR_TYPE_VIEW_CONFIGURATION_VIEW}; + defaultViewConfig.recommendedImageRectWidth = 1512; + defaultViewConfig.maxImageRectWidth = 1512 * 2; + defaultViewConfig.recommendedImageRectHeight = 1680; + defaultViewConfig.maxImageRectHeight = 1680 * 2; + defaultViewConfig.recommendedSwapchainSampleCount = 1; + defaultViewConfig.maxSwapchainSampleCount = 1; + + // Initialzie stereo view + MockViewConfiguration stereoViewConfig = {}; + stereoViewConfig.primary = true; + stereoViewConfig.enabled = true; + stereoViewConfig.active = true; + stereoViewConfig.stateFlags = defaultStateFlags; + stereoViewConfig.views = {{defaultViewConfig, + {{0.0f, 0.0f, 0.0f, 1.0f}, {-0.011f, 0.0f, 0.0f}}, + {-0.995535672f, 0.811128199f, 0.954059243f, -0.954661012f}}, + {defaultViewConfig, + {{0.0f, 0.0f, 0.0f, 1.0f}, {0.011f, 0.0f, 0.0f}}, + {-0.812360585f, 0.995566666f, 0.955580175f, -0.953877985f}}}; + viewConfigurations[XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO] = stereoViewConfig; + + // Add quad view poses if the extension is enabled + if ((createFlags & MR_CREATE_VARJO_QUAD_VIEWS_EXT) != 0) + { + auto& stereoView0 = viewConfigurations[XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO].views[0]; + auto& stereoView1 = viewConfigurations[XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO].views[1]; + XrViewConfigurationView quadViewConfigView = stereoView0.configuration; + quadViewConfigView.recommendedImageRectWidth /= 3; + quadViewConfigView.maxImageRectWidth /= 3; + quadViewConfigView.recommendedImageRectHeight /= 3; + quadViewConfigView.maxImageRectHeight /= 3; + + MockViewConfiguration quadViewConfig = {}; + quadViewConfig.primary = true; + quadViewConfig.enabled = true; + quadViewConfig.active = true; + quadViewConfig.stateFlags = defaultStateFlags; + quadViewConfig.views = { + stereoView0, + stereoView1, + {quadViewConfigView, + stereoView0.pose, + {stereoView0.fov.angleLeft / 3.0f, + stereoView0.fov.angleRight / 3.0f, + stereoView0.fov.angleUp / 3.0f, + stereoView0.fov.angleDown / 3.0f}}, + {quadViewConfigView, + stereoView1.pose, + {stereoView1.fov.angleLeft / 3.0f, + stereoView1.fov.angleRight / 3.0f, + stereoView1.fov.angleUp / 3.0f, + stereoView1.fov.angleDown / 3.0f}}}; + viewConfigurations[XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO] = quadViewConfig; + } + + // Add microsoft first person observer view if the extension is enabled + if ((createFlags & (MR_CREATE_MSFT_FIRST_PERSON_OBSERVER_EXT | MR_CREATE_MSFT_SECONDARY_VIEW_CONFIGURATION_EXT)) == (MR_CREATE_MSFT_FIRST_PERSON_OBSERVER_EXT | MR_CREATE_MSFT_SECONDARY_VIEW_CONFIGURATION_EXT)) + MSFTFirstPersonObserver_Init(); + + // Generate the internal strings + userPathStrings = { + "/user/hand/left", + "/user/hand/right", + "/user/head", + "/user/gamepad"}; + + InitializeInteractionProfiles(); + + if (IsConformanceAutomationEnabled()) + ConformanceAutomation_Create(); +} + +MockRuntime::~MockRuntime() +{ + if (IsConformanceAutomationEnabled()) + ConformanceAutomation_Destroy(); +} + +XrResult MockRuntime::GetReferenceSpaceBoundsRect(XrReferenceSpaceType referenceSpace, XrExtent2Df* extents) +{ + auto bounds = extentMap.find(referenceSpace); + if (bounds != extentMap.end()) + { + *extents = bounds->second; + return XR_SUCCESS; + } + + return XR_SPACE_BOUNDS_UNAVAILABLE; +} + +bool MockRuntime::ChangeSessionStateFrom(XrSessionState fromState, XrSessionState toState) +{ + if (s_Trace) + s_Trace->Trace(kXRLogTypeDebug, " - Transitioning from state %s => %s\n", to_string(fromState), to_string(toState)); + + if (!IsSessionState(fromState)) + return false; + + ChangeSessionState(toState); + return true; +} + +void MockRuntime::ChangeSessionState(XrSessionState state) +{ + if (currentState == state) + return; + + if (s_Trace) + s_Trace->Trace(kXRLogTypeDebug, " - Settings state to %s\n", to_string(state)); + + currentState = state; + + eventQueue.emplace(); + auto& evt = (XrEventDataSessionStateChanged&)eventQueue.back(); + evt.type = XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED; + evt.next = nullptr; + evt.session = session; + evt.state = state; +} + +XrResult MockRuntime::WaitFrame(const XrFrameWaitInfo* frameWaitInfo, XrFrameState* frameState) +{ + frameState->predictedDisplayPeriod = 16666000; + frameState->shouldRender = (createFlags & MR_CREATE_ALL_GFX_EXT) != 0; + + XrResult result = XR_SUCCESS; + if ((createFlags & MR_CREATE_MSFT_SECONDARY_VIEW_CONFIGURATION_EXT) != 0) + result = MSFTSecondaryViewConfiguration_WaitFrame(frameWaitInfo, frameState); + + return result; +} + +XrResult MockRuntime::EndFrame(const XrFrameEndInfo* frameEndInfo) +{ + secondaryLayersRendered = 0; + + XrResult result = XR_SUCCESS; + if ((createFlags & MR_CREATE_MSFT_SECONDARY_VIEW_CONFIGURATION_EXT) != 0) + result = MSFTSecondaryViewConfiguration_EndFrame(frameEndInfo); + + if (result != XR_SUCCESS) + return result; + + primaryLayersRendered = frameEndInfo->layerCount; + + if (nullptr != endFrameCallback) + endFrameCallback(); + + return XR_SUCCESS; +} + +XrResult MockRuntime::CreateSession(const XrSessionCreateInfo* createInfo) +{ + session = (XrSession)3; + + ChangeSessionState(XR_SESSION_STATE_IDLE); + + // TODO: STATEMANAGEMENT: If users presence is enabled then we need to do this transition at the point + // where the user is actually known to exist (i.e. presence sensor is triggered.) + ChangeSessionStateFrom(XR_SESSION_STATE_IDLE, XR_SESSION_STATE_READY); + + return XR_SUCCESS; +} + +XrResult MockRuntime::DestroySession() +{ + if (session == 0) + return XR_ERROR_HANDLE_INVALID; + + isRunning = false; + exitSessionRequested = false; + session = XR_NULL_HANDLE; + actionSetsAttached = false; + activeInteractionProfile = nullptr; + + return XR_SUCCESS; +} + +XrResult MockRuntime::BeginSession(const XrSessionBeginInfo* beginInfo) +{ + isRunning = true; + + ChangeSessionStateFrom(XR_SESSION_STATE_READY, XR_SESSION_STATE_SYNCHRONIZED); + ChangeSessionStateFrom(XR_SESSION_STATE_SYNCHRONIZED, XR_SESSION_STATE_VISIBLE); + ChangeSessionStateFrom(XR_SESSION_STATE_VISIBLE, XR_SESSION_STATE_FOCUSED); + + // Queue interaction profile change event + if (activeInteractionProfile != nullptr) + { + eventQueue.emplace(); + auto& evt = (XrEventDataInteractionProfileChanged&)eventQueue.back(); + evt.type = XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED; + evt.next = nullptr; + evt.session = session; + } + + XrResult result = XR_SUCCESS; + if ((createFlags & MR_CREATE_MSFT_SECONDARY_VIEW_CONFIGURATION_EXT) != 0) + result = MSFTSecondaryViewConfiguration_BeginSession(beginInfo); + + return result; +} + +XrResult MockRuntime::EndSession() +{ + isRunning = false; + ChangeSessionStateFrom(XR_SESSION_STATE_STOPPING, XR_SESSION_STATE_IDLE); + + if (exitSessionRequested) + { + exitSessionRequested = false; + ChangeSessionStateFrom(XR_SESSION_STATE_IDLE, XR_SESSION_STATE_EXITING); + } + + return XR_SUCCESS; +} + +XrResult MockRuntime::GetNextEvent(XrEventDataBuffer* eventData) +{ + if (!eventData) + return XR_ERROR_HANDLE_INVALID; + + if (eventQueue.size() > 0) + { + *eventData = eventQueue.front(); + if (s_Trace) + s_Trace->Trace(kXRLogTypeDebug, " - Returning event type: %s\n", to_string(eventData->type)); + eventQueue.pop(); + return XR_SUCCESS; + } + + return XR_EVENT_UNAVAILABLE; +} + +bool MockRuntime::IsStateTransitionValid(XrSessionState newState) const +{ + if (newState == XR_SESSION_STATE_LOSS_PENDING) + return true; + + switch (currentState) + { + case XR_SESSION_STATE_IDLE: + return newState == XR_SESSION_STATE_READY || newState == XR_SESSION_STATE_EXITING; + case XR_SESSION_STATE_READY: + return newState == XR_SESSION_STATE_SYNCHRONIZED; + case XR_SESSION_STATE_SYNCHRONIZED: + return newState == XR_SESSION_STATE_STOPPING || newState == XR_SESSION_STATE_VISIBLE; + case XR_SESSION_STATE_VISIBLE: + return newState == XR_SESSION_STATE_SYNCHRONIZED || newState == XR_SESSION_STATE_FOCUSED; + case XR_SESSION_STATE_FOCUSED: + return newState == XR_SESSION_STATE_VISIBLE; + case XR_SESSION_STATE_STOPPING: + return newState == XR_SESSION_STATE_IDLE; + case XR_SESSION_STATE_LOSS_PENDING: + return newState == XR_SESSION_STATE_LOSS_PENDING; + case XR_SESSION_STATE_EXITING: + return newState == XR_SESSION_STATE_IDLE; + default: + return false; + } +} + +void MockRuntime::SetMockBlendMode(XrEnvironmentBlendMode blendMode) +{ + this->blendMode = blendMode; +} + +void MockRuntime::SetExtentsForReferenceSpace(XrReferenceSpaceType referenceSpace, XrExtent2Df extents) +{ + extentMap[referenceSpace] = extents; + + // queue a reference space changed pending event + eventQueue.emplace(); + auto& evt = (XrEventDataReferenceSpaceChangePending&)eventQueue.back(); + evt.type = XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING; + evt.next = nullptr; + evt.session = session; + evt.referenceSpaceType = referenceSpace; + evt.changeTime = 0; + evt.poseValid = false; + evt.poseInPreviousSpace = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f}}; +} + +XrResult MockRuntime::CauseInstanceLoss() +{ + instanceIsLost = true; + + auto now = std::chrono::system_clock::now(); + auto killTime = now + std::chrono::seconds(5); + + eventQueue.emplace(); + auto& evt = (XrEventDataInstanceLossPending&)eventQueue.back(); + evt.type = XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING; + evt.next = nullptr; + evt.lossTime = killTime.time_since_epoch().count(); + return XR_SUCCESS; +} + +void MockRuntime::SetSpacePose(XrPosef pose, XrSpaceLocationFlags locationFlags) +{ + spacePose = pose; + spaceLocationFlags = locationFlags; + spacePoseOverriden = true; +} + +XrResult MockRuntime::LocateSpace(XrSpace space, XrSpace baseSpace, XrTime time, XrSpaceLocation* location) +{ + if (spacePoseOverriden) + { + location->pose = spacePose; + location->locationFlags = spaceLocationFlags; + return XR_SUCCESS; + } + + MockSpace* mockSpace = GetMockSpace(space); + if (nullptr == mockSpace) + return XR_ERROR_HANDLE_INVALID; + + // TODO: relative to the base space? + + location->pose = mockSpace->pose; + + if (mockSpace->action != XR_NULL_HANDLE) + { + MockAction* mockAction = GetMockAction(mockSpace->action); + if (nullptr == mockAction) + return XR_ERROR_HANDLE_INVALID; + + for (auto& binding : mockAction->bindings) + { + if (mockSpace->subActionPath != XR_NULL_PATH && GetUserPath(binding->path) != mockSpace->subActionPath) + continue; + + location->pose = binding->GetLocationPose(); + break; + } + } + + location->locationFlags = spaceLocationFlags; + return XR_SUCCESS; +} + +void MockRuntime::SetViewPose(int viewIndex, XrPosef pose, XrFovf fov, XrViewStateFlags viewStateFlags) +{ + if (viewIndex < 0 || viewIndex > 1) + return; + + MockViewConfiguration* mockViewConfig = GetMockViewConfiguration(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO); + if (nullptr == mockViewConfig) + return; + + if (viewIndex < 0 || viewIndex >= (int)mockViewConfig->views.size()) + return; + + mockViewConfig->stateFlags = viewStateFlags; + mockViewConfig->views[viewIndex].pose = pose; + mockViewConfig->views[viewIndex].fov = fov; +} + +XrResult MockRuntime::LocateViews(const XrViewLocateInfo* viewLocateInfo, XrViewState* viewState, uint32_t viewCapacityInput, uint32_t* viewCountOutput, XrView* views) +{ + if (nullptr == viewLocateInfo) + return XR_ERROR_VALIDATION_FAILURE; + + if (viewCapacityInput == 0) + return XR_SUCCESS; + + // OpenXR 1.0: The runtime must return error XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED if the given viewConfigurationType is not one of the supported type reported by xrEnumerateViewConfigurations. + MockViewConfiguration* mockViewConfiguration = GetMockViewConfiguration(viewLocateInfo->viewConfigurationType); + if (nullptr == mockViewConfiguration) + return XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED; + + if (!mockViewConfiguration->enabled) + return XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED; + + *viewCountOutput = (uint32_t)mockViewConfiguration->views.size(); + + if (viewCapacityInput == 0) + return XR_SUCCESS; + + if (viewCapacityInput < (uint32_t)mockViewConfiguration->views.size()) + return XR_ERROR_VALIDATION_FAILURE; + + viewState->viewStateFlags = mockViewConfiguration->stateFlags; + + // If the view is not active then remove the tracked bits + if (!mockViewConfiguration->active) + viewState->viewStateFlags &= ~(XR_VIEW_STATE_ORIENTATION_TRACKED_BIT | XR_VIEW_STATE_POSITION_TRACKED_BIT); + + for (uint32_t i = 0; i < mockViewConfiguration->views.size(); i++) + { + const MockView& mockView = mockViewConfiguration->views[i]; + views[i].pose = mockView.pose; + views[i].fov = mockView.fov; + } + + return XR_SUCCESS; +} + +XrResult MockRuntime::GetEndFrameStats(int* primaryLayersRendered, int* secondaryLayersRendered) +{ + *primaryLayersRendered = this->primaryLayersRendered; + *secondaryLayersRendered = this->secondaryLayersRendered; + return XR_SUCCESS; +} + +void MockRuntime::VisibilityMaskChangedKHR(XrViewConfigurationType viewConfigurationType, uint32_t viewIndex) +{ + eventQueue.emplace(); + auto& evt = (XrEventDataVisibilityMaskChangedKHR&)eventQueue.back(); + evt.type = XR_TYPE_EVENT_DATA_VISIBILITY_MASK_CHANGED_KHR; + evt.next = nullptr; + evt.session = session; + evt.viewConfigurationType = viewConfigurationType; + evt.viewIndex = viewIndex; +} + +XrResult MockRuntime::ValidateName(const char* name) const +{ + const char* nonperiod = name; + for (const char* c = name; *c; c++) + { + if ((*c >= '0' && *c <= '9') || (*c >= 'a' && *c <= 'z') || *c == '-' || *c == '_') + { + nonperiod = c; + continue; + } + else if (*c == '.') + continue; + + return XR_ERROR_PATH_FORMAT_INVALID; + } + + return XR_SUCCESS; +} + +XrResult MockRuntime::ValidatePath(const char* path) const +{ + // Path name strings must start with a single forward slash character. + if (path[0] != '/') + return XR_ERROR_PATH_FORMAT_INVALID; + + const char* c = path + 1; + const char* p = path; + const char* nonperiod = path; + const char* slash = path; + for (; *c; c++, p++) + { + // Path name strings must not contain two or more adjacent forward slash characters + // Path name strings must not contain two forward slash characters that are separated by only period characters. + if (*c == '/' && nonperiod == slash) + return XR_ERROR_PATH_FORMAT_INVALID; + + // non-period valid characters + if ((*c >= '0' && *c <= '9') || (*c >= 'a' && *c <= 'z') || *c == '-' || *c == '_') + { + nonperiod = c; + continue; + } + else if (*c == '.') + { + continue; + } + else if (*c == '/') + { + slash = c; + nonperiod = c; + continue; + } + + // Path name strings must be constructed entirely from characters on the following list (a-z|0-9|-|_|.|/) + return XR_ERROR_PATH_FORMAT_INVALID; + } + + // Path name strings must not end with a forward slash character or a dot. + if (*p == '/') + return XR_ERROR_PATH_FORMAT_INVALID; + + // OpenXR 1.0: Path name strings must not contain only period characters following the final forward slash character in the string. + if (nonperiod == slash) + return XR_ERROR_PATH_FORMAT_INVALID; + + // The maximum string length for a path name string, including the terminating \0 character, is defined by + if (c - p + 1 > XR_MAX_PATH_LENGTH) + return XR_ERROR_PATH_FORMAT_INVALID; + + return XR_SUCCESS; +} + +XrResult MockRuntime::CreateActionSet(const XrActionSetCreateInfo* createInfo, XrActionSet* actionSet) +{ + // OpenXR 1.0: If actionSetName or localizedActionSetName are empty strings, the runtime must return XR_ERROR_NAME_INVALID or XR_ERROR_LOCALIZED_NAME_INVALID + if (createInfo->actionSetName[0] == 0) + return XR_ERROR_NAME_INVALID; + if (createInfo->localizedActionSetName[0] == 0) + return XR_ERROR_LOCALIZED_NAME_INVALID; + + // OpenXR 1.0: actionSetName must be a null-terminated UTF-8 string whose length is less than or equal to XR_MAX_ACTION_SET_NAME_SIZE + if (strlen(createInfo->actionSetName) >= XR_MAX_ACTION_SET_NAME_SIZE) + return XR_ERROR_NAME_INVALID; + + // OpenXR 1.0: localizedActionSetName must be a null-terminated UTF-8 string whose length is less than or equal to XR_MAX_LOCALIZED_ACTION_SET_NAME_SIZE + if (strlen(createInfo->localizedActionSetName) >= XR_MAX_LOCALIZED_ACTION_SET_NAME_SIZE) + return XR_ERROR_LOCALIZED_NAME_INVALID; + + // OpenXR 1.0: If actionSetName contains characters which are not allowed in a single level of a well-formed path string, the runtime must return XR_ERROR_PATH_FORMAT_INVALID + CHECK_SUCCESS(ValidateName(createInfo->actionSetName)); + + // OpenXR 1.0: If actionSetName or localizedActionSetName are duplicates of the corresponding field for any existing action set in the specified instance, the runtime must return XR_ERROR_NAME_DUPLICATED or XR_ERROR_LOCALIZED_NAME_DUPLICATED respectively + for (auto it = actionSets.begin(); it != actionSets.end(); it++) + { + auto& existing = *it; + if (existing.name.compare(createInfo->actionSetName) == 0) + return XR_ERROR_NAME_DUPLICATED; + + if (existing.localizedName.compare(createInfo->localizedActionSetName) == 0) + return XR_ERROR_LOCALIZED_NAME_DUPLICATED; + } + + if (actionSets.size() >= 0xFFFE) + return XR_ERROR_LIMIT_REACHED; + + // Add a new action set. + actionSets.emplace_back(); + auto& added = actionSets.back(); + added.actionSet = (XrActionSet)actionSets.size(); + added.isDestroyed = false; + added.attached = false; + added.name = createInfo->actionSetName; + added.localizedName = createInfo->localizedActionSetName; + + // Return the action set + *actionSet = added.actionSet; + + return XR_SUCCESS; +} + +XrResult MockRuntime::DestroyActionSet(XrActionSet actionSet) +{ + // Scan the action sets for a match + for (auto it = actionSets.begin(); it != actionSets.end(); it++) + { + auto& mockActionSet = *it; + if (mockActionSet.actionSet == actionSet) + { + actionSets.erase(it); + + // TODO: The implementation must not free underlying resources for the action set while there are other valid handles that refer to those resources. The implementation may release resources for an action set when all of the action spaces for actions in that action set have been destroyed. See Action Spaces Lifetime for details. + return XR_SUCCESS; + } + } + + return XR_ERROR_HANDLE_INVALID; +} + +MockRuntime::MockViewConfiguration* MockRuntime::GetMockViewConfiguration(XrViewConfigurationType viewConfigType) +{ + auto it = viewConfigurations.find(viewConfigType); + if (it == viewConfigurations.end()) + return nullptr; + + return &it->second; +} + +MockRuntime::MockActionSet* MockRuntime::GetMockActionSet(XrActionSet actionSet) +{ + size_t actionSetIndex = ((uint64_t)actionSet) - 1; + if (actionSetIndex >= actionSets.size()) + return nullptr; + + MockActionSet* mockActionSet = &actionSets[actionSetIndex]; + if (mockActionSet->actionSet != actionSet) + return nullptr; + + return mockActionSet; +} + +XrResult MockRuntime::CreateAction(XrActionSet actionSet, const XrActionCreateInfo* createInfo, XrAction* action) +{ + MockActionSet* mockActionSet = GetMockActionSet(actionSet); + if (nullptr == mockActionSet) + return XR_ERROR_HANDLE_INVALID; + + // OpenXR 1.0: If actionSet has been included in a call to xrAttachSessionActionSets, the implementation must return XR_ERROR_ACTIONSETS_ALREADY_ATTACHED + if (mockActionSet->attached) + return XR_ERROR_ACTIONSETS_ALREADY_ATTACHED; + + // OpenXR 1.0: If actionName or localizedActionName are empty strings, the runtime must return XR_ERROR_NAME_INVALID or XR_ERROR_LOCALIZED_NAME_INVALID respectively. + if (createInfo->actionName[0] == 0) + return XR_ERROR_NAME_INVALID; + if (createInfo->localizedActionName[0] == 0) + return XR_ERROR_LOCALIZED_NAME_INVALID; + + // OpenXR 1.0: If actionSetName contains characters which are not allowed in a single level of a well-formed path string, the runtime must return XR_ERROR_PATH_FORMAT_INVALID + CHECK_SUCCESS(ValidateName(createInfo->actionName)); + + // OpenXR 1.0f: actionName must be a null-terminated UTF-8 string whose length is less than or equal to XR_MAX_ACTION_NAME_SIZE + if (strlen(createInfo->actionName) >= XR_MAX_ACTION_NAME_SIZE) + return XR_ERROR_NAME_INVALID; + + // OpenXR 1.0f: localizedActionName must be a null-terminated UTF-8 string whose length is less than or equal to XR_MAX_LOCALIZED_ACTION_NAME_SIZE + if (strlen(createInfo->localizedActionName) >= XR_MAX_LOCALIZED_ACTION_NAME_SIZE) + return XR_ERROR_LOCALIZED_NAME_INVALID; + + // OpenXR 1.0: If actionName or localizedActionName are duplicates of the corresponding field for any existing action in the specified action set, the runtime must return XR_ERROR_NAME_DUPLICATED or XR_ERROR_LOCALIZED_NAME_DUPLICATED respectively + MockAction* mockAction = nullptr; + for (auto& existingMockAction : mockActionSet->actions) + { + if (existingMockAction.isDestroyed) + { + if (existingMockAction.name == createInfo->actionName) + mockAction = &existingMockAction; + continue; + } + + if (existingMockAction.name == createInfo->actionName) + return XR_ERROR_NAME_DUPLICATED; + + if (!existingMockAction.isDestroyed && existingMockAction.localizedName == createInfo->localizedActionName) + return XR_ERROR_LOCALIZED_NAME_DUPLICATED; + } + + // OpenXR 1.0: actionType must be a valid XrActionType value + switch (createInfo->actionType) + { + case XR_ACTION_TYPE_BOOLEAN_INPUT: + case XR_ACTION_TYPE_FLOAT_INPUT: + case XR_ACTION_TYPE_VECTOR2F_INPUT: + case XR_ACTION_TYPE_POSE_INPUT: + case XR_ACTION_TYPE_VIBRATION_OUTPUT: + break; + + default: + return XR_ERROR_VALIDATION_FAILURE; + } + + if (mockActionSet->actions.size() >= 0xFFFE) + return XR_ERROR_LIMIT_REACHED; + + if (createInfo->countSubactionPaths > 0 && createInfo->subactionPaths == nullptr) + return XR_ERROR_VALIDATION_FAILURE; + + // Create a new action if we arent reusing a destroyed action + if (nullptr == mockAction) + { + mockActionSet->actions.emplace_back(); + mockAction = &mockActionSet->actions.back(); + } + + // Create a new mock action + mockAction->action = (XrAction)((uint64_t)actionSet + (mockActionSet->actions.size() << 32)); + mockAction->name = createInfo->actionName; + mockAction->localizedName = createInfo->localizedActionName; + mockAction->type = createInfo->actionType; + mockAction->isDestroyed = false; + + for (uint32_t i = 0; i < createInfo->countSubactionPaths; i++) + { + auto subactionPath = createInfo->subactionPaths[i]; + if (!IsValidUserPath(subactionPath)) + return XR_ERROR_PATH_UNSUPPORTED; + + // Do not allow duplicate sub action paths + if (std::find(mockAction->userPaths.begin(), mockAction->userPaths.end(), subactionPath) != mockAction->userPaths.end()) + return XR_ERROR_PATH_UNSUPPORTED; + + mockAction->userPaths.push_back(subactionPath); + } + + *action = mockAction->action; + + return XR_SUCCESS; +} + +XrResult MockRuntime::DestroyAction(XrAction action) +{ + MockAction* mockAction = GetMockAction(action); + if (nullptr == mockAction) + return XR_ERROR_HANDLE_INVALID; + + mockAction->isDestroyed = true; + return XR_SUCCESS; +} + +XrResult MockRuntime::RequestExitSession() +{ + if (!IsSessionRunning()) + return XR_ERROR_SESSION_NOT_RUNNING; + + ChangeSessionStateFrom(XR_SESSION_STATE_FOCUSED, XR_SESSION_STATE_VISIBLE); + ChangeSessionStateFrom(XR_SESSION_STATE_VISIBLE, XR_SESSION_STATE_SYNCHRONIZED); + ChangeSessionStateFrom(XR_SESSION_STATE_SYNCHRONIZED, XR_SESSION_STATE_STOPPING); + + exitSessionRequested = true; + + return XR_SUCCESS; +} + +XrPath MockRuntime::StringToPath(const char* pathString) +{ + XrPath path = XR_NULL_PATH; + if (XR_SUCCESS != StringToPath(pathString, &path)) + return XR_NULL_PATH; + return path; +} + +XrResult MockRuntime::StringToPath(const char* pathString, XrPath* path) +{ + CHECK_SUCCESS(ValidatePath(pathString)); + + // Ensure the string is not too long + if (strlen(pathString) >= XR_MAX_PATH_LENGTH) + { + *path = XR_NULL_PATH; + return XR_ERROR_PATH_FORMAT_INVALID; + } + + // If the path contains a user path then separate the user path from the component path + XrPath result = XR_NULL_PATH; + for (size_t i = 0; i < userPathStrings.size(); i++) + { + auto& userPathString = userPathStrings[i]; + if (strncmp(pathString, userPathString.c_str(), userPathString.length()) == 0) + { + result |= (XrPath)(i + 1); + pathString += userPathString.length(); + break; + } + } + + // Search the component paths + if (pathString[0] == '/') + { + for (size_t i = 0llu; i < componentPathStrings.size(); i++) + { + if (strcmp(pathString, componentPathStrings[i].c_str()) == 0) + { + result |= (XrPath)((i + 1) << 32); + *path = result; + return XR_SUCCESS; + } + } + + componentPathStrings.push_back(pathString); + result |= (XrPath)(componentPathStrings.size() << 32); + } + + *path = result; + return XR_SUCCESS; +} + +std::string MockRuntime::PathToString(XrPath path) const +{ + size_t userPath = (size_t)(path & 0xFFFFFFFF) - 1; + size_t componentPath = (size_t)(((uint64_t)path) >> 32) - 1; + + // Both user path and component path are there. + if (userPath < userPathStrings.size() && componentPath < componentPathStrings.size()) + return userPathStrings[userPath] + componentPathStrings[componentPath]; + + if (userPath < userPathStrings.size()) + return userPathStrings[userPath]; + + if (componentPath < componentPathStrings.size()) + return componentPathStrings[componentPath]; + + return ""; +} + +XrResult MockRuntime::PathToString(XrPath path, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer) const +{ + std::string pathString = PathToString(path); + if (pathString.length() == 0) + { + *bufferCountOutput = 0; + return XR_ERROR_PATH_INVALID; + } + + if (buffer == nullptr) + { + *bufferCountOutput = (uint32_t)(pathString.length() + 1); + return XR_SUCCESS; + } + + if (pathString.length() + 1 > bufferCapacityInput) + { + *bufferCountOutput = 0; + return XR_ERROR_SIZE_INSUFFICIENT; + } + + uint32_t strSize = (uint32_t)pathString.length(); + memcpy(buffer, pathString.c_str(), pathString.length()); + + buffer[strSize] = '\0'; + *bufferCountOutput = strSize + 1; + + return XR_SUCCESS; +} + +bool MockRuntime::IsValidHandle(XrPath path) const +{ + if (XR_NULL_PATH == path) + return false; + + size_t userPath = (size_t)(path & 0xFFFFFFFF); + size_t componentPath = (size_t)(path >> 32); + return userPath <= userPathStrings.size() && componentPath <= componentPathStrings.size(); +} + +XrPath MockRuntime::AppendPath(XrPath path, const char* append) +{ + std::string current = PathToString(path); + if (current.length() == 0) + return XR_NULL_PATH; + + return StringToPath((current + append).c_str()); +} + +XrPath MockRuntime::MakePath(XrPath userPath, XrPath componentPath) const +{ + if (GetUserPath(userPath) != userPath) + return XR_NULL_PATH; + + if (GetComponentPath(componentPath) != componentPath) + return XR_NULL_PATH; + + return userPath | componentPath; +} + +void MockRuntime::SetExpectedResultForFunction(const char* functionName, XrResult result) +{ + // TODO: Make thread safe + functionResultMap[functionName] = result; +} + +XrResult MockRuntime::GetExpectedResultForFunction(const char* functionName) +{ + // TODO: Make thread safe + // Assume success if nothing else specified. + XrResult ret = XR_SUCCESS; + + auto it = functionResultMap.find(functionName); + if (it != functionResultMap.end()) + { + XrResult ret = it->second; + functionResultMap.erase(it); + return ret; + } + + return ret; +} + +XrResult MockRuntime::SuggestInteractionProfileBindings(const XrInteractionProfileSuggestedBinding* suggestedBindings) +{ + // OpenXR 1.0: suggestedBindings must be a pointer to a valid XrInteractionProfileSuggestedBinding structure + if (nullptr == suggestedBindings || suggestedBindings->type != XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING || suggestedBindings->countSuggestedBindings == 0) + return XR_ERROR_VALIDATION_FAILURE; + + // OpenXR 1.0 The countSuggestedBindings parameter must be greater than 0 + if (suggestedBindings->countSuggestedBindings == 0) + return XR_ERROR_VALIDATION_FAILURE; + + // OpenXR 1.0: suggestedBindings must be a pointer to an array of countSuggestedBindings valid XrActionSuggestedBinding structures + if (suggestedBindings->suggestedBindings == nullptr) + return XR_ERROR_VALIDATION_FAILURE; + + if (actionSetsAttached) + return XR_ERROR_ACTIONSETS_ALREADY_ATTACHED; + + if (activeInteractionProfile == nullptr) + if (!SetActiveInteractionProfile(suggestedBindings->interactionProfile)) + return XR_ERROR_PATH_UNSUPPORTED; + + const MockInteractionProfile* mockProfile = GetMockInteractionProfile(suggestedBindings->interactionProfile); + if (nullptr == mockProfile) + return XR_ERROR_PATH_UNSUPPORTED; + + for (uint32_t i = 0; i < suggestedBindings->countSuggestedBindings; i++) + { + auto& suggestedBinding = suggestedBindings->suggestedBindings[i]; + + // Ensure the action is valid + MockAction* mockAction = GetMockAction(suggestedBinding.action); + if (nullptr == mockAction) + return XR_ERROR_HANDLE_INVALID; + + // If no user path was given then apply the binding to all known user paths in the action if there are any + XrPath bindingUserPath = GetUserPath(suggestedBinding.binding); + if (GetUserPath(suggestedBinding.binding) == XR_NULL_PATH) + return XR_ERROR_PATH_UNSUPPORTED; + + // Try to bind directly to a known input source + MockInputState* inputState = GetMockInputState(*mockProfile, suggestedBinding.binding, mockAction->type); + if (nullptr == inputState) + return XR_ERROR_PATH_UNSUPPORTED; + + mockAction->bindings.push_back(inputState); + } + +#if 0 + // Print all bindings + for(auto& as : actionSets) + for(auto& a : as.actions) + { + int bindingIndex = 0; + for(auto& b : a.bindings) + TRACE("[binding] %s.%s(%d) -> %s%s\n", as.name.c_str(), a.name.c_str(), bindingIndex++, PathToString(b->interactionProfile).c_str(), PathToString(b->path).c_str()); + } +#endif + + return XR_SUCCESS; +} + +MockInputState* MockRuntime::AddMockInputState(XrPath interactionPath, XrPath path, XrActionType actionType) +{ + inputStates.emplace_back(); + MockInputState& mockInputState = inputStates.back(); + mockInputState.interactionProfile = interactionPath; + mockInputState.path = path; + mockInputState.type = actionType; + mockInputState.Reset(); + return &mockInputState; +} + +bool MockRuntime::SetActiveInteractionProfile(XrPath interactionProfilePath) +{ + activeInteractionProfile = GetMockInteractionProfile(interactionProfilePath); + if (nullptr == activeInteractionProfile) + return false; + + return true; +} + +XrResult MockRuntime::AttachSessionActionSets(const XrSessionActionSetsAttachInfo* attachInfo) +{ + // OpenXR 1.0: attachInfo must be a pointer to a valid XrSessionActionSetsAttachInfo structure + if (attachInfo == nullptr || attachInfo->type != XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO) + return XR_ERROR_VALIDATION_FAILURE; + + // OpenXR 1.0: actionSets must be a pointer to an array of countActionSets valid XrActionSet handles + if (attachInfo->actionSets == nullptr) + return XR_ERROR_VALIDATION_FAILURE; + + // OpenXR 1.0: The countActionSets parameter must be greater than 0 + if (attachInfo->countActionSets == 0) + return XR_ERROR_VALIDATION_FAILURE; + + // OpenXR 1.0: The runtime must return XR_ERROR_ACTIONSETS_ALREADY_ATTACHED if xrAttachSessionActionSets is called more than once for a given session + if (actionSetsAttached) + return XR_ERROR_ACTIONSETS_ALREADY_ATTACHED; + + for (uint32_t i = 0; i < attachInfo->countActionSets; i++) + { + auto actionSet = GetMockActionSet(attachInfo->actionSets[i]); + + // OpenXR 1.0: actionSets must be a pointer to an array of countActionSets valid XrActionSet handles + if (nullptr == actionSet) + return XR_ERROR_HANDLE_INVALID; + + actionSet->attached = true; + } + + actionSetsAttached = true; + + return XR_SUCCESS; +} + +XrResult MockRuntime::GetCurrentInteractionProfile(XrPath topLevelUserPath, XrInteractionProfileState* interactionProfile) +{ + // OpenXR 1.0: If xrAttachSessionActionSets has not yet been called for the session, the runtime must return XR_ERROR_ACTIONSET_NOT_ATTACHED + if (!actionSetsAttached) + return XR_ERROR_ACTIONSET_NOT_ATTACHED; + + // OpenXR 1.0: If topLevelUserPath is not one of the device input subpaths described in section /user paths, the runtime must return XR_ERROR_PATH_UNSUPPORTED. + if (!IsValidUserPath(topLevelUserPath)) + return XR_ERROR_PATH_UNSUPPORTED; + + // Mock interaction profile is alwasy the same + interactionProfile->interactionProfile = activeInteractionProfile ? activeInteractionProfile->path : XR_NULL_PATH; + + return XR_SUCCESS; +} + +MockRuntime::MockActionSet* MockRuntime::GetMockActionSet(XrAction action) +{ + size_t actionSetIndex = (size_t)((uint64_t)action & 0xFFFF) - 1; + if (actionSetIndex >= actionSets.size()) + return nullptr; + + return &actionSets[actionSetIndex]; +} + +MockRuntime::MockAction* MockRuntime::GetMockAction(XrAction action) +{ + MockActionSet* mockActionSet = GetMockActionSet(action); + if (nullptr == mockActionSet) + return nullptr; + + size_t actionIndex = (size_t)(((uint64_t)action) >> 32) - 1; + if (actionIndex >= mockActionSet->actions.size()) + return nullptr; + + MockAction* mockAction = &mockActionSet->actions[actionIndex]; + if (mockAction->action != action) + return nullptr; + + return mockAction; +} + +MockInputState* MockRuntime::GetMockInputState(const MockInteractionProfile& mockProfile, XrPath path, XrActionType actionType, bool allowParentPath) +{ + for (auto& mockInputState : inputStates) + { + if (mockInputState.interactionProfile == mockProfile.path && mockInputState.path == path) + return &mockInputState; + } + + // Nothing was found, could be a parent path, lets check + if (allowParentPath) + { + switch (actionType) + { + case XR_ACTION_TYPE_BOOLEAN_INPUT: + { + MockInputState* mockInputState = GetMockInputState(mockProfile, AppendPath(path, "/value"), actionType, false); + if (nullptr != mockInputState) + return mockInputState; + + return GetMockInputState(mockProfile, AppendPath(path, "/click"), actionType, false); + } + + case XR_ACTION_TYPE_FLOAT_INPUT: + { + return GetMockInputState(mockProfile, AppendPath(path, "/value"), actionType, false); + } + + default: + break; + } + } + + return nullptr; +} + +XrResult MockRuntime::SyncActions(const XrActionsSyncInfo* syncInfo) +{ + // OpenXR 1.0: syncInfo must be a pointer to a valid XrActionsSyncInfo structure + if (syncInfo == nullptr || syncInfo->type != XR_TYPE_ACTIONS_SYNC_INFO) + return XR_ERROR_VALIDATION_FAILURE; + + // OpenXR 1.0: If countActiveActionSets is not 0, activeActionSets must be a pointer to an array of countActiveActionSets valid XrActiveActionSet structures + if (syncInfo->countActiveActionSets > 0 && syncInfo->activeActionSets == nullptr) + return XR_ERROR_VALIDATION_FAILURE; + + for (size_t i = 0; i < syncInfo->countActiveActionSets; i++) + { + MockActionSet* mockActionSet = GetMockActionSet(syncInfo->activeActionSets[i].actionSet); + if (nullptr == mockActionSet) + return XR_ERROR_HANDLE_INVALID; + + // OpenXR 1.0: If any action sets not attached to this session are passed to xrSyncActions it must return XR_ERROR_ACTIONSET_NOT_ATTACHED + if (!mockActionSet->attached) + return XR_ERROR_ACTIONSET_NOT_ATTACHED; + + // Update the all input sources for this action set from conformance automation if eneabled + if (IsConformanceAutomationEnabled()) + { + for (auto& mockAction : mockActionSet->actions) + { + for (auto& binding : mockAction.bindings) + { + // If a specific sub action path is given then ignore bindings that dont match that path + if (syncInfo->activeActionSets[i].subactionPath != XR_NULL_PATH && syncInfo->activeActionSets[i].subactionPath != GetUserPath(binding->path)) + continue; + + ConformanceAutomation_GetInputState(binding); + } + } + } + } + + // OpenXR 1.0: If session is not focused, the runtime must return XR_SESSION_NOT_FOCUSED, and all action states in the session must be inactive + if (currentState != XR_SESSION_STATE_FOCUSED) + return XR_SESSION_NOT_FOCUSED; + + return XR_SUCCESS; +} + +bool MockRuntime::IsActionAttached(XrAction action) +{ + if (!actionSetsAttached) + return false; + + MockActionSet* mockActionSet = GetMockActionSet(action); + if (nullptr == mockActionSet) + return false; + + return mockActionSet->attached; +} + +XrResult MockRuntime::GetActionStateFloat(const XrActionStateGetInfo* getInfo, XrActionStateFloat* state) +{ + // OpenXR 1.0: getInfo must be a pointer to a valid XrActionStateGetInfo structure + if (nullptr == getInfo || getInfo->type != XR_TYPE_ACTION_STATE_GET_INFO) + return XR_ERROR_VALIDATION_FAILURE; + + // OpenXR 1.0: state must be a pointer to an XrActionStateFloat structure + if (nullptr == state || state->type != XR_TYPE_ACTION_STATE_FLOAT) + return XR_ERROR_VALIDATION_FAILURE; + + MockAction* mockAction = GetMockAction(getInfo->action); + if (nullptr == mockAction) + return XR_ERROR_HANDLE_INVALID; + + if (!IsActionAttached(mockAction->action)) + return XR_ERROR_ACTIONSET_NOT_ATTACHED; + + float value = 0.0f; + + for (auto& binding : mockAction->bindings) + { + // If a subpath is given the binding subpath must match + if (getInfo->subactionPath != XR_NULL_PATH && GetUserPath(binding->path) != getInfo->subactionPath) + continue; + + // Must match the action type + if (!binding->IsCompatibleType(XR_ACTION_TYPE_FLOAT_INPUT)) + return XR_ERROR_ACTION_TYPE_MISMATCH; + + // OpenXR 1.0: The current state must be the state of the input with the largest absolute value + float bindingValue = binding->GetFloat(); + if (abs(bindingValue) > abs(value)) + value = bindingValue; + } + + state->currentState = value; + return XR_SUCCESS; +} + +XrResult MockRuntime::GetActionStateBoolean(const XrActionStateGetInfo* getInfo, XrActionStateBoolean* state) +{ + if (nullptr == getInfo || getInfo->type != XR_TYPE_ACTION_STATE_GET_INFO) + return XR_ERROR_VALIDATION_FAILURE; + + MockAction* mockAction = GetMockAction(getInfo->action); + if (nullptr == mockAction) + return XR_ERROR_HANDLE_INVALID; + + if (!IsActionAttached(mockAction->action)) + return XR_ERROR_ACTIONSET_NOT_ATTACHED; + + bool value = false; + + for (auto& binding : mockAction->bindings) + { + if (getInfo->subactionPath == XR_NULL_PATH || GetUserPath(binding->path) == getInfo->subactionPath) + { + if (!binding->IsCompatibleType(XR_ACTION_TYPE_BOOLEAN_INPUT)) + return XR_ERROR_ACTION_TYPE_MISMATCH; + + value |= (bool)binding->GetBoolean(); + } + } + + state->currentState = (XrBool32)value; + return XR_SUCCESS; +} + +XrResult MockRuntime::GetActionStateVector2f(const XrActionStateGetInfo* getInfo, XrActionStateVector2f* state) +{ + if (nullptr == getInfo || getInfo->type != XR_TYPE_ACTION_STATE_GET_INFO) + return XR_ERROR_VALIDATION_FAILURE; + + MockAction* mockAction = GetMockAction(getInfo->action); + if (nullptr == mockAction) + return XR_ERROR_HANDLE_INVALID; + + if (!IsActionAttached(mockAction->action)) + return XR_ERROR_ACTIONSET_NOT_ATTACHED; + + XrVector2f value = {}; + float valueLength = 0.0f; + + for (auto& binding : mockAction->bindings) + { + if (getInfo->subactionPath == XR_NULL_PATH || GetUserPath(binding->path) == getInfo->subactionPath) + { + if (!binding->IsCompatibleType(XR_ACTION_TYPE_VECTOR2F_INPUT)) + return XR_ERROR_ACTION_TYPE_MISMATCH; + + XrVector2f bindingValue = binding->GetVector2(); + float bindingValueLength = bindingValue.x * bindingValue.x + bindingValue.y * bindingValue.y; + if (bindingValueLength > valueLength) + { + valueLength = bindingValueLength; + value = bindingValue; + } + } + } + + state->currentState = value; + return XR_SUCCESS; +} + +XrResult MockRuntime::GetActionStatePose(const XrActionStateGetInfo* getInfo, XrActionStatePose* state) +{ + if (nullptr == getInfo || getInfo->type != XR_TYPE_ACTION_STATE_GET_INFO) + return XR_ERROR_VALIDATION_FAILURE; + + MockAction* mockAction = GetMockAction(getInfo->action); + if (nullptr == mockAction) + return XR_ERROR_HANDLE_INVALID; + + if (!IsActionAttached(mockAction->action)) + return XR_ERROR_ACTIONSET_NOT_ATTACHED; + + state->isActive = !mockAction->bindings.empty(); + + return XR_SUCCESS; +} + +XrResult MockRuntime::CreateReferenceSpace(const XrReferenceSpaceCreateInfo* createInfo, XrSpace* space) +{ + // OpenXR 1.0: type must be XR_TYPE_REFERENCE_SPACE_CREATE_INFO + if (createInfo == nullptr || createInfo->type != XR_TYPE_REFERENCE_SPACE_CREATE_INFO) + return XR_ERROR_VALIDATION_FAILURE; + + switch (createInfo->referenceSpaceType) + { + case XR_REFERENCE_SPACE_TYPE_LOCAL: + case XR_REFERENCE_SPACE_TYPE_STAGE: + case XR_REFERENCE_SPACE_TYPE_VIEW: + case XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT: + break; + + default: + return XR_ERROR_REFERENCE_SPACE_UNSUPPORTED; + } + + // Add the sapce and create the handle + spaces.emplace_back(); + MockSpace& mockSpace = spaces.back(); + mockSpace.pose = createInfo->poseInReferenceSpace; + mockSpace.isDestroyed = false; + mockSpace.action = XR_NULL_HANDLE; + mockSpace.subActionPath = XR_NULL_PATH; + + *space = (XrSpace)spaces.size(); + + return XR_SUCCESS; +} + +XrResult MockRuntime::CreateActionSpace(const XrActionSpaceCreateInfo* createInfo, XrSpace* space) +{ + // OpenXR 1.0: type must be XR_TYPE_ACTION_SPACE_CREATE_INFO + if (createInfo == nullptr || createInfo->type != XR_TYPE_ACTION_SPACE_CREATE_INFO) + return XR_ERROR_VALIDATION_FAILURE; + + // OpenXR 1.0: space must be a pointer to an XrSpace handle + if (nullptr == space) + return XR_ERROR_VALIDATION_FAILURE; + + // OpenXR 1.0: action must be a valid XrAction handle + MockAction* mockAction = GetMockAction(createInfo->action); + if (nullptr == mockAction) + return XR_ERROR_HANDLE_INVALID; + + // OpenXR 1.0: The runtime must return XR_ERROR_ACTION_TYPE_MISMATCH if the action provided in action is not of type XR_ACTION_TYPE_POSE_INPUT + if (mockAction->type != XR_ACTION_TYPE_POSE_INPUT) + return XR_ERROR_ACTION_TYPE_MISMATCH; + + // OpenXR 1.0: If subactionPath is a valid path not specified when the action was created the runtime must return XR_ERROR_PATH_UNSUPPORTED + if (createInfo->subactionPath != XR_NULL_PATH) + { + if (std::find(mockAction->userPaths.begin(), mockAction->userPaths.end(), createInfo->subactionPath) == mockAction->userPaths.end()) + return XR_ERROR_PATH_UNSUPPORTED; + } + + // Add the space and create the handle + spaces.emplace_back(); + MockSpace& mockSpace = spaces.back(); + mockSpace.pose = createInfo->poseInActionSpace; + mockSpace.isDestroyed = false; + mockSpace.action = mockAction->action; + mockSpace.subActionPath = createInfo->subactionPath; + + *space = (XrSpace)spaces.size(); + + return XR_SUCCESS; +} + +XrResult MockRuntime::GetInputSourceLocalizedName(const XrInputSourceLocalizedNameGetInfo* getInfo, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer) +{ + if (nullptr == getInfo || getInfo->type != XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO || getInfo->whichComponents == 0 || nullptr == bufferCountOutput) + return XR_ERROR_VALIDATION_FAILURE; + + if (bufferCapacityInput > 0 && buffer == nullptr) + return XR_ERROR_VALIDATION_FAILURE; + + if (!actionSetsAttached) + return XR_ERROR_ACTIONSET_NOT_ATTACHED; + + // TODO: we could build a better name here by looking up the binding in the interaction profile + *bufferCountOutput = 2; + buffer[0] = 'X'; + buffer[1] = 0; + + return XR_SUCCESS; +} + +XrResult MockRuntime::EnumerateBoundSourcesForAction(const XrBoundSourcesForActionEnumerateInfo* enumerateInfo, uint32_t sourceCapacityInput, uint32_t* sourceCountOutput, XrPath* sources) +{ + // OpenXR 1.0: enumerateInfo must be a pointer to a valid XrBoundSourcesForActionEnumerateInfo structure + if (enumerateInfo == nullptr || enumerateInfo->type != XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO) + return XR_ERROR_VALIDATION_FAILURE; + + // OpenXR 1.0: If sourceCapacityInput is not 0, sources must be a pointer to an array of sourceCapacityInput XrPath values + if (sourceCapacityInput > 0 && sources == nullptr) + return XR_ERROR_VALIDATION_FAILURE; + + // OpenXR 1.0: sourceCountOutput must be a pointer to a uint32_t value + if (sourceCountOutput == nullptr) + return XR_ERROR_VALIDATION_FAILURE; + + // OpenXR 1.0: action must be a valid XrAction handle + MockAction* mockAction = GetMockAction(enumerateInfo->action); + if (nullptr == mockAction) + return XR_ERROR_HANDLE_INVALID; + + // OpenXR 1.0: must return XR_ERROR_ACTIONSET_NOT_ATTACHED if passed an action in an action set never attached to the session with xrAttachSessionActionSets. + if (!actionSetsAttached || !GetMockActionSet(mockAction->action)->attached) + return XR_ERROR_ACTIONSET_NOT_ATTACHED; + + *sourceCountOutput = 0; + for (auto& mockInputState : mockAction->bindings) + { + if (sources != nullptr && *sourceCountOutput >= sourceCapacityInput) + return XR_ERROR_SIZE_INSUFFICIENT; + + (*sourceCountOutput)++; + *(sources++) = mockInputState->path; + } + + return XR_SUCCESS; +} + +XrResult MockRuntime::ApplyHapticFeedback(const XrHapticActionInfo* hapticActionInfo, const XrHapticBaseHeader* hapticFeedback) +{ + // OpenXR 1.0: hapticActionInfo must be a pointer to a valid XrHapticActionInfo structure + if (nullptr == hapticActionInfo || hapticActionInfo->type != XR_TYPE_HAPTIC_ACTION_INFO) + return XR_ERROR_VALIDATION_FAILURE; + + // OpenXR 1.0: hapticFeedback must be a pointer to a valid XrHapticBaseHeader-based structure + if (hapticFeedback == nullptr || hapticFeedback->type != XR_TYPE_HAPTIC_VIBRATION) + return XR_ERROR_VALIDATION_FAILURE; + + if (!IsActionAttached(hapticActionInfo->action)) + return XR_ERROR_ACTIONSET_NOT_ATTACHED; + + MockAction* mockAction = GetMockAction(hapticActionInfo->action); + if (nullptr == mockAction) + return XR_ERROR_HANDLE_INVALID; + + return XR_SUCCESS; +} + +XrResult MockRuntime::StopHapticFeedback(const XrHapticActionInfo* hapticActionInfo) +{ + // OpenXR 1.0: hapticActionInfo must be a pointer to a valid XrHapticActionInfo structure + if (nullptr == hapticActionInfo || hapticActionInfo->type != XR_TYPE_HAPTIC_ACTION_INFO) + return XR_ERROR_VALIDATION_FAILURE; + + if (!IsActionAttached(hapticActionInfo->action)) + return XR_ERROR_ACTIONSET_NOT_ATTACHED; + + MockAction* mockAction = GetMockAction(hapticActionInfo->action); + if (nullptr == mockAction) + return XR_ERROR_HANDLE_INVALID; + + return XR_SUCCESS; +} + +MockRuntime::MockSpace* MockRuntime::GetMockSpace(XrSpace space) +{ + if (space == 0 || ((size_t)space) > spaces.size()) + return nullptr; + + return &spaces[((size_t)space) - 1]; +} + +XrResult MockRuntime::EnumerateViewConfigurations(XrSystemId systemId, uint32_t viewConfigurationTypeCapacityInput, uint32_t* viewConfigurationTypeCountOutput, XrViewConfigurationType* viewConfigurationTypes) +{ + if (viewConfigurationTypeCountOutput == nullptr) + return XR_ERROR_VALIDATION_FAILURE; + + *viewConfigurationTypeCountOutput = (uint32_t)viewConfigurations.size(); + + if (viewConfigurationTypeCapacityInput == 0) + return XR_SUCCESS; + + if (viewConfigurationTypes == nullptr) + return XR_ERROR_VALIDATION_FAILURE; + + if (viewConfigurationTypeCapacityInput < viewConfigurations.size()) + return XR_ERROR_SIZE_INSUFFICIENT; + + int idx = 0; + for (auto view : viewConfigurations) + { + viewConfigurationTypes[idx++] = view.first; + } + + return XR_SUCCESS; +} + +XrResult MockRuntime::EnumerateViewConfigurationViews(XrSystemId systemId, XrViewConfigurationType viewConfigurationType, uint32_t viewCapacityInput, uint32_t* viewCountOutput, XrViewConfigurationView* viewsOutput) +{ + if (nullptr == viewCountOutput) + return XR_ERROR_VALIDATION_FAILURE; + + // OpenXR 1.0: The runtime must return error XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED if the given viewConfigurationType is not one of the supported type reported by xrEnumerateViewConfigurations. + MockViewConfiguration* mockViewConfig = GetMockViewConfiguration(viewConfigurationType); + if (nullptr == mockViewConfig) + return XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED; + + *viewCountOutput = (uint32_t)mockViewConfig->views.size(); + + // Just requesting the number of views + if (viewCapacityInput == 0) + return XR_SUCCESS; + + if (viewsOutput == nullptr) + return XR_ERROR_VALIDATION_FAILURE; + + for (size_t i = 0; i < mockViewConfig->views.size(); i++) + { + viewsOutput[i] = mockViewConfig->views[i].configuration; + } + + return XR_SUCCESS; +} + +XrResult MockRuntime::EnumerateEnvironmentBlendModes(XrSystemId systemId, XrViewConfigurationType viewConfigurationType, uint32_t environmentBlendModeCapacityInput, uint32_t* environmentBlendModeCountOutput, XrEnvironmentBlendMode* environmentBlendModes) +{ + MockViewConfiguration* mockViewConfig = GetMockViewConfiguration(viewConfigurationType); + if (nullptr == mockViewConfig) + return XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED; + + *environmentBlendModeCountOutput = 1; + + if (environmentBlendModeCapacityInput == 0) + return XR_SUCCESS; + + if (environmentBlendModeCapacityInput < *environmentBlendModeCountOutput) + return XR_ERROR_VALIDATION_FAILURE; + + environmentBlendModes[0] = blendMode; + + return XR_SUCCESS; +} + +XrResult MockRuntime::GetInstanceProcAddr(const char* name, PFN_xrVoidFunction* function) +{ +#if defined(XR_USE_GRAPHICS_API_VULKAN) + if (IsVulkanGfx() && XR_SUCCESS == MockVulkan_GetInstanceProcAddr(name, function)) + return XR_SUCCESS; +#endif + +#if defined(XR_USE_GRAPHICS_API_D3D11) + if (IsD3D11Gfx() && XR_SUCCESS == MockD3D11_GetInstanceProcAddr(name, function)) + return XR_SUCCESS; +#endif + +#if defined(_WIN32) + if (XR_SUCCESS == MockWin32ConvertPerformanceCounterTime_GetInstanceProcAddr(name, function)) + return XR_SUCCESS; +#endif + + if (IsSupportingDriverExtension() && XR_SUCCESS == MockDriver_GetInstanceProcAddr(instance, name, function)) + return XR_SUCCESS; + + if (IsConformanceAutomationEnabled() && XR_SUCCESS == ConformanceAutomation_GetInstanceProcAddr(name, function)) + return XR_SUCCESS; + + return XR_ERROR_FUNCTION_UNSUPPORTED; +} + +XrResult MockRuntime::RegisterEndFrameCallback(PFN_EndFrameCallback callback) +{ + endFrameCallback = callback; + return XR_SUCCESS; +} diff --git a/MockRuntime/Native~/openxr_loader/mock_runtime.h b/MockRuntime/Native~/openxr_loader/mock_runtime.h new file mode 100644 index 0000000..c750fa3 --- /dev/null +++ b/MockRuntime/Native~/openxr_loader/mock_runtime.h @@ -0,0 +1,350 @@ +#pragma once + +typedef XrFlags64 MockRuntimeCreateFlags; +static const MockRuntimeCreateFlags MR_CREATE_DRIVER_EXT = 0x00000001; +static const MockRuntimeCreateFlags MR_CREATE_NULL_GFX_EXT = 0x00000002; +static const MockRuntimeCreateFlags MR_CREATE_CONFORMANCE_AUTOMATION_EXT = 0x00000004; +static const MockRuntimeCreateFlags MR_CREATE_COMPOSITION_LAYER_DEPTH_EXT = 0x00000008; +static const MockRuntimeCreateFlags MR_CREATE_VULKAN_GFX_EXT = 0x00000010; +static const MockRuntimeCreateFlags MR_CREATE_D3D11_GFX_EXT = 0x00000020; +static const MockRuntimeCreateFlags MR_CREATE_VARJO_QUAD_VIEWS_EXT = 0x00000040; +static const MockRuntimeCreateFlags MR_CREATE_MSFT_SECONDARY_VIEW_CONFIGURATION_EXT = 0x00000080; +static const MockRuntimeCreateFlags MR_CREATE_MSFT_FIRST_PERSON_OBSERVER_EXT = 0x00000100; + +static const MockRuntimeCreateFlags MR_CREATE_ALL_GFX_EXT = MR_CREATE_VULKAN_GFX_EXT | MR_CREATE_NULL_GFX_EXT | MR_CREATE_D3D11_GFX_EXT; + +class MockRuntime +{ +public: + MockRuntime(XrInstance instance, MockRuntimeCreateFlags flags); + + ~MockRuntime(); + + XrInstance GetInstance() const + { + return instance; + } + + XrSession GetSession() const + { + return session; + } + + bool HasValidSession() const + { + return session != XR_NULL_HANDLE; + } + + bool HasValidInstance() const + { + return instance != XR_NULL_HANDLE; + } + + bool IsSupportingDriverExtension() const + { + return (createFlags & MR_CREATE_DRIVER_EXT) != 0; + } + + bool IsSessionState(XrSessionState state) const + { + return currentState == state; + } + + bool IsConformanceAutomationEnabled() const + { + return (createFlags & MR_CREATE_CONFORMANCE_AUTOMATION_EXT) != 0; + } + + XrResult GetNextEvent(XrEventDataBuffer* eventData); + + XrResult CreateSession(const XrSessionCreateInfo* createInfo); + XrResult BeginSession(const XrSessionBeginInfo* beginInfo); + XrResult EndSession(); + XrResult DestroySession(); + + void SetMockSession(XrInstance instance, XrSession* session); + void SetSupportingDriverExtension(XrInstance instance, bool usingExtension); + bool IsSupportingDriverExtension(XrInstance instance); + + bool IsStateTransitionValid(XrSessionState newState) const; + bool ChangeSessionStateFrom(XrSessionState fromState, XrSessionState toState); + void ChangeSessionState(XrSessionState state); + + bool IsSessionRunning() const + { + return isRunning; + } + + void SetExpectedResultForFunction(const char* name, XrResult result); + XrResult GetExpectedResultForFunction(const char* name); + + XrResult RequestExitSession(); + bool HasExitBeenRequested() const + { + return exitSessionRequested; + } + + void SetMockBlendMode(XrEnvironmentBlendMode blendMode); + XrEnvironmentBlendMode GetMockBlendMode() const + { + return blendMode; + } + + void SetExtentsForReferenceSpace(XrReferenceSpaceType referenceSpace, XrExtent2Df extents); + XrResult GetReferenceSpaceBoundsRect(XrReferenceSpaceType referenceSpace, XrExtent2Df* extents); + + XrResult CauseInstanceLoss(); + bool IsInstanceLost(XrInstance instance) const + { + return instanceIsLost; + }; + + void SetNullGfx(bool nullGfx); + bool IsNullGfx() const + { + return (createFlags & MR_CREATE_NULL_GFX_EXT) != 0; + } + + bool IsVulkanGfx() const + { + return (createFlags & MR_CREATE_VULKAN_GFX_EXT) != 0; + } + + bool IsD3D11Gfx() const + { + return (createFlags & MR_CREATE_D3D11_GFX_EXT) != 0; + } + + XrResult WaitFrame(const XrFrameWaitInfo* frameWaitInfo, XrFrameState* frameState); + + void SetSpacePose(XrPosef pose, XrSpaceLocationFlags locationFlags); + XrResult LocateSpace(XrSpace space, XrSpace baseSpace, XrTime time, XrSpaceLocation* location); + + void SetViewPose(int viewIndex, XrPosef pose, XrFovf fov, XrViewStateFlags viewStateFlags); + + XrResult GetEndFrameStats(int* primaryLayerCount, int* secondaryLayerCount); + XrResult EndFrame(const XrFrameEndInfo* frameEndInfo); + + XrResult CreateActionSet(const XrActionSetCreateInfo* createInfo, XrActionSet* actionSet); + XrResult DestroyActionSet(XrActionSet actionSet); + XrResult CreateAction(XrActionSet actionSet, const XrActionCreateInfo* createInfo, XrAction* action); + XrResult DestroyAction(XrAction action); + XrResult SyncActions(const XrActionsSyncInfo* syncInfo); + + XrResult GetActionStateFloat(const XrActionStateGetInfo* getInfo, XrActionStateFloat* state); + XrResult GetActionStateBoolean(const XrActionStateGetInfo* getInfo, XrActionStateBoolean* state); + XrResult GetActionStateVector2f(const XrActionStateGetInfo* getInfo, XrActionStateVector2f* state); + XrResult GetActionStatePose(const XrActionStateGetInfo* getInfo, XrActionStatePose* state); + + XrResult CreateReferenceSpace(const XrReferenceSpaceCreateInfo* createInfo, XrSpace* space); + XrResult CreateActionSpace(const XrActionSpaceCreateInfo* createInfo, XrSpace* space); + + void VisibilityMaskChangedKHR(XrViewConfigurationType viewConfigurationType, uint32_t viewIndex); + + XrResult StringToPath(const char* pathString, XrPath* path); + XrPath StringToPath(const char* pathString); + + std::string PathToString(XrPath) const; + XrResult PathToString(XrPath path, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer) const; + + // Return the user path portion of the given path handle + XrPath GetUserPath(XrPath path) const + { + return path & 0x00000000FFFFFFFFull; + } + + // Return the component path portion of the given path handle + XrPath GetComponentPath(XrPath path) const + { + return path & 0xFFFFFFFF00000000ull; + } + + // Make a new path using the given user path and component path + XrPath MakePath(XrPath userPath, XrPath componentPath) const; + + // Append the given path + XrPath AppendPath(XrPath path, const char* append); + + // Returns true if the given path is a valid path handle + bool IsValidHandle(XrPath path) const; + + XrResult GetCurrentInteractionProfile(XrPath topLevelUserPath, XrInteractionProfileState* interactionProfile); + + XrResult AttachSessionActionSets(const XrSessionActionSetsAttachInfo* attachInfo); + + XrResult SuggestInteractionProfileBindings(const XrInteractionProfileSuggestedBinding* suggestedBindings); + + XrResult GetInputSourceLocalizedName(const XrInputSourceLocalizedNameGetInfo* getInfo, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer); + + XrResult LocateViews(const XrViewLocateInfo* viewLocateInfo, XrViewState* viewState, uint32_t viewCapacityInput, uint32_t* viewCountOutput, XrView* views); + + XrResult EnumerateBoundSourcesForAction(const XrBoundSourcesForActionEnumerateInfo* enumerateInfo, uint32_t sourceCapacityInput, uint32_t* sourceCountOutput, XrPath* sources); + + XrResult EnumerateViewConfigurations(XrSystemId systemId, uint32_t viewConfigurationTypeCapacityInput, uint32_t* viewConfigurationTypeCountOutput, XrViewConfigurationType* viewConfigurationTypes); + + XrResult EnumerateViewConfigurationViews(XrSystemId systemId, XrViewConfigurationType viewConfigurationType, uint32_t viewCapacityInput, uint32_t* viewCountOutput, XrViewConfigurationView* views); + + XrResult EnumerateEnvironmentBlendModes(XrSystemId systemId, XrViewConfigurationType viewConfigurationType, uint32_t environmentBlendModeCapacityInput, uint32_t* environmentBlendModeCountOutput, XrEnvironmentBlendMode* environmentBlendModes); + + XrResult ApplyHapticFeedback(const XrHapticActionInfo* hapticActionInfo, const XrHapticBaseHeader* hapticFeedback); + XrResult StopHapticFeedback(const XrHapticActionInfo* hapticActionInfo); + + XrResult GetInstanceProcAddr(const char* name, PFN_xrVoidFunction* function); + + XrResult ActivateSecondaryView(XrViewConfigurationType viewConfiguration, bool activate); + + XrResult RegisterEndFrameCallback(PFN_EndFrameCallback callback); + +private: + struct MockView + { + XrViewConfigurationView configuration; + XrPosef pose; + XrFovf fov; + }; + + struct MockViewConfiguration + { + XrViewStateFlags stateFlags; + std::vector views; + bool primary; + bool enabled; + bool active; + }; + + struct MockAction + { + XrAction action; + XrPath path; + std::string name; + std::string localizedName; + XrActionType type; + std::vector bindings; + std::vector userPaths; + bool isDestroyed; + }; + + struct MockActionSet + { + XrActionSet actionSet; + bool attached; + std::string name; + std::string localizedName; + std::vector actions; + bool isDestroyed; + }; + + struct MockInteractionInputSource + { + XrPath path; + XrActionType actionType; + }; + + struct MockInteractionProfile + { + XrPath path; + std::vector userPaths; + std::vector inputSources; + }; + + struct MockSpace + { + XrPosef pose; + bool isDestroyed; + XrAction action; + XrPath subActionPath; + }; + + uint64_t GetNextHandle() + { + return ++nextHandle; + } + + template + T GetNextHandle() + { + return (T)GetNextHandle(); + } + + bool IsValidUserPath(XrPath path) const + { + return path != XR_NULL_PATH && GetUserPath(path) == path; + } + + MockActionSet* GetMockActionSet(XrActionSet actionSet); + MockActionSet* GetMockActionSet(XrAction action); + MockAction* GetMockAction(XrAction action); + const MockInteractionProfile* GetMockInteractionProfile(XrPath interactionProfile) const; + bool IsActionAttached(XrAction action); + MockInputState* GetMockInputState(const MockInteractionProfile& mockProfile, XrPath path, XrActionType actionType, bool allowParentPath = true); + MockSpace* GetMockSpace(XrSpace space); + MockViewConfiguration* GetMockViewConfiguration(XrViewConfigurationType viewConfigType); + + XrResult ValidateName(const char* name) const; + XrResult ValidatePath(const char* path) const; + + void InitializeInteractionProfiles(); + + bool SetActiveInteractionProfile(XrPath interactionProfilePath); + MockInputState* AddMockInputState(XrPath interactionPath, XrPath path, XrActionType actionType); + + //// XR_MSFT_secondary_view_configuration + + XrResult MSFTSecondaryViewConfiguration_BeginSession(const XrSessionBeginInfo* beginInfo); + XrResult MSFTSecondaryViewConfiguration_WaitFrame(const XrFrameWaitInfo* frameWaitInfo, XrFrameState* frameState); + XrResult MSFTSecondaryViewConfiguration_EndFrame(const XrFrameEndInfo* frameEndInfo); + + std::vector secondaryViewConfigurationStates; + + //// XR_MSFT_first_person_observer + + XrResult MSFTFirstPersonObserver_Init(); + + typedef std::map ExtentMap; + + std::unordered_map functionResultMap; + std::vector interactionProfiles; + + MockRuntimeCreateFlags createFlags; + std::queue eventQueue; + XrInstance instance; + XrSession session; + XrSessionState currentState; + XrEnvironmentBlendMode blendMode{XR_ENVIRONMENT_BLEND_MODE_OPAQUE}; + bool isRunning; + bool exitSessionRequested; + bool actionSetsAttached; + + std::map viewConfigurations; + + std::vector componentPathStrings; + std::vector userPathStrings; + + ExtentMap extentMap; + + bool instanceIsLost; + bool nullGfx; + + int primaryLayersRendered; + int secondaryLayersRendered; + + bool spacePoseOverriden; + XrPosef spacePose; + XrSpaceLocationFlags spaceLocationFlags; + + uint64_t nextHandle; + + std::vector actionSets; + std::vector inputStates; + std::vector spaces; + + const MockInteractionProfile* activeInteractionProfile; + + XrPath userLeftPath; + XrPath userRightPath; + XrPath userHeadPath; + + PFN_EndFrameCallback endFrameCallback; +}; + +extern MockRuntime* s_runtime; diff --git a/MockRuntime/Native~/openxr_loader/mock_runtime_interaction_profiles.cpp b/MockRuntime/Native~/openxr_loader/mock_runtime_interaction_profiles.cpp new file mode 100644 index 0000000..0fc1a5c --- /dev/null +++ b/MockRuntime/Native~/openxr_loader/mock_runtime_interaction_profiles.cpp @@ -0,0 +1,325 @@ +#include "mock.h" + +struct MockInputSourcePath +{ + const char* path; + XrActionType type; +}; + +struct MockInteractionProfileDef +{ + const char* name; + std::vector userPaths; + std::vector inputSources; +}; + +static std::vector s_InteractionProfiles = { + // KHR Simple controller + { + "/interaction_profiles/khr/simple_controller", + {"/user/hand/left", + "/user/hand/right"}, + {{"/user/hand/left/input/select/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/menu/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/left/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/left/output/haptic", XR_ACTION_TYPE_VIBRATION_OUTPUT}, + {"/user/hand/right/input/select/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/menu/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/right/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/right/output/haptic", XR_ACTION_TYPE_VIBRATION_OUTPUT}}}, + + // Microsoft Mixed Reality Motion Controller Profile + { + "/interaction_profiles/microsoft/motion_controller", + {"/user/hand/left", + "/user/hand/right"}, + {{"/user/hand/left/input/menu/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/squeeze/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/trigger/value", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/thumbstick/x", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/thumbstick/y", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/thumbstick/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/thumbstick", XR_ACTION_TYPE_VECTOR2F_INPUT}, + {"/user/hand/left/input/trackpad/x", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/trackpad/y", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/trackpad", XR_ACTION_TYPE_VECTOR2F_INPUT}, + {"/user/hand/left/input/trackpad/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/trackpad/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/left/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/left/output/haptic", XR_ACTION_TYPE_VIBRATION_OUTPUT}, + {"/user/hand/right/input/menu/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/squeeze/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/trigger/value", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/thumbstick/x", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/thumbstick/y", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/thumbstick/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/thumbstick", XR_ACTION_TYPE_VECTOR2F_INPUT}, + {"/user/hand/right/input/trackpad/x", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/trackpad/y", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/trackpad", XR_ACTION_TYPE_VECTOR2F_INPUT}, + {"/user/hand/right/input/trackpad/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/trackpad/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/right/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/right/output/haptic", XR_ACTION_TYPE_VIBRATION_OUTPUT}}}, + + // Google Daydream Controller Profile + { + "/interaction_profiles/google/daydream_controller", + {"/user/hand/left", + "/user/hand/right"}, + {{"/user/hand/left/input/select/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/trackpad/x", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/trackpad/y", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/trackpad/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/trackpad/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/trackpad", XR_ACTION_TYPE_VECTOR2F_INPUT}, + {"/user/hand/left/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/left/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/right/input/select/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/trackpad/x", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/trackpad/y", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/trackpad/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/trackpad/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/trackpad", XR_ACTION_TYPE_VECTOR2F_INPUT}, + {"/user/hand/right/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/right/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT}}}, + + // HTC Vive Controller Profile + { + "/interaction_profiles/htc/vive_controller", + {"/user/hand/left", + "/user/hand/right"}, + {{"/user/hand/left/input/menu/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/squeeze/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/trigger/value", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/trigger/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/trackpad/x", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/trackpad/y", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/trackpad/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/trackpad/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/trackpad", XR_ACTION_TYPE_VECTOR2F_INPUT}, + {"/user/hand/left/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/left/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/left/output/haptic", XR_ACTION_TYPE_VIBRATION_OUTPUT}, + {"/user/hand/right/input/menu/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/squeeze/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/trigger/value", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/trigger/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/trackpad/x", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/trackpad/y", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/trackpad/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/trackpad/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/trackpad", XR_ACTION_TYPE_VECTOR2F_INPUT}, + {"/user/hand/right/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/right/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/right/output/haptic", XR_ACTION_TYPE_VIBRATION_OUTPUT}}}, + + // HTC Vive Pro Profile + { + "/interaction_profiles/htc/vive_pro", + {"/user/head"}, + { + {"/user/head/input/volume_up/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/head/input/volume_down/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/head/input/mute_mic/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + }}, + + // Microsoft Xbox Controller Profile + { + "/interaction_profiles/microsoft/xbox_controller", + {"/user/gamepad"}, + {{"/user/gamepad/input/menu/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/gamepad/input/view/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/gamepad/input/a/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/gamepad/input/b/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/gamepad/input/x/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/gamepad/input/y/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/gamepad/input/dpad_down/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/gamepad/input/dpad_right/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/gamepad/input/dpad_up/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/gamepad/input/dpad_left/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/gamepad/input/shoulder_left/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/gamepad/input/shoulder_right/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/gamepad/input/thumbstick_left/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/gamepad/input/thumbstick_right/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/gamepad/input/trigger_left/value", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/gamepad/input/trigger_right/value", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/gamepad/input/thumbstick_left/x", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/gamepad/input/thumbstick_left/y", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/gamepad/input/thumbstick_left", XR_ACTION_TYPE_VECTOR2F_INPUT}, + {"/user/gamepad/input/thumbstick_right/x", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/gamepad/input/thumbstick_right/y", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/gamepad/input/thumbstick_right", XR_ACTION_TYPE_VECTOR2F_INPUT}, + {"/user/gamepad/output/haptic_left", XR_ACTION_TYPE_VIBRATION_OUTPUT}, + {"/user/gamepad/output/haptic_right", XR_ACTION_TYPE_VIBRATION_OUTPUT}, + {"/user/gamepad/output/haptic_left_trigger", XR_ACTION_TYPE_VIBRATION_OUTPUT}, + {"/user/gamepad/output/haptic_right_trigger", XR_ACTION_TYPE_VIBRATION_OUTPUT}}}, + + // Oculus Go Controller Profile + { + "/interaction_profiles/oculus/go_controller", + {"/user/hand/left", + "/user/hand/right"}, + { + {"/user/hand/left/input/trigger/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/back/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/trackpad/x", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/trackpad/y", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/trackpad", XR_ACTION_TYPE_VECTOR2F_INPUT}, + {"/user/hand/left/input/trackpad/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/trackpad/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/left/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/right/input/trigger/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/back/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/trackpad/x", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/trackpad/y", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/trackpad", XR_ACTION_TYPE_VECTOR2F_INPUT}, + {"/user/hand/right/input/trackpad/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/trackpad/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/right/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT}, + }}, + + // Oculus Touch Controller Profile + { + "/interaction_profiles/oculus/touch_controller", + {"/user/hand/left", + "/user/hand/right"}, + { + {"/user/hand/left/input/x/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/x/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/y/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/y/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/menu/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/squeeze/value", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/trigger/value", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/trigger/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/thumbstick/x", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/thumbstick/y", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/thumbstick/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/thumbstick/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/thumbstick", XR_ACTION_TYPE_VECTOR2F_INPUT}, + // Rift S and Quest controllers lack thumbrests + // {"/user/hand/left/input/thumbrest/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/left/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/left/output/haptic", XR_ACTION_TYPE_VIBRATION_OUTPUT}, + {"/user/hand/right/input/a/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/a/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/b/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/b/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + // The system ("Oculus") button is reserved for system applications + // {"/user/hand/right/input/system/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/squeeze/value", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/trigger/value", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/trigger/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/thumbstick/x", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/thumbstick/y", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/thumbstick/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/thumbstick/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/thumbstick", XR_ACTION_TYPE_VECTOR2F_INPUT}, + // Rift S and Quest controllers lack thumbrests + // {"/user/hand/right/input/thumbrest/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/right/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/right/output/haptic", XR_ACTION_TYPE_VIBRATION_OUTPUT}, + }}, + + // Valve Index Controller Profile + { + "/interaction_profiles/valve/index_controller", + {"/user/hand/left", + "/user/hand/right"}, + { + {"/user/hand/left/input/a/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/a/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/b/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/b/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/squeeze/value", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/squeeze/force", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/trigger/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/trigger/value", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/trigger/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/thumbstick/x", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/thumbstick/y", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/thumbstick/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/thumbstick/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/thumbstick", XR_ACTION_TYPE_VECTOR2F_INPUT}, + {"/user/hand/left/input/trackpad/x", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/trackpad/y", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/trackpad/force", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/left/input/trackpad/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/trackpad", XR_ACTION_TYPE_VECTOR2F_INPUT}, + {"/user/hand/left/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/left/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/left/output/haptic", XR_ACTION_TYPE_VIBRATION_OUTPUT}, + {"/user/hand/right/input/a/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/a/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/b/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/b/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/squeeze/value", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/squeeze/force", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/trigger/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/trigger/value", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/trigger/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/thumbstick/x", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/thumbstick/y", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/thumbstick/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/thumbstick/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/thumbstick", XR_ACTION_TYPE_VECTOR2F_INPUT}, + {"/user/hand/right/input/trackpad/x", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/trackpad/y", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/trackpad/force", XR_ACTION_TYPE_FLOAT_INPUT}, + {"/user/hand/right/input/trackpad/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/trackpad", XR_ACTION_TYPE_VECTOR2F_INPUT}, + {"/user/hand/right/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/right/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"/user/hand/right/output/haptic", XR_ACTION_TYPE_VIBRATION_OUTPUT}, + }}, +}; + +void MockRuntime::InitializeInteractionProfiles() +{ + interactionProfiles.reserve(s_InteractionProfiles.size()); + for (MockInteractionProfileDef& def : s_InteractionProfiles) + { + interactionProfiles.emplace_back(); + MockInteractionProfile& mockProfile = interactionProfiles.back(); + mockProfile.path = StringToPath(def.name); + mockProfile.userPaths.reserve(def.userPaths.size()); + for (const char* userPathString : def.userPaths) + mockProfile.userPaths.push_back(StringToPath(userPathString)); + + mockProfile.inputSources.reserve(def.inputSources.size()); + for (MockInputSourcePath& componentDef : def.inputSources) + { + mockProfile.inputSources.push_back({StringToPath(componentDef.path), componentDef.type}); + } + } + + // Create action states for each interaction profile + inputStates.clear(); + + for (MockInteractionProfile& mockProfile : interactionProfiles) + { + inputStates.reserve(mockProfile.inputSources.size()); + for (auto& inputSource : mockProfile.inputSources) + { + AddMockInputState(mockProfile.path, inputSource.path, inputSource.actionType); + } + } +} + +const MockRuntime::MockInteractionProfile* MockRuntime::GetMockInteractionProfile(XrPath interactionProfile) const +{ + for (const MockInteractionProfile& mockProfile : interactionProfiles) + if (mockProfile.path == interactionProfile) + return &mockProfile; + + return nullptr; +} diff --git a/MockRuntime/Native~/openxr_loader/mock_state.cpp b/MockRuntime/Native~/openxr_loader/mock_state.cpp deleted file mode 100644 index c2f9f64..0000000 --- a/MockRuntime/Native~/openxr_loader/mock_state.cpp +++ /dev/null @@ -1,395 +0,0 @@ -#include "mock_state.h" -#include "mock.h" - -#include "XR/IUnityXRTrace.h" - -#include -#include -#include -#include -#include - -#include "openxr/openxr_reflection.h" - -#include "enums_to_string.h" - -IUnityXRTrace* s_Trace = nullptr; - -// TODO: Make thread safe -static std::unordered_map s_FunctionResultMap{}; - -// TODO: Make thread safe -struct MockState -{ - typedef std::map ExtentMap; - - std::queue eventQueue; - XrInstance instance; - XrSession session; - XrSessionState currentState; - XrEnvironmentBlendMode blendMode{XR_ENVIRONMENT_BLEND_MODE_OPAQUE}; - bool supportingDriverExtension; - bool isRunning; - - ExtentMap extents; - - bool instanceIsLost; - bool nullGfx; - - int primaryLayersRendered; - int secondaryLayersRendered; - - XrPosef spacePose; - XrSpaceLocationFlags spaceLocationFlags; - - XrViewStateFlags viewStateFlags; - XrPosef viewPose[2]; - XrFovf viewFov[2]; - -} s_MockState{}; - -static void ResetMockState(XrInstance instance) -{ - s_MockState = {}; - - // Default pose is identity pose - s_MockState.spacePose = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f}}; - s_MockState.spaceLocationFlags = - XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | - XR_SPACE_LOCATION_POSITION_VALID_BIT | - XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT | - XR_SPACE_LOCATION_POSITION_TRACKED_BIT; - - // Matches unity's mock hmd which is modeled after vive. - s_MockState.viewPose[0] = {{0.0f, 0.0f, 0.0f, 1.0f}, {-0.011f, 0.0f, 0.0f}}; - s_MockState.viewPose[1] = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.011f, 0.0f, 0.0f}}; - s_MockState.viewFov[0] = {-0.995535672f, 0.811128199f, 0.954059243f, -0.954661012f}; - s_MockState.viewFov[1] = {-0.812360585f, 0.995566666f, 0.955580175f, -0.953877985f}; - s_MockState.viewStateFlags = - XR_VIEW_STATE_ORIENTATION_TRACKED_BIT | - XR_VIEW_STATE_ORIENTATION_VALID_BIT | - XR_VIEW_STATE_POSITION_TRACKED_BIT | - XR_VIEW_STATE_POSITION_VALID_BIT; -} - -bool HasCurrentInstance() -{ - return s_MockState.instance != XR_NULL_HANDLE; -} - -void AddInstance(XrInstance instance) -{ - ResetMockState(instance); - s_MockState.instance = instance; -} - -void RemoveInstance(XrInstance instance) -{ - ResetMockState(instance); -} - -void SetMockSession(XrInstance instance, XrSession* session) -{ - if (s_MockState.instance == instance) - s_MockState.session = session ? *session : nullptr; -} - -XrInstance GetInstanceFromSession(XrSession session) -{ - if (s_MockState.session == session) - return s_MockState.instance; - return (XrInstance)XR_NULL_HANDLE; -} - -XrSession GetSessionFromInstance(XrInstance instance) -{ - if (s_MockState.instance == instance) - return s_MockState.session; - return (XrSession)XR_NULL_HANDLE; -} - -XrResult GetNextEvent(XrInstance instance, XrEventDataBuffer* eventData) -{ - if (!eventData) - return XR_ERROR_HANDLE_INVALID; - - if (s_MockState.eventQueue.size() > 0) - { - *eventData = s_MockState.eventQueue.front(); - if (s_Trace) - s_Trace->Trace(kXRLogTypeDebug, " - Returning event type: %s\n", to_string(eventData->type)); - s_MockState.eventQueue.pop(); - return XR_SUCCESS; - } - - return XR_EVENT_UNAVAILABLE; -} - -bool IsStateTransitionValid(XrSession session, XrSessionState newState) -{ - if (newState == XR_SESSION_STATE_LOSS_PENDING) - return true; - - switch (s_MockState.currentState) - { - case XR_SESSION_STATE_IDLE: - return newState == XR_SESSION_STATE_READY || newState == XR_SESSION_STATE_EXITING; - case XR_SESSION_STATE_READY: - return newState == XR_SESSION_STATE_SYNCHRONIZED; - case XR_SESSION_STATE_SYNCHRONIZED: - return newState == XR_SESSION_STATE_STOPPING || newState == XR_SESSION_STATE_VISIBLE; - case XR_SESSION_STATE_VISIBLE: - return newState == XR_SESSION_STATE_SYNCHRONIZED || newState == XR_SESSION_STATE_FOCUSED; - case XR_SESSION_STATE_FOCUSED: - return newState == XR_SESSION_STATE_VISIBLE; - case XR_SESSION_STATE_STOPPING: - return newState == XR_SESSION_STATE_IDLE; - case XR_SESSION_STATE_LOSS_PENDING: - return newState == XR_SESSION_STATE_LOSS_PENDING; - case XR_SESSION_STATE_EXITING: - return newState == XR_SESSION_STATE_IDLE; - default: - return false; - } -} - -bool IsSessionAtCurrentState(XrSession session, XrSessionState state) -{ - return s_MockState.currentState == state; -} - -bool ChangeSessionStateFrom(XrSession session, XrSessionState fromState, XrSessionState toState) -{ - if (s_Trace) - s_Trace->Trace(kXRLogTypeDebug, " - Transitioning from state %s => %s\n", to_string(fromState), to_string(toState)); - - if (!IsSessionAtCurrentState(session, fromState)) - return false; - - ChangeSessionState(session, toState); - return true; -} - -void ChangeSessionState(XrSession session, XrSessionState state) -{ - if (s_MockState.currentState == state) - return; - - if (s_Trace) - s_Trace->Trace(kXRLogTypeDebug, " - Settings state to %s\n", to_string(state)); - - s_MockState.currentState = state; - - s_MockState.eventQueue.emplace(); - auto& evt = *(XrEventDataSessionStateChanged*)&s_MockState.eventQueue.back(); - evt.type = XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED; - evt.next = nullptr; - evt.session = s_MockState.session; - evt.state = state; -} - -void SetSupportingDriverExtension(XrInstance instance, bool usingExtension) -{ - s_MockState.supportingDriverExtension = usingExtension; -} - -bool IsSupportingDriverExtension(XrInstance instance) -{ - return s_MockState.supportingDriverExtension; -} - -void SetSessionRunning(XrSession session, bool running) -{ - s_MockState.isRunning = running; -} - -bool IsSessionRunning(XrSession session) -{ - return s_MockState.isRunning; -} - -bool HasValidSession() -{ - return s_MockState.session != XR_NULL_HANDLE; -} - -bool HasValidInstance() -{ - return s_MockState.instance != XR_NULL_HANDLE; -} - -void SetExpectedResultForFunction(const char* functionName, XrResult result) -{ - s_FunctionResultMap[functionName] = result; -} - -XrResult GetExpectedResultForFunction(const char* functionName) -{ - // Assume success if nothing else specified. - XrResult ret = XR_SUCCESS; - - auto it = s_FunctionResultMap.find(functionName); - if (it != s_FunctionResultMap.end()) - { - XrResult ret = it->second; - s_FunctionResultMap.erase(it); - return ret; - } - - return ret; -} - -static bool s_ExitHasBeenRequested = false; - -void SetExitRequestState(bool state) -{ - s_ExitHasBeenRequested = state; -} - -bool HasExitBeenRequested() -{ - return s_ExitHasBeenRequested; -} - -void SetMockBlendMode(XrEnvironmentBlendMode blendMode) -{ - s_MockState.blendMode = blendMode; -} - -XrEnvironmentBlendMode GetMockBlendMode() -{ - return s_MockState.blendMode; -} - -void SetExtentsForReferenceSpace(XrSession session, XrReferenceSpaceType referenceSpace, XrExtent2Df extents) -{ - s_MockState.extents[referenceSpace] = extents; - - // queue a reference space changed pending event - s_MockState.eventQueue.emplace(); - - auto& evt = *(XrEventDataReferenceSpaceChangePending*)&s_MockState.eventQueue.back(); - - evt.type = XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING; - evt.next = nullptr; - evt.session = s_MockState.session; - evt.referenceSpaceType = referenceSpace; - evt.changeTime = 0; - evt.poseValid = false; - evt.poseInPreviousSpace = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f}}; -} - -bool GetExtentsForReferenceSpace(XrSession session, XrReferenceSpaceType referenceSpace, XrExtent2Df* extents) -{ - auto bounds = s_MockState.extents.find(referenceSpace); - if (bounds != s_MockState.extents.end()) - { - *extents = bounds->second; - return true; - } - return false; -} - -XrResult CauseInstanceLoss(XrInstance instance) -{ - if (instance == s_MockState.instance) - { - s_MockState.instanceIsLost = true; - s_MockState.eventQueue.emplace(); - - auto now = std::chrono::system_clock::now(); - auto killTime = now + std::chrono::seconds(5); - - auto& evt = *(XrEventDataInstanceLossPending*)&s_MockState.eventQueue.back(); - - evt.type = XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING; - evt.next = nullptr; - evt.lossTime = killTime.time_since_epoch().count(); - return XR_SUCCESS; - } - - return XR_ERROR_HANDLE_INVALID; -} - -bool InstanceLost(XrInstance instance) -{ - if (instance == s_MockState.instance) - return s_MockState.instanceIsLost; - - return false; -} - -void SetNullGfx(bool nullGfx) -{ - s_MockState.nullGfx = nullGfx; -} - -bool IsNullGfx() -{ - return s_MockState.nullGfx; -} - -void SetSpacePose(XrPosef pose, XrSpaceLocationFlags locationFlags) -{ - s_MockState.spacePose = pose; - s_MockState.spaceLocationFlags = locationFlags; -} - -XrResult LocateSpace(XrSpace space, XrSpace baseSpace, XrTime time, XrSpaceLocation* location) -{ - location->pose = s_MockState.spacePose; - location->locationFlags = s_MockState.spaceLocationFlags; - return XR_SUCCESS; -} - -void SetViewPose(int viewIndex, XrPosef pose, XrFovf fov, XrViewStateFlags viewStateFlags) -{ - if (viewIndex < 0 || viewIndex > 1) - return; - - s_MockState.viewStateFlags = viewStateFlags; - s_MockState.viewFov[viewIndex] = fov; - s_MockState.viewPose[viewIndex] = pose; -} - -XrResult LocateViews(XrSession session, const XrViewLocateInfo* viewLocateInfo, XrViewState* viewState, uint32_t viewCapacityInput, uint32_t* viewCountOutput, XrView* views) -{ - *viewCountOutput = 2; - if (viewCapacityInput == 0) - return XR_SUCCESS; - if (viewCapacityInput < *viewCountOutput) - return XR_ERROR_VALIDATION_FAILURE; - - viewState->viewStateFlags = s_MockState.viewStateFlags; - views[0].pose = s_MockState.viewPose[0]; - views[0].fov = s_MockState.viewFov[0]; - views[1].pose = s_MockState.viewPose[1]; - views[1].fov = s_MockState.viewFov[1]; - - return XR_SUCCESS; -} - -XrResult GetEndFrameStats(int* primaryLayersRendered, int* secondaryLayersRendered) -{ - *primaryLayersRendered = s_MockState.primaryLayersRendered; - *secondaryLayersRendered = s_MockState.secondaryLayersRendered; - return XR_SUCCESS; -} - -XrResult EndFrame(XrSession session, const XrFrameEndInfo* frameEndInfo) -{ - s_MockState.primaryLayersRendered = frameEndInfo->layerCount; - s_MockState.secondaryLayersRendered = frameEndInfo->next ? ((const XrFrameEndInfo*)frameEndInfo->next)->layerCount : 0; - return XR_SUCCESS; -} - -void VisibilityMaskChangedKHR(XrSession session, XrViewConfigurationType viewConfigurationType, uint32_t viewIndex) -{ - s_MockState.eventQueue.emplace(); - auto& evt = *(XrEventDataVisibilityMaskChangedKHR*)&s_MockState.eventQueue.back(); - evt.type = XR_TYPE_EVENT_DATA_VISIBILITY_MASK_CHANGED_KHR; - evt.next = nullptr; - evt.session = session; - evt.viewConfigurationType = viewConfigurationType; - evt.viewIndex = viewIndex; -} diff --git a/MockRuntime/Native~/openxr_loader/mock_state.h b/MockRuntime/Native~/openxr_loader/mock_state.h deleted file mode 100644 index 58e0c1a..0000000 --- a/MockRuntime/Native~/openxr_loader/mock_state.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "openxr/openxr.h" - -void AddInstance(XrInstance instance); -bool HasCurrentInstance(); -XrResult GetNextEvent(XrInstance instance, XrEventDataBuffer* eventData); -void RemoveInstance(XrInstance instance); - -void SetMockSession(XrInstance instance, XrSession* session); -XrInstance GetInstanceFromSession(XrSession session); -XrSession GetSessionFromInstance(XrInstance instance); -void SetSupportingDriverExtension(XrInstance instance, bool usingExtension); -bool IsSupportingDriverExtension(XrInstance instance); - -bool IsStateTransitionValid(XrSession session, XrSessionState newState); -bool IsSessionAtCurrentState(XrSession session, XrSessionState state); -bool ChangeSessionStateFrom(XrSession session, XrSessionState fromState, XrSessionState toState); -void ChangeSessionState(XrSession session, XrSessionState state); -void ChangeSessionState(XrSession session, XrSessionState state); - -void SetSessionRunning(XrSession session, bool running); -bool IsSessionRunning(XrSession session); - -bool HasValidSession(); -bool HasValidInstance(); - -void SetExpectedResultForFunction(const char* name, XrResult result); -XrResult GetExpectedResultForFunction(const char* name); - -void SetExitRequestState(bool state); -bool HasExitBeenRequested(); - -void SetMockBlendMode(XrEnvironmentBlendMode blendMode); -XrEnvironmentBlendMode GetMockBlendMode(); - -void SetExtentsForReferenceSpace(XrSession session, XrReferenceSpaceType referenceSpace, XrExtent2Df extents); -bool GetExtentsForReferenceSpace(XrSession session, XrReferenceSpaceType referenceSpace, XrExtent2Df* extents); - -XrResult CauseInstanceLoss(XrInstance instance); -bool InstanceLost(XrInstance instance); - -void SetNullGfx(bool nullGfx); -bool IsNullGfx(); - -void SetSpacePose(XrPosef pose, XrSpaceLocationFlags locationFlags); -XrResult LocateSpace(XrSpace space, XrSpace baseSpace, XrTime time, XrSpaceLocation* location); - -void SetViewPose(int viewIndex, XrPosef pose, XrFovf fov, XrViewStateFlags viewStateFlags); -XrResult LocateViews(XrSession session, const XrViewLocateInfo* viewLocateInfo, XrViewState* viewState, uint32_t viewCapacityInput, uint32_t* viewCountOutput, XrView* views); - -XrResult GetEndFrameStats(int* primaryLayerCount, int* secondaryLayerCount); -XrResult EndFrame(XrSession session, const XrFrameEndInfo* frameEndInfo); diff --git a/MockRuntime/README.md b/MockRuntime/README.md new file mode 100644 index 0000000..720b1da --- /dev/null +++ b/MockRuntime/README.md @@ -0,0 +1,30 @@ +# Mock Runtime 0.0.2 +OpenXR Runtime that allows for testing without a device. + + +## Paths + +The `XrPath` handles generated by the Mock Runtime are a combination of two identifiers with the high 32-bits being the component path and the low 32-bits being the user path. This allows for quick comparisons of portions of the path and isolates the parsing of the path to the `xrStringToPath` method. The `GetUserPath` and `GetComponentPath` methods can be used to extract the individual parts of the `XrPath`. + +## Interaction Profiles + +The list of all interaction profiles supported by the mock runtime can be found in `mock_runtime_interaction_profiles.cpp`. This list is 1.0 conformant and will be used to create a list of `MockInteractionProfile` instances in the Mock Runtime as well as generate the list of all [Input State](#input-state) instances. + +## Action Sets + +Action sets within the Mock Runtime are stored in a list within the runtime state. The `XrActionSet` handle returned from `CreateActionSet` contains the index of the action set into the action set list. To convert a `XrActionSet` handle into a `MockActionSet` use the `GetMockActionSet` method within the runtime. + +## Actions + +The Mock Runtime implements conformant action support. Since the Mock Runtime does not have any actual controllers it will instead mimic the first interaction profile it receives through suggested bindings. This means that no actions will have bindings until bindings are suggested and that `xrGetCurrentInteractionProfile` will return the first interaction profile encountered by `xrSuggestInteractionProfileBindings`. When `xrSuggestInteractionProfileBindings` is called the actions are bound to known [Input State](#input-state) which allows those values to be directly controlled via the conformance automation extension. + +The `XrAction` handle generated by `CreateAction` is a combination of the action set index (low 32-bits) that the action belongs to and the action index within the parent action set (high 32-bits). Since actions are stored as children of an action set this handle format allows an action to be found quickly without any indirect lookups. The methods `GetMockActionSet` and `GetMockAction` will convert an `XrAction` handle in to an `MockActionSet` and `MockAction` respectively. + +## Input State + +The MockRuntime manages a list of input states, one for each combination of interaction profile, user path, and input source path. These input states have an `XrActionType` to define the type of data they represent, which is defined by their interaction profile. This allows the Mock Runtime to allow binding to any input source on any known interaction profile. However this does mean that there may be multiple instances of a input source (ex. `/input/grip` may exist multiple times, for each interaction profile). The side effect of this design is that if you bind multiple interaction profiles to the same action then there will be two sources of data for that action and the [OpenXR Specification](https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#multiple_inputs) on how to resolve which to use will be applied. + +## Conformance Automation Extension + +The MockRuntime implements the conformance automation extension and allows [Input State](#input-state) values to be set. When a value is set via Conformance Automation it is temporarly stored in the extension itself rather than directly settings the equivalent value in Mock Runtime. The values stored in the extension will then be read by the MockRuntime during `xrSyncActions` and copied into the runtime state where they will persist. + diff --git a/MockRuntime/README.md.meta b/MockRuntime/README.md.meta new file mode 100644 index 0000000..22b76f4 --- /dev/null +++ b/MockRuntime/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5315f44a895de4c4e8bfe5cb24317219 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/MockRuntime/unity-mock-runtime.json b/MockRuntime/unity-mock-runtime.json new file mode 100644 index 0000000..f56b11b --- /dev/null +++ b/MockRuntime/unity-mock-runtime.json @@ -0,0 +1,9 @@ +{ + "file_format_version": "1.0.0", + "runtime": + { + "api_version": "1.0", + "name": "Unity Mock Runtime", + "library_path": ".\\Windows\\x64\\openxr_loader.dll" + } +} diff --git a/MockRuntime/unity-mock-runtime.json.meta b/MockRuntime/unity-mock-runtime.json.meta new file mode 100644 index 0000000..76ccc1f --- /dev/null +++ b/MockRuntime/unity-mock-runtime.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f4c90870f0f8d764da1dda3fb931630d +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/MockRuntime/windows.meta b/MockRuntime/windows.meta index f049bf5..244fc20 100644 --- a/MockRuntime/windows.meta +++ b/MockRuntime/windows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 43ee61987da84baeacd7f48f6ee2f7ff +guid: 4d2e107c1b234d568451630a9b085e5b folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/MockRuntime/windows/x64.meta b/MockRuntime/windows/x64.meta index 1af9ac2..57e6c91 100644 --- a/MockRuntime/windows/x64.meta +++ b/MockRuntime/windows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 69a85b06fe8d4e5aa542bc60fa3a9231 +guid: d9cc198fffce445982c96b7d873a0841 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/MockRuntime/windows/x64/openxr_loader.dll b/MockRuntime/windows/x64/openxr_loader.dll index 4133668..40eef33 100644 Binary files a/MockRuntime/windows/x64/openxr_loader.dll and b/MockRuntime/windows/x64/openxr_loader.dll differ diff --git a/MockRuntime/windows/x64/openxr_loader.dll.meta b/MockRuntime/windows/x64/openxr_loader.dll.meta index 096d862..b772833 100644 --- a/MockRuntime/windows/x64/openxr_loader.dll.meta +++ b/MockRuntime/windows/x64/openxr_loader.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ec186e7c01a84b438e0b979da392b2de +guid: 90e1ed9a9838487b9443f86513e2ae57 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/Features/Interactions/OculusTouchControllerProfile.cs b/Runtime/Features/Interactions/OculusTouchControllerProfile.cs index dc50776..b681118 100644 --- a/Runtime/Features/Interactions/OculusTouchControllerProfile.cs +++ b/Runtime/Features/Interactions/OculusTouchControllerProfile.cs @@ -145,15 +145,27 @@ public class OculusTouchController : XRControllerWithRumble /// /// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. Tthis is the device position. For the Oculus Touch device, this is both the grip and the pointer position. This value is equivalent to mapping devicePose/position. /// - [Preserve, InputControl(offset = 96, aliases = new[] { "pointerPosition", "gripPosition" })] + [Preserve, InputControl(offset = 36, aliases = new[] { "gripPosition" })] new public Vector3Control devicePosition { get; private set; } /// /// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the device orientation. For the Oculus Touch device, this is both the grip and the pointer rotation. This value is equivalent to mapping devicePose/rotation. /// - [Preserve, InputControl(offset = 108, aliases = new[] { "pointerOrientation", "pointerRotation", "gripOrientation" })] + [Preserve, InputControl(offset = 48, aliases = new[] { "gripOrientation" })] new public QuaternionControl deviceRotation { get; private set; } + /// + /// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for back compatibility with the XRSDK layouts. This is the pointer position. This value is equivalent to mapping pointerPose/position. + /// + [Preserve, InputControl(offset = 96)] + public Vector3Control pointerPosition { get; private set; } + + /// + /// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the pointer rotation. This value is equivalent to mapping pointerPose/rotation. + /// + [Preserve, InputControl(offset = 108, aliases = new[] { "pointerOrientation" })] + public QuaternionControl pointerRotation { get; private set; } + /// /// Internal call used to assign controls to the the correct element. /// @@ -181,6 +193,8 @@ protected override void FinishSetup() trackingState = GetChildControl("trackingState"); devicePosition = GetChildControl("devicePosition"); deviceRotation = GetChildControl("deviceRotation"); + pointerPosition = GetChildControl("pointerPosition"); + pointerRotation = GetChildControl("pointerRotation"); } } diff --git a/Runtime/OpenXRLoader.cs b/Runtime/OpenXRLoader.cs index e019b82..a8287a0 100644 --- a/Runtime/OpenXRLoader.cs +++ b/Runtime/OpenXRLoader.cs @@ -8,6 +8,7 @@ using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Text; +using UnityEngine.Scripting; using UnityEngine.XR.Management; using UnityEngine.XR.OpenXR.Input; using UnityEngine.XR.OpenXR.Features; @@ -18,6 +19,8 @@ #endif +[assembly: Preserve] + [assembly:InternalsVisibleTo("Unity.XR.OpenXR.TestHelpers")] [assembly:InternalsVisibleTo("Unity.XR.OpenXR.Tests")] [assembly:InternalsVisibleTo("Unity.XR.OpenXR.Tests.Editor")] @@ -127,12 +130,21 @@ bool ShouldExitEarly() private UnhandledExceptionEventHandler unhandledExceptionHandler = null; - OpenXRRestarter GetRestarter() + private static OpenXRRestarter s_RestarterInstance = null; + + internal OpenXRRestarter GetRestarter() { - var go = new GameObject("~oxrestarter"); - go.hideFlags = HideFlags.HideAndDontSave; - var ret = go.AddComponent(); - return ret; + if (s_RestarterInstance == null) + { + var go = GameObject.Find("~oxrestarter"); + if (go == null) + { + go = new GameObject("~oxrestarter"); + go.hideFlags = HideFlags.HideAndDontSave; + } + s_RestarterInstance = go.AddComponent(); + } + return s_RestarterInstance; } internal bool DisableValidationChecksOnEnteringPlaymode = false; @@ -200,6 +212,8 @@ private bool InitializeInternal () } #endif + OpenXRInput.RegisterLayouts(); + OpenXRFeature.Initialize(); if (!LoadOpenXRSymbols()) @@ -237,8 +251,6 @@ private bool InitializeInternal () OpenXRFeature.ReceiveLoaderEvent(this, OpenXRFeature.LoaderEvent.SubsystemCreate); - OpenXRInput.Initialize(); - DebugLogEnabledSpecExtensions(); Application.onBeforeRender += ProcessOpenXRMessageLoop; @@ -566,7 +578,7 @@ private void RequestOpenXRFeatures() DiagnosticReport.AddSectionBreak(section); DiagnosticReport.AddSectionEntry(section, "Features requested to be enabled", $"{count}\n{requestedLog.ToString()}"); DiagnosticReport.AddSectionBreak(section); - DiagnosticReport.AddSectionEntry(section, "Features failed to be enabled", $"{failedCount}\n{failedLog.ToString()}"); + DiagnosticReport.AddSectionEntry(section, "Features with extensions that failed to be enabled", $"{failedCount}\n{failedLog.ToString()}"); } private static void DebugLogEnabledSpecExtensions() diff --git a/Runtime/OpenXRRenderSettings.cs b/Runtime/OpenXRRenderSettings.cs index fa416dd..0856c41 100644 --- a/Runtime/OpenXRRenderSettings.cs +++ b/Runtime/OpenXRRenderSettings.cs @@ -31,8 +31,20 @@ public enum RenderMode /// public RenderMode renderMode { - get => Internal_GetRenderMode(); - set => Internal_SetRenderMode(value); + get + { + if (OpenXRLoaderBase.Instance != null) + return Internal_GetRenderMode(); + else + return m_renderMode; + } + set + { + if (OpenXRLoaderBase.Instance != null) + Internal_SetRenderMode(value); + else + m_renderMode = value; + } } /// @@ -66,14 +78,26 @@ public enum DepthSubmissionMode /// public DepthSubmissionMode depthSubmissionMode { - get => Internal_GetDepthSubmissionMode(); - set => Internal_SetDepthSubmissionMode(value); + get + { + if (OpenXRLoaderBase.Instance != null) + return Internal_GetDepthSubmissionMode(); + else + return m_depthSubmissionMode; + } + set + { + if (OpenXRLoaderBase.Instance != null) + Internal_SetDepthSubmissionMode(value); + else + m_depthSubmissionMode = value; + } } private void ApplyRenderSettings() { - renderMode = m_renderMode; - depthSubmissionMode = m_depthSubmissionMode; + Internal_SetRenderMode(m_renderMode); + Internal_SetDepthSubmissionMode(m_depthSubmissionMode); } #if UNITY_EDITOR diff --git a/Runtime/OpenXRRestarter.cs b/Runtime/OpenXRRestarter.cs index 3e60932..07fd9cc 100644 --- a/Runtime/OpenXRRestarter.cs +++ b/Runtime/OpenXRRestarter.cs @@ -15,6 +15,8 @@ internal class OpenXRRestarter : MonoBehaviour internal OpenXRLoader loader = null; internal bool shouldRestart = true; + internal Func ShouldCancelQuit = null; + internal void ShutdownLoader(OpenXRLoaderBase loader, Action shutdownCallback) { StartCoroutine(Restart(loader, false, shutdownCallback, () => {})); @@ -54,8 +56,21 @@ IEnumerator Restart(OpenXRLoaderBase loader, bool shouldRestart, Action shutdown restartCallback(); } + else + { - GameObject.Destroy(gameObject); + if (ShouldCancelQuit != null && ShouldCancelQuit.Invoke()) + { +#if UNITY_EDITOR + if (EditorApplication.isPlaying || EditorApplication.isPaused) + { + EditorApplication.ExitPlaymode(); + } +#else + Application.Quit(); +#endif + } + } } } } diff --git a/Runtime/UnitySubsystemsManifest.json b/Runtime/UnitySubsystemsManifest.json index e8fcb05..4de7567 100644 --- a/Runtime/UnitySubsystemsManifest.json +++ b/Runtime/UnitySubsystemsManifest.json @@ -1,6 +1,6 @@ { "name": "OpenXR XR Plugin", - "version": "0.1.0-preview.1", + "version": "1.1.1", "libraryName": "UnityOpenXR", "displays": [ { diff --git a/Runtime/android.meta b/Runtime/android.meta index 88dddff..5b56d93 100644 --- a/Runtime/android.meta +++ b/Runtime/android.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 1d54d21e0ec746ff93141a290163d8e8 +guid: 395bdf56336c41478c1a24556904544c folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Runtime/android/arm64.meta b/Runtime/android/arm64.meta index 46f7139..107be3d 100644 --- a/Runtime/android/arm64.meta +++ b/Runtime/android/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 73858b3d8bc84dc28053c7debf741808 +guid: ca5fb3d216d845daba725324cfa7fe86 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Runtime/android/arm64/libUnityOpenXR.so b/Runtime/android/arm64/libUnityOpenXR.so index 123e232..a27417f 100644 Binary files a/Runtime/android/arm64/libUnityOpenXR.so and b/Runtime/android/arm64/libUnityOpenXR.so differ diff --git a/Runtime/android/arm64/libUnityOpenXR.so.meta b/Runtime/android/arm64/libUnityOpenXR.so.meta index 78c7461..8a944c6 100644 --- a/Runtime/android/arm64/libUnityOpenXR.so.meta +++ b/Runtime/android/arm64/libUnityOpenXR.so.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 679995d58a7149ac93e7fd279331d39a +guid: 20f0135472aa407eb8a5406bc74e80bc PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/input/OpenXRDevice.cs b/Runtime/input/OpenXRDevice.cs index dd75933..83d0c69 100644 --- a/Runtime/input/OpenXRDevice.cs +++ b/Runtime/input/OpenXRDevice.cs @@ -1,5 +1,6 @@ using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.XR; +using UnityEngine.Scripting; using TrackingState = UnityEngine.XR.InputTrackingState; namespace UnityEngine.XR.OpenXR.Input @@ -9,7 +10,7 @@ namespace UnityEngine.XR.OpenXR.Input /// /// /// - [InputControlLayout(displayName = "OpenXR Action Map")] + [Preserve, InputControlLayout(displayName = "OpenXR Action Map")] public abstract class OpenXRDevice : UnityEngine.InputSystem.InputDevice { /// diff --git a/Runtime/input/OpenXRHmd.cs b/Runtime/input/OpenXRHmd.cs index 63aa149..eafa5d7 100644 --- a/Runtime/input/OpenXRHmd.cs +++ b/Runtime/input/OpenXRHmd.cs @@ -1,13 +1,14 @@ using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.XR; +using UnityEngine.Scripting; namespace UnityEngine.XR.OpenXR.Input { - [InputControlLayout(displayName = "OpenXR HMD")] + [Preserve, InputControlLayout(displayName = "OpenXR HMD")] internal class OpenXRHmd : XRHMD { - [InputControl] ButtonControl userPresence { get; set; } + [Preserve, InputControl] ButtonControl userPresence { get; set; } /// protected override void FinishSetup() diff --git a/Runtime/input/OpenXRInput.cs b/Runtime/input/OpenXRInput.cs index 02c3179..cbf34e5 100644 --- a/Runtime/input/OpenXRInput.cs +++ b/Runtime/input/OpenXRInput.cs @@ -1,19 +1,41 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Runtime.InteropServices; +using UnityEditor; using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.XR; using UnityEngine.XR.OpenXR.Features; namespace UnityEngine.XR.OpenXR.Input { +#if UNITY_EDITOR + [InitializeOnLoad] +#endif internal static class OpenXRInput { static List s_ActionMapsToLoad = new List(); static bool started { get; set; } + static OpenXRInput() + { +#if UNITY_EDITOR + // In the editor we need to make sure the OpenXR layouts get registered even if the user doesn't + // navigate to the project settings. The following code will register the base layouts as well + // as any enabled interaction features. + RegisterLayouts(); + + var settings = OpenXRSettings.Instance; + if (settings == null) + return; + + foreach (var feature in settings.features.OfType()) + feature.ActiveStateChanged(); +#endif + } + internal static void AddActionMap(OpenXRInteractionFeature.ActionMapConfig map) { if (started) @@ -26,7 +48,7 @@ internal static void AddActionMap(OpenXRInteractionFeature.ActionMapConfig map) s_ActionMapsToLoad.Add(map); } - internal static void Initialize() + internal static void RegisterLayouts () { InputSystem.InputSystem.RegisterLayout("Pose"); InputSystem.InputSystem.RegisterLayout(); @@ -199,4 +221,4 @@ private static unsafe bool SendToOpenXR(List \ void SendToCSharp(const char* fieldname, handlename t) \ @@ -20,3 +23,4 @@ XR_LIST_HANDLES(SEND_TO_CSHARP_HANDLES) } XR_LIST_HANDLES(SEND_TO_CSHARP_HANDLES_PTRS) +#endif diff --git a/RuntimeDebugger/android.meta b/RuntimeDebugger/android.meta index cb3cc2e..a880540 100644 --- a/RuntimeDebugger/android.meta +++ b/RuntimeDebugger/android.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a0aed9cb10f44fe59fbd664f1e5aeb36 +guid: bd73b182d2ae4bdab40fe502080a4288 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeDebugger/android/arm64.meta b/RuntimeDebugger/android/arm64.meta index 60172f3..11979f8 100644 --- a/RuntimeDebugger/android/arm64.meta +++ b/RuntimeDebugger/android/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 4d4e32c004244cc581feb8348254dbc6 +guid: 8ea7ab42ee244654ad9e62b06b6292ae folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeDebugger/android/arm64/libopenxr_runtime_debugger.so b/RuntimeDebugger/android/arm64/libopenxr_runtime_debugger.so index 09851c5..4e5c855 100644 Binary files a/RuntimeDebugger/android/arm64/libopenxr_runtime_debugger.so and b/RuntimeDebugger/android/arm64/libopenxr_runtime_debugger.so differ diff --git a/RuntimeDebugger/android/arm64/libopenxr_runtime_debugger.so.meta b/RuntimeDebugger/android/arm64/libopenxr_runtime_debugger.so.meta index c4fd6d3..d490574 100644 --- a/RuntimeDebugger/android/arm64/libopenxr_runtime_debugger.so.meta +++ b/RuntimeDebugger/android/arm64/libopenxr_runtime_debugger.so.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 1ba2245380c74623a99998b54dc577c4 +guid: 28367263d4cf411f8f237959fdaebd70 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/RuntimeDebugger/universalwindows.meta b/RuntimeDebugger/universalwindows.meta index d5b6eb1..1ffc7ae 100644 --- a/RuntimeDebugger/universalwindows.meta +++ b/RuntimeDebugger/universalwindows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3a0bcf17c03a45469d89280df079073c +guid: 90afaf74998d4b29b37bf3ebd2e756ae folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeDebugger/universalwindows/arm32.meta b/RuntimeDebugger/universalwindows/arm32.meta new file mode 100644 index 0000000..c363543 --- /dev/null +++ b/RuntimeDebugger/universalwindows/arm32.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8327dd92655949eaa28840072bdfaeeb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/RuntimeDebugger/universalwindows/arm32/openxr_runtime_debugger.dll b/RuntimeDebugger/universalwindows/arm32/openxr_runtime_debugger.dll new file mode 100644 index 0000000..270bf87 Binary files /dev/null and b/RuntimeDebugger/universalwindows/arm32/openxr_runtime_debugger.dll differ diff --git a/RuntimeDebugger/universalwindows/arm32/openxr_runtime_debugger.dll.meta b/RuntimeDebugger/universalwindows/arm32/openxr_runtime_debugger.dll.meta new file mode 100644 index 0000000..b01d112 --- /dev/null +++ b/RuntimeDebugger/universalwindows/arm32/openxr_runtime_debugger.dll.meta @@ -0,0 +1,81 @@ +fileFormatVersion: 2 +guid: 8f94b0b59d7247ecb07233b16ba6cb01 +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: diff --git a/RuntimeDebugger/universalwindows/arm64.meta b/RuntimeDebugger/universalwindows/arm64.meta index 0065659..23249c0 100644 --- a/RuntimeDebugger/universalwindows/arm64.meta +++ b/RuntimeDebugger/universalwindows/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e5d2061603104b6aa7d4fbc11140ea3a +guid: 65f6b5a41d874dd68d75f75e3b8b79fa folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeDebugger/universalwindows/arm64/openxr_runtime_debugger.dll b/RuntimeDebugger/universalwindows/arm64/openxr_runtime_debugger.dll index c9ff584..e37b34d 100644 Binary files a/RuntimeDebugger/universalwindows/arm64/openxr_runtime_debugger.dll and b/RuntimeDebugger/universalwindows/arm64/openxr_runtime_debugger.dll differ diff --git a/RuntimeDebugger/universalwindows/arm64/openxr_runtime_debugger.dll.meta b/RuntimeDebugger/universalwindows/arm64/openxr_runtime_debugger.dll.meta index aec7e65..347237a 100644 --- a/RuntimeDebugger/universalwindows/arm64/openxr_runtime_debugger.dll.meta +++ b/RuntimeDebugger/universalwindows/arm64/openxr_runtime_debugger.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9f78d107f2c746fe8a92414776c82e1c +guid: f46b77a5fef547c8a80d2210a750d95c PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/RuntimeDebugger/universalwindows/x64.meta b/RuntimeDebugger/universalwindows/x64.meta index 0f97375..2cd7f89 100644 --- a/RuntimeDebugger/universalwindows/x64.meta +++ b/RuntimeDebugger/universalwindows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: cd704a0f03f94566b2b2ff0df36899ef +guid: e92a0126112345c4946b8f055507d969 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeDebugger/universalwindows/x64/openxr_runtime_debugger.dll b/RuntimeDebugger/universalwindows/x64/openxr_runtime_debugger.dll index 1df34d6..50449ff 100644 Binary files a/RuntimeDebugger/universalwindows/x64/openxr_runtime_debugger.dll and b/RuntimeDebugger/universalwindows/x64/openxr_runtime_debugger.dll differ diff --git a/RuntimeDebugger/universalwindows/x64/openxr_runtime_debugger.dll.meta b/RuntimeDebugger/universalwindows/x64/openxr_runtime_debugger.dll.meta index 3d02d77..7182b7e 100644 --- a/RuntimeDebugger/universalwindows/x64/openxr_runtime_debugger.dll.meta +++ b/RuntimeDebugger/universalwindows/x64/openxr_runtime_debugger.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ae0cde4de2e045fa8c6daaee8e22a31b +guid: 3fd56ea156e149cca41465bce6602c5b PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/RuntimeDebugger/windows.meta b/RuntimeDebugger/windows.meta index 98eb554..51e7243 100644 --- a/RuntimeDebugger/windows.meta +++ b/RuntimeDebugger/windows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 7da8d042670146c1af39bea38216e4b6 +guid: f3b716d717614bc68262b539b9e142a7 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeDebugger/windows/x64.meta b/RuntimeDebugger/windows/x64.meta index 54eacf5..f7ff3ff 100644 --- a/RuntimeDebugger/windows/x64.meta +++ b/RuntimeDebugger/windows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: eb4acd47bc90445fb2227a7483af6c47 +guid: 4848619f2137403fa6647b673bc466cc folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeDebugger/windows/x64/openxr_runtime_debugger.dll b/RuntimeDebugger/windows/x64/openxr_runtime_debugger.dll index ebdb193..185e71c 100644 Binary files a/RuntimeDebugger/windows/x64/openxr_runtime_debugger.dll and b/RuntimeDebugger/windows/x64/openxr_runtime_debugger.dll differ diff --git a/RuntimeDebugger/windows/x64/openxr_runtime_debugger.dll.meta b/RuntimeDebugger/windows/x64/openxr_runtime_debugger.dll.meta index d2d259d..0ca51c1 100644 --- a/RuntimeDebugger/windows/x64/openxr_runtime_debugger.dll.meta +++ b/RuntimeDebugger/windows/x64/openxr_runtime_debugger.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 391ac6f767ec4fc980f489fc411694dd +guid: 5821395701ca439dba06a286b26634e3 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/RuntimeLoaders/universalwindows.meta b/RuntimeLoaders/universalwindows.meta index c7f7925..65df42b 100644 --- a/RuntimeLoaders/universalwindows.meta +++ b/RuntimeLoaders/universalwindows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b0ce06067d0440c5880d8c292cb12779 +guid: 6aa8d4405ffc42409378f588b6907eb6 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeLoaders/universalwindows/arm32.meta b/RuntimeLoaders/universalwindows/arm32.meta new file mode 100644 index 0000000..a306e9d --- /dev/null +++ b/RuntimeLoaders/universalwindows/arm32.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 39b9872754004873a55a3c26d377816d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/RuntimeLoaders/universalwindows/arm32/openxr_loader.dll b/RuntimeLoaders/universalwindows/arm32/openxr_loader.dll new file mode 100644 index 0000000..f2e4f45 Binary files /dev/null and b/RuntimeLoaders/universalwindows/arm32/openxr_loader.dll differ diff --git a/RuntimeLoaders/universalwindows/arm32/openxr_loader.dll.meta b/RuntimeLoaders/universalwindows/arm32/openxr_loader.dll.meta new file mode 100644 index 0000000..a9aba62 --- /dev/null +++ b/RuntimeLoaders/universalwindows/arm32/openxr_loader.dll.meta @@ -0,0 +1,81 @@ +fileFormatVersion: 2 +guid: b935dec802e14bd2b5d347020434d837 +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: diff --git a/RuntimeLoaders/universalwindows/arm64.meta b/RuntimeLoaders/universalwindows/arm64.meta index d29c8bc..523032b 100644 --- a/RuntimeLoaders/universalwindows/arm64.meta +++ b/RuntimeLoaders/universalwindows/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 1d8289850bfb4f498c91bd38aa13ae1f +guid: 852032ee2a66452f97ba74020c179734 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeLoaders/universalwindows/arm64/openxr_loader.dll.meta b/RuntimeLoaders/universalwindows/arm64/openxr_loader.dll.meta index cc974e3..8c7c736 100644 --- a/RuntimeLoaders/universalwindows/arm64/openxr_loader.dll.meta +++ b/RuntimeLoaders/universalwindows/arm64/openxr_loader.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: df86e652e0354d49ba0393afd3023956 +guid: 7a0beb5fe6f547a99db61b557cddbb73 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/RuntimeLoaders/universalwindows/x64.meta b/RuntimeLoaders/universalwindows/x64.meta index 1f2575b..5933b34 100644 --- a/RuntimeLoaders/universalwindows/x64.meta +++ b/RuntimeLoaders/universalwindows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f6128c1c7bc142d89e15827ff7ed07d9 +guid: ce0e0f10b3734c99a0dd2649fd780104 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeLoaders/universalwindows/x64/openxr_loader.dll.meta b/RuntimeLoaders/universalwindows/x64/openxr_loader.dll.meta index cd2e730..b9e6f3e 100644 --- a/RuntimeLoaders/universalwindows/x64/openxr_loader.dll.meta +++ b/RuntimeLoaders/universalwindows/x64/openxr_loader.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d4eba7b1ec284e0ab2b43403dc404e4c +guid: 1b9f9dece899457eb58816a9b5190423 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/RuntimeLoaders/windows.meta b/RuntimeLoaders/windows.meta index d3cd28f..f721d8d 100644 --- a/RuntimeLoaders/windows.meta +++ b/RuntimeLoaders/windows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 93ced75e85ee48028ddd2fe0ebde35ff +guid: 6b422b6c9e6b411abf416e4d6b614c4f folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeLoaders/windows/x64.meta b/RuntimeLoaders/windows/x64.meta index 1731703..d5d2ef2 100644 --- a/RuntimeLoaders/windows/x64.meta +++ b/RuntimeLoaders/windows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a6939a93267a438ba7f8f1dde917da2c +guid: 7dd625f316d748ffa98a9ea739892033 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeLoaders/windows/x64/openxr_loader.dll.meta b/RuntimeLoaders/windows/x64/openxr_loader.dll.meta index d6d6602..d2d89ec 100644 --- a/RuntimeLoaders/windows/x64/openxr_loader.dll.meta +++ b/RuntimeLoaders/windows/x64/openxr_loader.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c95ba24dd3a1425a8a2b94cd2ab50e8f +guid: d5bc2bf08bde4f37aa00c97e4c4c6cbe PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/Samples~/InterceptFeature/InterceptCreateSessionFeature.cs b/Samples~/InterceptFeature/InterceptCreateSessionFeature.cs index af93fce..8433209 100644 --- a/Samples~/InterceptFeature/InterceptCreateSessionFeature.cs +++ b/Samples~/InterceptFeature/InterceptCreateSessionFeature.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using AOT; using UnityEditor; using UnityEngine.XR.OpenXR.Features; #if UNITY_EDITOR @@ -60,6 +61,8 @@ protected override bool OnInstanceCreate(ulong xrInstance) return true; } + private delegate void OnMessageDelegate(string message); + [MonoPInvokeCallback(typeof(OnMessageDelegate))] private static void OnMessage(string message) { if (message == null) diff --git a/Samples~/InterceptFeature/InterceptFeaturePlugin/android.meta b/Samples~/InterceptFeature/InterceptFeaturePlugin/android.meta index 88fa5ff..5dbe25c 100644 --- a/Samples~/InterceptFeature/InterceptFeaturePlugin/android.meta +++ b/Samples~/InterceptFeature/InterceptFeaturePlugin/android.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2def904ecf2c4ec49219945dac20755b +guid: 30fc076ad64e446189316c510db11355 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/InterceptFeature/InterceptFeaturePlugin/android/arm64.meta b/Samples~/InterceptFeature/InterceptFeaturePlugin/android/arm64.meta index 38f21b0..b121220 100644 --- a/Samples~/InterceptFeature/InterceptFeaturePlugin/android/arm64.meta +++ b/Samples~/InterceptFeature/InterceptFeaturePlugin/android/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 875692234cf44005acad2d5994477262 +guid: 0d17a5c74b7f44df989a264edb9ccf39 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows.meta b/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows.meta index e2203b8..abc9a0b 100644 --- a/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows.meta +++ b/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c0b77f5dcd0f4d65ac5277326620b472 +guid: d7379132be59483cbc27e2f3ff54e522 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/arm32.meta b/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/arm32.meta new file mode 100644 index 0000000..d746750 --- /dev/null +++ b/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/arm32.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 77490058c07646e5b510775477513024 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/arm64.meta b/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/arm64.meta index 8a5f4dc..afe758b 100644 --- a/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/arm64.meta +++ b/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: fb347025e37b4e64b8dcf5b341a53ff0 +guid: 99d509e1a1bf45a99245045d93bfd36c folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/x64.meta b/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/x64.meta index f844075..d97ea9c 100644 --- a/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/x64.meta +++ b/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 4c0c244b936d4482a7a798817394fb56 +guid: 25063cb24aa749ad83e89766ab38c736 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/InterceptFeature/InterceptFeaturePlugin/windows.meta b/Samples~/InterceptFeature/InterceptFeaturePlugin/windows.meta index 5a432b8..040baae 100644 --- a/Samples~/InterceptFeature/InterceptFeaturePlugin/windows.meta +++ b/Samples~/InterceptFeature/InterceptFeaturePlugin/windows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 221e9016126944f98f7dca3b730054c4 +guid: cf3e47e9658f46ebbf2440852474054f folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/InterceptFeature/InterceptFeaturePlugin/windows/x64.meta b/Samples~/InterceptFeature/InterceptFeaturePlugin/windows/x64.meta index 263c6a4..d5e6825 100644 --- a/Samples~/InterceptFeature/InterceptFeaturePlugin/windows/x64.meta +++ b/Samples~/InterceptFeature/InterceptFeaturePlugin/windows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 281e06a7534749acbe5db18f52175409 +guid: 19ff3647c1f54942b97beee87599cafe folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/InterceptFeature/InterceptFeaturePlugin/windows/x64/InterceptFeaturePlugin.dll b/Samples~/InterceptFeature/InterceptFeaturePlugin/windows/x64/InterceptFeaturePlugin.dll index af8acfe..caace42 100644 Binary files a/Samples~/InterceptFeature/InterceptFeaturePlugin/windows/x64/InterceptFeaturePlugin.dll and b/Samples~/InterceptFeature/InterceptFeaturePlugin/windows/x64/InterceptFeaturePlugin.dll differ diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/android.meta b/Samples~/MeshingFeature/MeshingFeaturePlugin/android.meta index 5473c0f..561eb63 100644 --- a/Samples~/MeshingFeature/MeshingFeaturePlugin/android.meta +++ b/Samples~/MeshingFeature/MeshingFeaturePlugin/android.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 409b23454afa43818376a9974f7398b1 +guid: f62af97ab6a74f2891cacc6bb4602f10 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/android/arm64.meta b/Samples~/MeshingFeature/MeshingFeaturePlugin/android/arm64.meta index 9670a47..b065531 100644 --- a/Samples~/MeshingFeature/MeshingFeaturePlugin/android/arm64.meta +++ b/Samples~/MeshingFeature/MeshingFeaturePlugin/android/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d42daf8a2f03476eaaf7a3e73500d7be +guid: 27f4fdf38a48423cbd1c4227272bffee folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/android/arm64/libMeshingFeaturePlugin.so.meta b/Samples~/MeshingFeature/MeshingFeaturePlugin/android/arm64/libMeshingFeaturePlugin.so.meta index fff67d3..e0eba9a 100644 --- a/Samples~/MeshingFeature/MeshingFeaturePlugin/android/arm64/libMeshingFeaturePlugin.so.meta +++ b/Samples~/MeshingFeature/MeshingFeaturePlugin/android/arm64/libMeshingFeaturePlugin.so.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3df066c606264d778b663f9e0b3eca44 +guid: 7f5e312052a84f64b9123ee877d7ac2e PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows.meta b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows.meta index 7b509f4..1809426 100644 --- a/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows.meta +++ b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: db97d81ea04c41cb856750d705a6c30a +guid: 4f3058bf568349a2bdd188a87dff9a60 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/arm32.meta b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/arm32.meta new file mode 100644 index 0000000..e10ea48 --- /dev/null +++ b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/arm32.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d1ad06e24e624f68b0bbd6b71452894d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/arm64.meta b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/arm64.meta index ec29ea5..9fdaf79 100644 --- a/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/arm64.meta +++ b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 50b5e44958744c7bbf292bafeefeb1f9 +guid: b5788c2fc0b542389b29edd3d129f9e8 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/x64.meta b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/x64.meta index bc26013..f2787a9 100644 --- a/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/x64.meta +++ b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 6328b10af289474f9a3f4405b50b9e6a +guid: 38effe9126c74a748c3327b56fd1cb1d folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/x64/MeshingFeaturePlugin.dll b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/x64/MeshingFeaturePlugin.dll index 7266b57..c6b66e9 100644 Binary files a/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/x64/MeshingFeaturePlugin.dll and b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/x64/MeshingFeaturePlugin.dll differ diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/windows.meta b/Samples~/MeshingFeature/MeshingFeaturePlugin/windows.meta index dbd5435..214b9fc 100644 --- a/Samples~/MeshingFeature/MeshingFeaturePlugin/windows.meta +++ b/Samples~/MeshingFeature/MeshingFeaturePlugin/windows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 4e61667028f5414f9ebaf82f9b49957c +guid: 767508e5707d4539a2c17e6c7c5b01cd folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/windows/x64.meta b/Samples~/MeshingFeature/MeshingFeaturePlugin/windows/x64.meta index aa17fe9..a8f3dfe 100644 --- a/Samples~/MeshingFeature/MeshingFeaturePlugin/windows/x64.meta +++ b/Samples~/MeshingFeature/MeshingFeaturePlugin/windows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 822fe085b84d45b6bd30273cc0a878df +guid: c30a59a9fa1d4c92bc31a01f2eb3df7a folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/windows/x64/MeshingFeaturePlugin.dll b/Samples~/MeshingFeature/MeshingFeaturePlugin/windows/x64/MeshingFeaturePlugin.dll index dbe9280..0486f76 100644 Binary files a/Samples~/MeshingFeature/MeshingFeaturePlugin/windows/x64/MeshingFeaturePlugin.dll and b/Samples~/MeshingFeature/MeshingFeaturePlugin/windows/x64/MeshingFeaturePlugin.dll differ diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/windows/x64/MeshingFeaturePlugin.dll.meta b/Samples~/MeshingFeature/MeshingFeaturePlugin/windows/x64/MeshingFeaturePlugin.dll.meta index 22a3347..a30fdab 100644 --- a/Samples~/MeshingFeature/MeshingFeaturePlugin/windows/x64/MeshingFeaturePlugin.dll.meta +++ b/Samples~/MeshingFeature/MeshingFeaturePlugin/windows/x64/MeshingFeaturePlugin.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e71752665a4d4cf89440efc4f85de659 +guid: 3b04e62518ff4318b89da5aa6bfc7966 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/Shared.meta b/Shared.meta new file mode 100644 index 0000000..f3d3fe5 --- /dev/null +++ b/Shared.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 31f6436129f985441a5f3972f42d2555 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Shared/Native~/enums_to_string.h b/Shared/Native~/enums_to_string.h new file mode 100644 index 0000000..abca091 --- /dev/null +++ b/Shared/Native~/enums_to_string.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include +#include +#include + +// Macro to generate stringify functions for OpenXR enumerations based data provided in openxr_reflection.h +// clang-format off +#define ENUM_CASE_STR(name, val) case name: return #name; +#define MAKE_TO_STRING_FUNC(enumType) \ + inline const char* to_string(enumType e) { \ + switch (e) { \ + XR_LIST_ENUM_##enumType(ENUM_CASE_STR) \ + default: return "Unknown " #enumType; \ + } \ + } +// clang-format on + +MAKE_TO_STRING_FUNC(XrReferenceSpaceType); +MAKE_TO_STRING_FUNC(XrViewConfigurationType); +MAKE_TO_STRING_FUNC(XrEnvironmentBlendMode); +MAKE_TO_STRING_FUNC(XrSessionState); +MAKE_TO_STRING_FUNC(XrResult); +MAKE_TO_STRING_FUNC(XrFormFactor); +MAKE_TO_STRING_FUNC(XrStructureType); diff --git a/Shared/Native~/openxr_utils.h b/Shared/Native~/openxr_utils.h new file mode 100644 index 0000000..ffb12c1 --- /dev/null +++ b/Shared/Native~/openxr_utils.h @@ -0,0 +1,31 @@ +#pragma once + +template +TOUT* FindNextPointerType(TIN* typeIn, XrStructureType structureType) +{ + auto* baseStruct = reinterpret_cast(typeIn); + + // Loop over the next pointers and look for structureType + while ((baseStruct = static_cast(baseStruct->next)) != nullptr && + baseStruct->type != structureType) + { + } + + // If we found it, cast to out type. If not we'll return nullptr. + return reinterpret_cast(baseStruct); +} + +template +const TOUT* FindNextPointerType(const TIN* typeIn, XrStructureType structureType) +{ + auto* baseStruct = reinterpret_cast(typeIn); + + // Loop over the next pointers and look for structureType + while ((baseStruct = static_cast(baseStruct->next)) != nullptr && + baseStruct->type != structureType) + { + } + + // If we found it, cast to out type. If not we'll return nullptr. + return reinterpret_cast(baseStruct); +} \ No newline at end of file diff --git a/Tests/Editor/FeatureTests.cs b/Tests/Editor/FeatureTests.cs index 6e383f8..27f6b6e 100644 --- a/Tests/Editor/FeatureTests.cs +++ b/Tests/Editor/FeatureTests.cs @@ -37,7 +37,6 @@ public void CheckDefaultValues() Assert.AreEqual(mockExtInfo.Attribute.OpenxrExtensionStrings, mockExtInfo.Feature.openxrExtensionStrings); } -#if OPENXR_CI [Test] public void ValidationError() { @@ -84,7 +83,6 @@ public void ValidationError() // Close the validation window ... OpenXRProjectValidationWindow.CloseWindow(); } -#endif //OPENXR_CI [Test] public void GetFeatureByFeatureId() diff --git a/Tests/Editor/zBuildHookTests.cs b/Tests/Editor/zBuildHookTests.cs index 78ff5f5..9d28cdb 100644 --- a/Tests/Editor/zBuildHookTests.cs +++ b/Tests/Editor/zBuildHookTests.cs @@ -25,15 +25,13 @@ internal static BuildReport BuildMockPlayer() opts.targetGroup = BuildTargetGroup.Standalone; opts.locationPathName = "mocktest/mocktest.exe"; - EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Standalone, opts.target); - UnityEngine.TestTools.LogAssert.ignoreFailingMessages = true; + EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Standalone, opts.target); var report = BuildPipeline.BuildPlayer(opts); UnityEngine.TestTools.LogAssert.ignoreFailingMessages = false; return report; } -#if OPENXR_CI [Test] public void PrePostCallbacksAreReceived() { @@ -55,12 +53,16 @@ public void PrePostCallbacksAreReceived() return true; }; - BuildMockPlayer(); + var result = BuildMockPlayer(); + + if(Environment.GetEnvironmentVariable("UNITY_OPENXR_YAMATO") == "1") + Assert.IsTrue(result.summary.result == BuildResult.Succeeded); + else if (result.summary.result != BuildResult.Succeeded) + return; Assert.IsTrue(preprocessCalled); Assert.IsTrue(postprocessCalled); } -#endif //OPENXR_CI internal class BuildCallbacks : OpenXRFeatureBuildHooks { diff --git a/Tests/Runtime/LoaderTestSetup.cs b/Tests/Runtime/LoaderTestSetup.cs index 50a2fac..d9718e7 100644 --- a/Tests/Runtime/LoaderTestSetup.cs +++ b/Tests/Runtime/LoaderTestSetup.cs @@ -48,6 +48,7 @@ T GetOrCreateAsset(string path) where T : UnityEngine.ScriptableObject protected void DestroyLoaderAndSettings() { #if UNITY_EDITOR + var path = GetAssetPathForComponents(s_TempSettingsPath); var settingsPath = Path.Combine(path, $"Test_{typeof(S).Name}.asset"); AssetDatabase.DeleteAsset(settingsPath); diff --git a/Tests/Runtime/NoRuntimeTests.cs b/Tests/Runtime/NoRuntimeTests.cs index d29b39d..2c6d16a 100644 --- a/Tests/Runtime/NoRuntimeTests.cs +++ b/Tests/Runtime/NoRuntimeTests.cs @@ -30,7 +30,7 @@ public override void AfterTest() [UnityTest] [Category("Loader Tests")] - [UnityPlatform(include = new[] {RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor})] // we can't run these tests on player because only the mock loader is included - this needs the khronos loader + [UnityPlatform(include = new[] {RuntimePlatform.WindowsEditor})] // we can't run these tests on player because only the mock loader is included - this needs the khronos loader public IEnumerator NoInitNoCrash() { base.InitializeAndStart(); @@ -42,7 +42,7 @@ public IEnumerator NoInitNoCrash() [UnityTest] [Category("Loader Tests")] - [UnityPlatform(include = new[] {RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor})] + [UnityPlatform(include = new[] {RuntimePlatform.WindowsEditor})] public IEnumerator LoadRuntimeAfterNoRuntime() { base.InitializeAndStart(); diff --git a/Tests/Runtime/OpenXRLoaderSetup.cs b/Tests/Runtime/OpenXRLoaderSetup.cs index cef5c1b..30c2510 100644 --- a/Tests/Runtime/OpenXRLoaderSetup.cs +++ b/Tests/Runtime/OpenXRLoaderSetup.cs @@ -1,4 +1,5 @@ -using NUnit.Framework; +using System.Collections; +using NUnit.Framework; using System.Collections.Generic; using System.Runtime.CompilerServices; using UnityEngine; @@ -86,6 +87,7 @@ public virtual void BeforeTest() Assert.IsTrue(EnableMockRuntime(true)); #pragma warning disable CS0618 loader = XRGeneralSettings.Instance?.Manager?.loaders[0] as OpenXRLoader; + loader.GetRestarter().ShouldCancelQuit = () => false; #pragma warning restore CS0618 } @@ -95,6 +97,11 @@ public virtual void BeforeTest() [TearDown] public virtual void AfterTest() { +#pragma warning disable CS0618 + loader = XRGeneralSettings.Instance?.Manager?.loaders[0] as OpenXRLoader; + loader.GetRestarter().ShouldCancelQuit = null; +#pragma warning restore CS0618 + StopAndShutdown(); EnableMockRuntime(false); MockRuntime.Instance.TestCallback = (methodName, param) => true; diff --git a/Tests/Runtime/OpenXRRuntimeTests.cs b/Tests/Runtime/OpenXRRuntimeTests.cs index a6bd8bd..2d0fb22 100644 --- a/Tests/Runtime/OpenXRRuntimeTests.cs +++ b/Tests/Runtime/OpenXRRuntimeTests.cs @@ -132,13 +132,19 @@ public void SessionDestroyed() [Test] public void InstanceDestroyed() { + object instance = null; bool instanceDestroyed = false; MockRuntime.Instance.TestCallback = (methodName, param) => { + if (methodName == nameof(OpenXRFeature.OnInstanceCreate)) + { + instance = param; + } + if (methodName == nameof(OpenXRFeature.OnInstanceDestroy)) { instanceDestroyed = true; - Assert.AreEqual(1, param); + Assert.AreEqual(instance, param); } return true; @@ -162,12 +168,12 @@ public IEnumerator XrSpaceApp() // this function checks to see if the initial SetAppSpace call // from unity_session.cpp. if you change the default setup in unity_session.cpp // you will need to update the value here so that the handle matches. - // this also makes an assumption that the 5th space we create is the "Stage" + // this also makes an assumption that the 3rd space we create is the "Stage" // space and that the handles are deterministic. if (methodName == nameof(OpenXRFeature.OnAppSpaceChange)) { - spaceAppSet = (oldSpaceApp == 0 && (ulong) param == 5); - spaceAppRemoved = (oldSpaceApp == 5 && (ulong) param == 0); + spaceAppSet = (oldSpaceApp == 0 && (ulong) param == 3); + spaceAppRemoved = (oldSpaceApp == 3 && (ulong) param == 0); oldSpaceApp = (ulong) param; } @@ -324,8 +330,6 @@ public IEnumerator CheckSpecExtensionEnabled() public IEnumerator CheckDepthSubmissionMode([ValueSource("depthModes")] OpenXRSettings.DepthSubmissionMode depthMode) { - MockRuntime.Instance.openxrExtensionStrings = MockRuntime.XR_UNITY_mock_test; - base.InitializeAndStart(); yield return null; OpenXRSettings.Instance.depthSubmissionMode = depthMode; @@ -336,8 +340,6 @@ public IEnumerator CheckDepthSubmissionMode([ValueSource("depthModes")] [UnityTest] public IEnumerator CheckRenderMode() { - MockRuntime.Instance.openxrExtensionStrings = MockRuntime.XR_UNITY_mock_test; - base.InitializeAndStart(); yield return null; @@ -379,8 +381,6 @@ public IEnumerator CheckSpecExtensionEnabledAtXrInstanceCreated() [UnityTest] public IEnumerator CheckDisplayRestartAfterStopSendRestartEvent() { - AddExtension(MockRuntime.XR_UNITY_mock_test); - bool onBeginSessionAfterEndSessionCalled = false; bool onEndSessionCalled = false; @@ -480,6 +480,7 @@ public IEnumerator RefreshRate() } [UnityTest] + [UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.WindowsPlayer)] public IEnumerator PreInitRealGfxAPI() { // remove the null gfx device from requested extensions @@ -510,17 +511,19 @@ public IEnumerator PreInitRealGfxAPI() Assert.That(initedRealGfxApi, Is.True); } + [UnityPlatform(exclude=new[] {RuntimePlatform.OSXEditor, RuntimePlatform.OSXPlayer})] // OSX doesn't support single-pass very well, disable for test. [UnityTest] public IEnumerator CombinedFrustum() { + EnableMockDriver(); + var cameraGO = new GameObject("Test Cam"); var camera = cameraGO.AddComponent(); base.InitializeAndStart(); OpenXRSettings.Instance.renderMode = OpenXRSettings.RenderMode.SinglePassInstanced; - yield return null; - yield return null; + yield return new WaitForXrFrame(2); var displays = new List(); SubsystemManager.GetInstances(displays); @@ -567,14 +570,34 @@ public IEnumerator InvalidLocateSpace() base.InitializeAndStart(); - // Wait two frames to let the input catch up with the renderer - yield return null; - yield return null; + // Wait a few frames to let the input catch up with the renderer + yield return new WaitForXrFrame(2); MockDriver.GetEndFrameStats(out var primaryLayerCount, out var secondaryLayerCount); Assert.IsTrue(primaryLayerCount == 0); } + [UnityTest] + public IEnumerator FirstPersonObserver() + { + EnableMockDriver(); + base.InitializeAndStart(); + + MockDriver.ActivateSecondaryView(MockDriver.XrViewConfigurationType.SecondaryMonoFirstPersonObserver, true); + + yield return new WaitForXrFrame(1); + + MockDriver.GetEndFrameStats(out var primaryLayerCount, out var secondaryLayerCount); + Assert.IsTrue(secondaryLayerCount == 1); + + MockDriver.ActivateSecondaryView(MockDriver.XrViewConfigurationType.SecondaryMonoFirstPersonObserver, false); + + yield return new WaitForXrFrame(1); + + MockDriver.GetEndFrameStats(out primaryLayerCount, out secondaryLayerCount); + Assert.IsTrue(secondaryLayerCount == 0); + } + [UnityTest] public IEnumerator NullFeature() { diff --git a/Tests/Runtime/WaitForXrFrame.cs b/Tests/Runtime/WaitForXrFrame.cs new file mode 100644 index 0000000..227594e --- /dev/null +++ b/Tests/Runtime/WaitForXrFrame.cs @@ -0,0 +1,36 @@ +using UnityEngine.Assertions; +using UnityEngine.XR.OpenXR.Features.Mock; + +namespace UnityEngine.XR.OpenXR.Tests +{ + internal class WaitForXrFrame : CustomYieldInstruction + { + private int frames = 0; + + public override bool keepWaiting => frames > 0; + + public WaitForXrFrame(int frames) + { + this.frames = frames; + if (frames == 0) + return; + + // Start waiting for a new frame count + var driver = OpenXRSettings.Instance.GetFeature(); + Assert.IsNotNull(driver); + Assert.IsTrue(driver.enabled); + + MockDriver.onEndFrame += OnEndFrame; + } + + private void OnEndFrame() + { + frames--; + if (frames > 0) + return; + + frames = 0; + MockDriver.onEndFrame -= OnEndFrame; + } + } +} \ No newline at end of file diff --git a/Tests/Runtime/WaitForXrFrame.cs.meta b/Tests/Runtime/WaitForXrFrame.cs.meta new file mode 100644 index 0000000..6df73e9 --- /dev/null +++ b/Tests/Runtime/WaitForXrFrame.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c13e63b99a8248eda9d91f10eb39538d +timeCreated: 1614807146 \ No newline at end of file diff --git a/package.json b/package.json index 2102403..f477c2d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.unity.xr.openxr", "displayName": "OpenXR Plugin", - "version": "1.0.3", + "version": "1.1.1", "unity": "2020.2", "unityRelease": "0b14", "description": "OpenXR is an open, royalty-free standard developed by Khronos that aims to simplify AR/VR development by allowing developers to target a wide range of AR/VR devices. Use this plug-in to enable OpenXR in XR Plug-in Management.", @@ -34,11 +34,11 @@ } ], "upmCi": { - "footprint": "bf81a38ac02f56369ebaaa8310d0a9716e97aba6" + "footprint": "d1806d2dd73e07d979446411f7daab0dffb21fde" }, "repository": { "url": "https://github.cds.internal.unity3d.com/unity/xr.sdk.openxr.git", "type": "git", - "revision": "82546898438f3e4ef3f1e52e008ca0b93c722c9a" + "revision": "385943b78c1ad69f8096e3820beca29305960a9f" } }