diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f41d5f..311011d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,35 @@ 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.7.0] - 2023-02-21 + +### Fixed +* Fixed - Meta builds now don't include Bluetooth permissions in Android manifest by default when using Microphone class in script code. +* Fixed crash in OpenXR runtime debugger when cache size is set to 0. +* Fixed OpenXR project validation to check for correct versions of OpenGLES in Unity 2023 and up. +* Fixed crash when runtime reports an invalid view configuration from xrWaitFrame. +* Fixed - OpenXR plugin will only look up functions from supported and enabled extensions. +* Fixed GPU selection in multi-GPU scenarios. + +### Updated +* Updated documentation for the Meta Quest feature. + +### Added +* Added API `OpenXRRuntime.retryInitializationOnFormFactorErrors` to retry xrGetSystem during initialization if xrGetSystem returns a form factor error. +* Enable XR_META_performance_metrics. This enables performance stats for Meta Quest devices on OpenXR. +* Add class MetaQuestTouchProControllerProfile.QuestProTouchController new interaction profile to support Meta Quest pro controllers. +* Added ability for OpenXRFeature derived classes to add Awake() functions. +* Added API `OpenXRInput.GetActionIsActive` to check whether an InputAction has any bindings which are currently active. +* Added API `OpenXRInput.GetActionHandle` to get the action handle of an InputAction and returns 0 if not found. + +## [1.6.1-preview.1] - 2022-12-01 + ## [1.6.0] - 2022-11-29 ### Fixed -* Fixed black game window issue when building a URP project. -* Fixed `InputDevice.TryGetHapticCapabilities` always return True with OpenXR. -* Fixed repeated warnings for failed to restart OpenXR when no HMD is attached. -* Fixed invalid pose values got populated when tracked flags are invalid. +* Fixed issue where game window would show as black in URP. +* Fixed `InputDevice.TryGetHapticCapabilities` always returning True with OpenXR. +* Fixed repeated "Failed to restart OpenXR" warnings when no HMD is attached. +* Fixed invalid pose values getting populated when tracked flags are invalid. * Fixed XR_SPACE_BOUNDS_UNAVAILABLE return code marked as Error in the log. ### Updated diff --git a/ConformanceAutomation/android.meta b/ConformanceAutomation/android.meta index ae906aa..ea68c22 100644 --- a/ConformanceAutomation/android.meta +++ b/ConformanceAutomation/android.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: cfbc27dec15942e180a99ca680337396 +guid: 957f695da8ce4866818b85a08ed718cf folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/ConformanceAutomation/android/arm64.meta b/ConformanceAutomation/android/arm64.meta index c2974d6..9377a8e 100644 --- a/ConformanceAutomation/android/arm64.meta +++ b/ConformanceAutomation/android/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 65c5b539fcfc46c29211c8c91b1d3db8 +guid: cfd0c1bfd9a4400193e94ff9eb9024d0 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/ConformanceAutomation/android/arm64/ConformanceAutomationExt.so.meta b/ConformanceAutomation/android/arm64/ConformanceAutomationExt.so.meta index f563194..7627649 100644 --- a/ConformanceAutomation/android/arm64/ConformanceAutomationExt.so.meta +++ b/ConformanceAutomation/android/arm64/ConformanceAutomationExt.so.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 796e9d68728f4802980031e1f9fd6498 +guid: 41ccd112329b476b90136b3fdc35f89c PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/ConformanceAutomation/universalwindows.meta b/ConformanceAutomation/universalwindows.meta index 4f6b3ff..8937166 100644 --- a/ConformanceAutomation/universalwindows.meta +++ b/ConformanceAutomation/universalwindows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: db6f11084d044042b7872e4c209af39f +guid: ff6ed522bce5436a99dad9157214048d folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/ConformanceAutomation/universalwindows/arm32.meta b/ConformanceAutomation/universalwindows/arm32.meta index 9cd20d0..feaffa6 100644 --- a/ConformanceAutomation/universalwindows/arm32.meta +++ b/ConformanceAutomation/universalwindows/arm32.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 41bf2eadf7374d29bb9784c4e4a32857 +guid: 8fc9185aa1f345e9bbd17acba01fc75a folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/ConformanceAutomation/universalwindows/arm32/ConformanceAutomationExt.dll b/ConformanceAutomation/universalwindows/arm32/ConformanceAutomationExt.dll index 22a17ca..b3dd76c 100644 Binary files a/ConformanceAutomation/universalwindows/arm32/ConformanceAutomationExt.dll and b/ConformanceAutomation/universalwindows/arm32/ConformanceAutomationExt.dll differ diff --git a/ConformanceAutomation/universalwindows/arm32/ConformanceAutomationExt.dll.meta b/ConformanceAutomation/universalwindows/arm32/ConformanceAutomationExt.dll.meta index 27b4fe0..f4aaa4a 100644 --- a/ConformanceAutomation/universalwindows/arm32/ConformanceAutomationExt.dll.meta +++ b/ConformanceAutomation/universalwindows/arm32/ConformanceAutomationExt.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 0387367348684740aa0024da7e44e9fa +guid: 0978372b8b414a22a956f22e2f5d8af9 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/ConformanceAutomation/universalwindows/arm64.meta b/ConformanceAutomation/universalwindows/arm64.meta index fc94f8b..ce74d9e 100644 --- a/ConformanceAutomation/universalwindows/arm64.meta +++ b/ConformanceAutomation/universalwindows/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5a9f9879f0554f37834d0615a6ed46bb +guid: ae6269a4be0c415c8467da2ce646a64b folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/ConformanceAutomation/universalwindows/arm64/ConformanceAutomationExt.dll b/ConformanceAutomation/universalwindows/arm64/ConformanceAutomationExt.dll index b94ed61..099d6f7 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 3699d9a..d5e6803 100644 --- a/ConformanceAutomation/universalwindows/arm64/ConformanceAutomationExt.dll.meta +++ b/ConformanceAutomation/universalwindows/arm64/ConformanceAutomationExt.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: bc072079d69f4c119b77caad3de86da8 +guid: 3e847018538e45079ef463e49775c14d PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/ConformanceAutomation/universalwindows/x64.meta b/ConformanceAutomation/universalwindows/x64.meta index e79e358..037a75d 100644 --- a/ConformanceAutomation/universalwindows/x64.meta +++ b/ConformanceAutomation/universalwindows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: cc87159c50534b9387ca830bf6bc7953 +guid: f1fe2b691388474dac5774e4d9d6e30c folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/ConformanceAutomation/universalwindows/x64/ConformanceAutomationExt.dll b/ConformanceAutomation/universalwindows/x64/ConformanceAutomationExt.dll index b5ae5be..eae4c15 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 9fe95d7..0eb7f99 100644 --- a/ConformanceAutomation/universalwindows/x64/ConformanceAutomationExt.dll.meta +++ b/ConformanceAutomation/universalwindows/x64/ConformanceAutomationExt.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 4886953e8acb45ad93fad67d0f4b7170 +guid: 75c8085679c4444e84b955c33c39a7d1 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/ConformanceAutomation/windows.meta b/ConformanceAutomation/windows.meta index 5a669c1..cd83200 100644 --- a/ConformanceAutomation/windows.meta +++ b/ConformanceAutomation/windows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 0afd613ea0ee46b1a3f023d8a01e4729 +guid: 3eef25980fc446fface419a26123c7ed folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/ConformanceAutomation/windows/x64.meta b/ConformanceAutomation/windows/x64.meta index e9811f1..9c76310 100644 --- a/ConformanceAutomation/windows/x64.meta +++ b/ConformanceAutomation/windows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 6355363b5da34ef98ffe894405220bdc +guid: dceb6ee053994bbd9b8894612781efc3 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/ConformanceAutomation/windows/x64/ConformanceAutomationExt.dll b/ConformanceAutomation/windows/x64/ConformanceAutomationExt.dll index 9ba6a3b..c1cc90b 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 d074915..bb8f39c 100644 --- a/ConformanceAutomation/windows/x64/ConformanceAutomationExt.dll.meta +++ b/ConformanceAutomation/windows/x64/ConformanceAutomationExt.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5ffbe27b4c334b11b76b71f7e619ec45 +guid: b4e400762363416aa5d918d980c5f3d0 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/Documentation~/TableOfContents.md b/Documentation~/TableOfContents.md index 5dd82a2..36566f5 100644 --- a/Documentation~/TableOfContents.md +++ b/Documentation~/TableOfContents.md @@ -1,7 +1,7 @@ * [About Unity OpenXR](index.md) * [Project Configuration](project-configuration.md) * [OpenXR Features](features.md) - * [Oculus Quest Support](features/oculusquest.md) + * [Meta Quest Support](features/metaquest.md) * [OpenXR Input](input.md) * [Microsoft Mixed Reality Motion Controller Profile](features/microsoftmotioncontrollerprofile.md) * [Oculus Touch Controller Profile](features/oculustouchcontrollerprofile.md) diff --git a/Documentation~/features/oculusquest.md b/Documentation~/features/metaquest.md similarity index 83% rename from Documentation~/features/oculusquest.md rename to Documentation~/features/metaquest.md index 54a7dfe..3219d79 100644 --- a/Documentation~/features/oculusquest.md +++ b/Documentation~/features/metaquest.md @@ -1,13 +1,13 @@ -# Oculus Quest Support +# Meta Quest Support -In order to deploy to Oculus Quest, enable the Oculus Quest Support feature on the Android build target: +In order to deploy to Meta Quest, enable the Meta Quest Support feature on the Android build target: 1. Open the Project Settings window (menu: **Edit > Project Settings**). 2. Select the **XR Plug-in Management** from the list of settings on the left. 3. If necessary, enable **OpenXR** in the list of **Plug-in Providers**. Unity installs the OpenXR plug-in, if not already installed. 4. Select the OpenXR settings page under XR Plug-in Management. 5. Add the **Oculus Touch Controller Profile** to the **Interaction Profiles** list. (You can have multiple profiles enabled in the list, the OpenXR chooses the one to use based on the current device at runtime.) -6. Enable **Oculus Quest Support** under **OpenXR Feature Groups**. +6. Enable **Meta Quest Support** under **OpenXR Feature Groups**. The Android apks that are produced with Quest support enabled can be run on the Quest family of devices. See the [XR](xref:XR) section of the Unity Manual for more information about developing VR games and applications. diff --git a/Documentation~/features/metaquesttouchprocontrollerprofile.md b/Documentation~/features/metaquesttouchprocontrollerprofile.md new file mode 100644 index 0000000..2718d9b --- /dev/null +++ b/Documentation~/features/metaquesttouchprocontrollerprofile.md @@ -0,0 +1,41 @@ +# Meta Quest Pro Touch Controller Profile + +Enables the OpenXR interaction profile for Meta Quest Pro controllers and exposes the `` device layout within the [Unity Input System](https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/manual/). + +## Available controls + +| OpenXR Path | Unity Control Name | Type | +|----|----|----| +|`/input/thumbstick`| thumbstick | Vector2 | +|`/input/squeeze/value`| grip | Float | +|`/input/squeeze/value`| gripPressed | Float ( float cast to boolean) | +|`/input/menu/click`| menu (Left Hand Only)| Boolean | +|`/input/system/click`| menu (Right Hand Only)| Boolean | +|`/input/a/click`| primaryButton (Right Hand Only) | Boolean | +|`/input/a/touch`| primaryTouched (Right Hand Only) | Boolean | +|`/input/b/click`| secondaryButton (Right Hand Only) | Boolean | +|`/input/b/touch`| secondaryTouched (Right Hand Only) | Boolean | +|`/input/x/click`| primaryButton (Left Hand Only) | Boolean | +|`/input/x/touch`| primaryTouched (Left Hand Only) | Boolean | +|`/input/y/click`| secondaryButton (Left Hand Only) | Boolean | +|`/input/y/touch`| secondaryTouched (Left Hand Only) | Boolean | +|`/input/trigger/value`| trigger | Float | +|`/input/trigger/value`| triggerPressed | Boolean (float cast to boolean) | +|`/input/trigger/touch`| triggerTouched| Boolean (float cast to boolean) | +|`/input/thumbstick/click`| thumbstickClicked | Boolean | +|`/input/thumbstick/touch`| thumbstickTouched | Boolean | +|`/input/thumbrest/touch`| thumbrestTouched | Boolean | +|`/input/grip/pose` | devicePose | Pose | +|`/input/aim/pose` | pointer | Pose | +|`/input/stylus_fb/force` | stylusForce | Float | +|`/input/trigger/curl_fb` | triggerCurl | Float | +|`/input/trigger/slide_fb` | triggerSlide | Float | +|`/input/trigger/proximity_fb` | triggerProximity | Boolean | +|`/input/thumb_fb/proximity_fb` | thumbProximity | Boolean | +|`/output/haptic` | haptic | Vibrate | +|`/output/trigger_haptic_fb` | hapticTrigger | Vibrate | +|`/output/thumb_haptic_fb` | hapticThumb | Vibrate | +| Unity Layout Only | isTracked | Flag Data | +| Unity Layout Only | trackingState | Flag Data | +| Unity Layout Only | devicePosition | Vector3 | +| Unity Layout Only | deviceRotation | Quaternion | \ No newline at end of file diff --git a/Documentation~/index.md b/Documentation~/index.md index 74135e7..79a401c 100644 --- a/Documentation~/index.md +++ b/Documentation~/index.md @@ -17,7 +17,7 @@ Unity's OpenXR plug-in should work with any device that supports conformant Open |Windows Mixed Reality|Windows 64-bit|DX11|Full feature parity via [Mixed Reality OpenXR Plugin for Unity](https://docs.microsoft.com/en-us/windows/mixed-reality/develop/unity/openxr-getting-started)|| |HoloLens 2|UWP arm64|DX11|Full feature parity via [Mixed Reality OpenXR Plugin for Unity](https://docs.microsoft.com/en-us/windows/mixed-reality/develop/unity/openxr-getting-started)|| |Oculus PC + Link|Windows 64-bit|DX11|HMD + Controllers|Oculus Integration package features not available| -|Oculus Quest|Android arm64|Vulkan|HMD + Controllers via [Oculus Quest Support Feature](./features/oculusquest.md)|Oculus Integration package features not available

Not yet recommended for production.| +|Meta Quest|Android arm64|Vulkan|HMD + Controllers via [Meta Quest Support Feature](./features/metaquest.md)| |All other conformant runtimes (eg. SteamVR)|Windows 64-bit|DX11|HMD + Controllers|Given the unbounded combinations of possible hardware/software configurations, Unity is unable to test or guarantee that all configurations will work optimally.

SteamVR Plugin features not available| To help the community as a whole, Unity will continue to submit any runtime issues, and contribute conformance tests and specification changes to the Khronos working group. diff --git a/MetaQuest/Editor/ModifyAndroidManifestMeta.cs b/MetaQuest/Editor/ModifyAndroidManifestMeta.cs index be40cc3..269f01a 100644 --- a/MetaQuest/Editor/ModifyAndroidManifestMeta.cs +++ b/MetaQuest/Editor/ModifyAndroidManifestMeta.cs @@ -146,6 +146,24 @@ private void UpdateOrCreateAttribute(XmlElement xmlParentElement, string tag, st } } + void RemoveNameValueElementInTag(string parentPath, string tag, string name, string value) + { + var xmlNodeList = this.SelectNodes(parentPath + "/" + tag); + + foreach (XmlNode node in xmlNodeList) + { + var attributeList = ((XmlElement)node).Attributes; + + foreach (XmlAttribute attrib in attributeList) + { + if (attrib.Name == name && attrib.Value == value) + { + node.ParentNode?.RemoveChild(node); + } + } + } + } + internal void AddMetaData() { OpenXRSettings androidOpenXRSettings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Android); @@ -195,6 +213,10 @@ internal void AddMetaData() ("version", "1") }); + // if the Microphone class is used in a project, the BLUETOOTH permission is automatically added to the manifest + // we remove it here since it will cause projects to fail Meta cert + // this shouldn't affect Bluetooth HID devices, which don't need the permission + RemoveNameValueElementInTag("/manifest", "uses-permission", "android:name", "android.permission.BLUETOOTH"); } } } diff --git a/MetaQuest/Runtime/MetaQuestFeature.cs b/MetaQuest/Runtime/MetaQuestFeature.cs index f914904..7c71bac 100644 --- a/MetaQuest/Runtime/MetaQuestFeature.cs +++ b/MetaQuest/Runtime/MetaQuestFeature.cs @@ -90,7 +90,7 @@ protected override void GetValidationChecks(List rules, BuildTar { rules.Add(new ValidationRule(this) { - message = "Only the Oculus Touch Interaction Profile is supported right now.", + message = "Only the Oculus Touch Interaction Profile and Meta Quest Pro Touch Interaction Profile are supported right now.", checkPredicate = () => { var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(targetGroup); @@ -103,7 +103,7 @@ protected override void GetValidationChecks(List rules, BuildTar { if (feature.enabled) { - if (feature is OculusTouchControllerProfile) + if ((feature is OculusTouchControllerProfile) || (feature is MetaQuestTouchProControllerProfile)) touchFeatureEnabled = true; else otherInteractionFeatureEnabled = true; @@ -111,18 +111,10 @@ protected override void GetValidationChecks(List rules, BuildTar } return touchFeatureEnabled && !otherInteractionFeatureEnabled; }, - fixIt = () => - { - var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(targetGroup); - if (null == settings) - return; - - foreach (var feature in settings.GetFeatures()) - { - feature.enabled = (feature is OculusTouchControllerProfile); - } - }, error = true, + fixIt = () => { SettingsService.OpenProjectSettings("Project/XR Plug-in Management/OpenXR");}, + fixItAutomatic = false, + fixItMessage = "Open Project Settings to select Oculus Touch or Meta Quest Pro Touch interaction profiles or select both." }); rules.Add(new ValidationRule(this) diff --git a/MockRuntime/MockRuntime.cs b/MockRuntime/MockRuntime.cs index 76909eb..68897ff 100644 --- a/MockRuntime/MockRuntime.cs +++ b/MockRuntime/MockRuntime.cs @@ -458,6 +458,9 @@ protected internal override void GetValidationChecks(List result [DllImport(extLib, EntryPoint = "MockRuntime_RegisterFunctionCallbacks")] private static extern void MockRuntime_RegisterFunctionCallbacks(BeforeFunctionDelegate hookBefore, AfterFunctionDelegate hookAfter); + [DllImport(extLib, EntryPoint = "MockRuntime_MetaPerformanceMetrics_SeedCounterOnce_Float")] + internal static extern void MetaPerformanceMetrics_SeedCounterOnce_Float(string xrPathString, float value, uint unit); + #if UNITY_EDITOR static void UseGenericLoaderAndroid() { diff --git a/MockRuntime/Native~/mock_runtime/Extensions/mock_conformance_automation.cpp b/MockRuntime/Native~/mock_runtime/Extensions/mock_conformance_automation.cpp index 02a463e..a43464a 100644 --- a/MockRuntime/Native~/mock_runtime/Extensions/mock_conformance_automation.cpp +++ b/MockRuntime/Native~/mock_runtime/Extensions/mock_conformance_automation.cpp @@ -122,4 +122,6 @@ bool ConformanceAutomation_IsActive(XrPath interactionProfilePath, XrPath userPa active = s_ext->activeStates.find(std::pair(XR_NULL_PATH, userPath)); return (active != s_ext->activeStates.end()) ? active->second : defaultValue; -} \ No newline at end of file +} + +#undef CHECK_EXT diff --git a/MockRuntime/Native~/mock_runtime/Extensions/mock_meta_performance_metrics.cpp b/MockRuntime/Native~/mock_runtime/Extensions/mock_meta_performance_metrics.cpp new file mode 100644 index 0000000..b0cee0d --- /dev/null +++ b/MockRuntime/Native~/mock_runtime/Extensions/mock_meta_performance_metrics.cpp @@ -0,0 +1,304 @@ +#include "mock_meta_performance_metrics.h" + +#include "../mock.h" +#include + +#define CHECK_EXT() \ + if (nullptr == MockMetaPerformanceMetrics::Instance()) \ + return XR_ERROR_FUNCTION_UNSUPPORTED; + +// storage for static +std::unique_ptr MockMetaPerformanceMetrics::s_ext; + +enum class MockMetaPerformanceMetrics::InternalPaths : int +{ + Invalid = 0, + AppCPUFrametime, + AppGPUFrametime, + AppMotionToPhotonLatency, + CompositorCPUFrametime, + CompositorGPUFrametime, + CompositorDroppedFrameCount, + CompositorSpacewarpMode, + DeviceCPUUtilizationAvg, + DeviceCPUUtilizationWorst, + DeviceGPUUtilization, +}; + +namespace +{ +constexpr char kAppCPUFrametimeStr[] = "/perfmetrics_meta/app/cpu_frametime"; +constexpr char kAppGPUFrametimeStr[] = "/perfmetrics_meta/app/gpu_frametime"; +constexpr char kAppMotionToPhotonLatencyStr[] = "/perfmetrics_meta/app/motion_to_photon_latency"; +constexpr char kCompositorCPUFrametimeStr[] = "/perfmetrics_meta/compositor/cpu_frametime"; +constexpr char kCompositorGPUFrametimeStr[] = "/perfmetrics_meta/compositor/gpu_frametime"; +constexpr char kCompositorDroppedFrameCountStr[] = "/perfmetrics_meta/compositor/dropped_frame_count"; +constexpr char kCompositorSpacewarpModeStr[] = "/perfmetrics_meta/compositor/spacewarp_mode"; +constexpr char kDeviceCPUUtilizationAvgStr[] = "/perfmetrics_meta/device/cpu_utilization_average"; +constexpr char kDeviceCPUUtilizationWorstStr[] = "/perfmetrics_meta/device/cpu_utilization_worst"; +constexpr char kDeviceGPUUtilizationStr[] = "/perfmetrics_meta/device/gpu_utilization"; +} // namespace + +MockMetaPerformanceMetrics::InternalPaths MockMetaPerformanceMetrics::InternalPathFromString( + const std::string& s) +{ + if (s == kAppCPUFrametimeStr) + return InternalPaths::AppCPUFrametime; + if (s == kAppGPUFrametimeStr) + return InternalPaths::AppGPUFrametime; + if (s == kAppMotionToPhotonLatencyStr) + return InternalPaths::AppMotionToPhotonLatency; + if (s == kCompositorCPUFrametimeStr) + return InternalPaths::CompositorCPUFrametime; + if (s == kCompositorGPUFrametimeStr) + return InternalPaths::CompositorGPUFrametime; + if (s == kCompositorDroppedFrameCountStr) + return InternalPaths::CompositorDroppedFrameCount; + if (s == kCompositorSpacewarpModeStr) + return InternalPaths::CompositorSpacewarpMode; + if (s == kDeviceCPUUtilizationAvgStr) + return InternalPaths::DeviceCPUUtilizationAvg; + if (s == kDeviceCPUUtilizationWorstStr) + return InternalPaths::DeviceCPUUtilizationWorst; + if (s == kDeviceGPUUtilizationStr) + return InternalPaths::DeviceGPUUtilization; + return InternalPaths::Invalid; +} + +MockMetaPerformanceMetrics::MockMetaPerformanceMetrics(MockRuntime& runtime, const int numMockCPUs) + : m_NumMockCPUs{numMockCPUs} + , m_Runtime{runtime} +{ + static constexpr const char* k_ListPaths[] = { + kAppCPUFrametimeStr, + kAppGPUFrametimeStr, + kAppMotionToPhotonLatencyStr, + kCompositorCPUFrametimeStr, + kCompositorGPUFrametimeStr, + kCompositorDroppedFrameCountStr, + kCompositorSpacewarpModeStr, + kDeviceCPUUtilizationAvgStr, + kDeviceCPUUtilizationWorstStr, + kDeviceGPUUtilizationStr, + }; + + for (const char* const pathString : k_ListPaths) + { + const XrPath path = runtime.StringToPath(pathString); + m_Paths[path] = InternalPathFromString(pathString); + } + + for (int i = 0; i < m_NumMockCPUs; i++) + { + std::stringstream ss{"/perfmetrics_meta/device/cpu"}; + ss << i << "_utilization"; + + std::string pathString = ss.str(); + const XrPath path = runtime.StringToPath(pathString.c_str()); + m_Paths[path] = InternalPathFromString(pathString); + } +} + +void MockMetaPerformanceMetrics::Init(MockRuntime& runtime, const int numMockCPUs) +{ + s_ext.reset(new MockMetaPerformanceMetrics(runtime, numMockCPUs)); +} + +void MockMetaPerformanceMetrics::Deinit() +{ + s_ext.reset(); +} + +MockMetaPerformanceMetrics* MockMetaPerformanceMetrics::Instance() +{ + return s_ext.get(); +} + +XrResult MockMetaPerformanceMetrics::EnumeratePaths( + XrInstance instance, + uint32_t counterPathCapacityInput, + uint32_t* counterPathCountOutput, + XrPath* counterPaths) +{ + *counterPathCountOutput = static_cast(m_Paths.size()); + if (counterPathCapacityInput >= *counterPathCountOutput) + { + int i = 0; + for (const auto kv : m_Paths) + { + counterPaths[i] = kv.first; + i++; + } + } + return XR_SUCCESS; +} + +XrResult MockMetaPerformanceMetrics::SetState(XrSession session, const XrPerformanceMetricsStateMETA* state) +{ + if (!state || state->type != XR_TYPE_PERFORMANCE_METRICS_STATE_META) + { + return XR_ERROR_VALIDATION_FAILURE; + } + m_Enabled = state->enabled == XR_TRUE; + return XR_SUCCESS; +} + +XrResult MockMetaPerformanceMetrics::GetState(XrSession session, XrPerformanceMetricsStateMETA* state) +{ + if (!state || state->type != XR_TYPE_PERFORMANCE_METRICS_STATE_META) + { + return XR_ERROR_VALIDATION_FAILURE; + } + state->enabled = m_Enabled ? XR_TRUE : XR_FALSE; + return XR_SUCCESS; +} + +XrResult MockMetaPerformanceMetrics::QueryCounter( + XrSession session, + XrPath counterPath, + XrPerformanceMetricsCounterMETA* counter) +{ + if (!m_Enabled || !counter || counter->type != XR_TYPE_PERFORMANCE_METRICS_COUNTER_META) + { + // disabled state return value confirmed by Xiang Wei @ Meta. + return XR_ERROR_VALIDATION_FAILURE; + } + + std::list& values = m_SeededResults[m_Paths[counterPath]]; + if (values.empty()) + { + if (!m_NoResultsSeededWarningShown) + { + m_NoResultsSeededWarningShown = true; + MOCK_TRACE_DEBUG("No results were seeded for the requested stat. If you aren't testing stats, ignore this warning."); + } + *counter = m_DefaultResult.value; + return m_DefaultResult.rv; + } + + // FIFO, fill in the counter struct and return the seeded return value. + *counter = values.front().value; + XrResult rv = values.front().rv; + values.pop_front(); + return rv; +} + +void MockMetaPerformanceMetrics::SeedCounterOnce(const std::string& counterPathString, MockResult result) +{ + const XrPath path = m_Runtime.StringToPath(counterPathString.c_str()); + if (path == XR_NULL_PATH) + { + // providing an invalid path during testing shouldn't happen, kill and warn our dev + // if you want to test whether a path exists, use the runtime functions themselves. + MOCK_TRACE_ERROR("Could not find path %s", counterPathString.c_str()); + return; + } + + if (result.value.type != XR_TYPE_PERFORMANCE_METRICS_COUNTER_META) + { + MOCK_TRACE_ERROR( + "Invalid or no type supplied for XrPerformanceMetricsCounterMETA. Type MUST be" + " XR_TYPE_PERFORMANCE_METRICS_COUNTER_META."); + return; + } + if (result.value.next != nullptr) + { + MOCK_TRACE_ERROR( + "This structure does not currently support chaining (`next` should be `nullptr`)."); + return; + } + std::list& values = m_SeededResults[m_Paths[path]]; + values.push_back(result); +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR +xrEnumeratePerformanceMetricsCounterPathsMETA( + XrInstance instance, + uint32_t counterPathCapacityInput, + uint32_t* counterPathCountOutput, + XrPath* counterPaths) +{ + LOG_FUNC(); + CHECK_RUNTIME(); + CHECK_INSTANCE(instance); + CHECK_EXT(); + MOCK_HOOK_BEFORE(); + + const XrResult result = + MockMetaPerformanceMetrics::Instance()->EnumeratePaths( + instance, + counterPathCapacityInput, + counterPathCountOutput, + counterPaths); + + MOCK_HOOK_AFTER(result); + + return result; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR +xrSetPerformanceMetricsStateMETA( + XrSession session, + const XrPerformanceMetricsStateMETA* state) +{ + LOG_FUNC(); + CHECK_SESSION(session); + CHECK_EXT(); + MOCK_HOOK_BEFORE(); + + const XrResult result = MockMetaPerformanceMetrics::Instance()->SetState(session, state); + + MOCK_HOOK_AFTER(result); + + return result; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR +xrGetPerformanceMetricsStateMETA( + XrSession session, + XrPerformanceMetricsStateMETA* state) +{ + LOG_FUNC(); + CHECK_SESSION(session); + CHECK_EXT(); + MOCK_HOOK_BEFORE(); + + const XrResult result = MockMetaPerformanceMetrics::Instance()->GetState(session, state); + + MOCK_HOOK_AFTER(result); + + return result; +} + +extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR +xrQueryPerformanceMetricsCounterMETA( + XrSession session, + XrPath counterPath, + XrPerformanceMetricsCounterMETA* counter) +{ + LOG_FUNC(); + CHECK_SESSION(session); + CHECK_EXT(); + MOCK_HOOK_BEFORE(); + + const XrResult result = + MockMetaPerformanceMetrics::Instance()->QueryCounter( + session, + counterPath, + counter); + + MOCK_HOOK_AFTER(result); + + return result; +} + +XrResult MockMetaPerformanceMetrics_GetInstanceProcAddr(const char* name, PFN_xrVoidFunction* function) +{ + GET_PROC_ADDRESS(xrEnumeratePerformanceMetricsCounterPathsMETA) + GET_PROC_ADDRESS(xrSetPerformanceMetricsStateMETA) + GET_PROC_ADDRESS(xrGetPerformanceMetricsStateMETA) + GET_PROC_ADDRESS(xrQueryPerformanceMetricsCounterMETA) + return XR_ERROR_FUNCTION_UNSUPPORTED; +} + +#undef CHECK_EXT diff --git a/MockRuntime/Native~/mock_runtime/Extensions/mock_meta_performance_metrics.h b/MockRuntime/Native~/mock_runtime/Extensions/mock_meta_performance_metrics.h new file mode 100644 index 0000000..04c6a85 --- /dev/null +++ b/MockRuntime/Native~/mock_runtime/Extensions/mock_meta_performance_metrics.h @@ -0,0 +1,73 @@ +// Mock extension for XR_META_performance_metrics extension +// https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_META_performance_metrics + +#pragma once + +#include "openxr/openxr.h" +#include +#include +#include +#include +#include + +class MockRuntime; + +class MockMetaPerformanceMetrics +{ +public: + enum class InternalPaths : int; + + struct MockResult + { + XrResult rv; + XrPerformanceMetricsCounterMETA value; + }; + + static void Init(MockRuntime& runtime, const int numMockCPUs); + static void Deinit(); + static MockMetaPerformanceMetrics* Instance(); + + static InternalPaths InternalPathFromString(const std::string& s); + + XrResult EnumeratePaths( + XrInstance instance, + uint32_t counterPathCapacityInput, + uint32_t* counterPathCountOutput, + XrPath* counterPaths); + XrResult SetState(XrSession session, const XrPerformanceMetricsStateMETA* state); + XrResult GetState(XrSession session, XrPerformanceMetricsStateMETA* state); + XrResult QueryCounter( + XrSession session, + XrPath counterPath, + XrPerformanceMetricsCounterMETA* counter); + + void SeedCounterOnce(const std::string& counterPath, MockResult result); + +private: + MockMetaPerformanceMetrics(MockRuntime& runtime, const int numMockCPUs); + + static std::unique_ptr s_ext; + MockRuntime& m_Runtime; + + const int m_NumMockCPUs; + + // we use an ordered set just to keep things consistent for enumeration + std::map m_Paths; + + bool m_Enabled; + bool m_NoResultsSeededWarningShown; + + std::map> m_SeededResults; + MockResult m_DefaultResult{ + XR_SUCCESS, + { + XR_TYPE_PERFORMANCE_METRICS_COUNTER_META, + nullptr, + XR_PERFORMANCE_METRICS_COUNTER_ANY_VALUE_VALID_BIT_META, + XR_PERFORMANCE_METRICS_COUNTER_UNIT_GENERIC_META, + 0, + 0.f, + }}; +}; + +XrResult MockMetaPerformanceMetrics_GetInstanceProcAddr(const char* name, PFN_xrVoidFunction* function); diff --git a/MockRuntime/Native~/mock_runtime/Extensions/mock_msft_secondary_view_configuration.cpp b/MockRuntime/Native~/mock_runtime/Extensions/mock_msft_secondary_view_configuration.cpp index b5a5690..e4c3b46 100644 --- a/MockRuntime/Native~/mock_runtime/Extensions/mock_msft_secondary_view_configuration.cpp +++ b/MockRuntime/Native~/mock_runtime/Extensions/mock_msft_secondary_view_configuration.cpp @@ -52,7 +52,12 @@ XrResult MockRuntime::MSFTSecondaryViewConfiguration_WaitFrame(const XrFrameWait return XR_ERROR_VALIDATION_FAILURE; for (auto& viewConfiguration : secondaryViewConfigurationStates) - viewConfiguration.active = GetMockViewConfiguration(viewConfiguration.viewConfigurationType)->active; + { + if (viewConfiguration.viewConfigurationType != 0) + { + viewConfiguration.active = GetMockViewConfiguration(viewConfiguration.viewConfigurationType)->active; + } + } memcpy(secondaryFrameState->viewConfigurationStates, secondaryViewConfigurationStates.data(), secondaryViewConfigurationStates.size() * sizeof(XrSecondaryViewConfigurationStateMSFT)); @@ -89,6 +94,13 @@ XrResult MockRuntime::MSFTSecondaryViewConfiguration_EndFrame(const XrFrameEndIn XrResult MockRuntime::ActivateSecondaryView(XrViewConfigurationType viewConfigurationType, bool active) { + if (viewConfigurationType == 0) + { + // emulating a bug we saw with microsoft runtime + secondaryViewConfigurationStates.clear(); + secondaryViewConfigurationStates.push_back({XR_TYPE_SECONDARY_VIEW_CONFIGURATION_STATE_MSFT}); + } + MockViewConfiguration* viewConfiguration = GetMockViewConfiguration(viewConfigurationType); if (nullptr == viewConfiguration || (IsSessionRunning() && !viewConfiguration->enabled)) return XR_ERROR_SECONDARY_VIEW_CONFIGURATION_TYPE_NOT_ENABLED_MSFT; diff --git a/MockRuntime/Native~/mock_runtime/mock_api.cpp b/MockRuntime/Native~/mock_runtime/mock_api.cpp index b9d591c..bef8112 100644 --- a/MockRuntime/Native~/mock_runtime/mock_api.cpp +++ b/MockRuntime/Native~/mock_runtime/mock_api.cpp @@ -286,6 +286,23 @@ MOCK_API_TRAMPOLINE(bool, false, MockRuntime_TransitionToState, } #endif +MOCK_API_TRAMPOLINE(void, NO_RETURN(), MockRuntime_MetaPerformanceMetrics_SeedCounterOnce_Float, + (const char* xrPathString, float value, uint32_t unit), + (xrPathString, value, unit)) +#if !TRAMPOLINE +{ + MockMetaPerformanceMetrics::Instance()->SeedCounterOnce( + xrPathString, + {XR_SUCCESS, + {XR_TYPE_PERFORMANCE_METRICS_COUNTER_META, + nullptr, + XR_PERFORMANCE_METRICS_COUNTER_FLOAT_VALUE_VALID_BIT_META, + static_cast(unit), + 0, + value}}); +} +#endif + #if !TRAMPOLINE XrResult GetProcAddrMockAPI(XrInstance instance, const char* name, PFN_xrVoidFunction* function) { @@ -302,6 +319,7 @@ XrResult GetProcAddrMockAPI(XrInstance instance, const char* name, PFN_xrVoidFun GET_PROC_ADDRESS(MockRuntime_RegisterScriptEventCallback) GET_PROC_ADDRESS(MockRuntime_RegisterFunctionCallbacks) GET_PROC_ADDRESS(MockRuntime_TransitionToState) + GET_PROC_ADDRESS(MockRuntime_MetaPerformanceMetrics_SeedCounterOnce_Float) return XR_ERROR_FUNCTION_UNSUPPORTED; } diff --git a/MockRuntime/Native~/mock_runtime/mock_extensions.h b/MockRuntime/Native~/mock_runtime/mock_extensions.h index b533076..783d63e 100644 --- a/MockRuntime/Native~/mock_runtime/mock_extensions.h +++ b/MockRuntime/Native~/mock_runtime/mock_extensions.h @@ -1,5 +1,7 @@ #pragma once +#include "Extensions/mock_meta_performance_metrics.h" + // XR_EXT_conformance_automation struct ConformanceAutomation; diff --git a/MockRuntime/Native~/mock_runtime/mock_loader.cpp b/MockRuntime/Native~/mock_runtime/mock_loader.cpp index a7c23b7..57c4089 100644 --- a/MockRuntime/Native~/mock_runtime/mock_loader.cpp +++ b/MockRuntime/Native~/mock_runtime/mock_loader.cpp @@ -82,6 +82,13 @@ uint64_t s_nextInstanceId = 11; // Start at 11 because 10 is a special test case XR_MSFT_HAND_INTERACTION_EXTENSION_NAME, XR_MSFT_hand_interaction_SPEC_VERSION }, + //To-Do: update to proper ext name when it is released in the openxr spec. + { + XR_TYPE_EXTENSION_PROPERTIES, + nullptr, + "XR_FB_touch_controller_pro", + 1 + }, { XR_TYPE_EXTENSION_PROPERTIES, nullptr, @@ -93,6 +100,12 @@ uint64_t s_nextInstanceId = 11; // Start at 11 because 10 is a special test case nullptr, XR_MSFT_THIRD_PERSON_OBSERVER_PRIVATE_EXTENSION_NAME, XR_MSFT_third_person_observer_private_SPEC_VERSION + }, + { + XR_TYPE_EXTENSION_PROPERTIES, + nullptr, + XR_META_PERFORMANCE_METRICS_EXTENSION_NAME, + XR_META_performance_metrics_SPEC_VERSION } #if defined(XR_USE_GRAPHICS_API_VULKAN) ,{ @@ -237,6 +250,17 @@ extern "C" XrResult UNITY_INTERFACE_EXPORT XRAPI_PTR xrCreateInstance(const XrIn flags |= MR_CREATE_MSFT_HAND_INTERACTION_EXT; continue; } + //To-Do: update to proper ext name when it is released in the openxr spec. + if (strncmp("XR_FB_touch_controller_pro", extension, sizeof("XR_FB_touch_controller_pro")) == 0) + { + flags |= MR_CREATE_FB_TOUCH_CONTROLLER_PRO; + continue; + } + + if (strncmp(XR_META_PERFORMANCE_METRICS_EXTENSION_NAME, extension, sizeof(XR_META_PERFORMANCE_METRICS_EXTENSION_NAME)) == 0) + { + flags |= MR_CREATE_META_PERFORMANCE_METRICS_EXT; + } } if ((flags & MR_CREATE_ALL_GFX_EXT) == 0) diff --git a/MockRuntime/Native~/mock_runtime/mock_runtime.cpp b/MockRuntime/Native~/mock_runtime/mock_runtime.cpp index d5ecc2c..a43f677 100644 --- a/MockRuntime/Native~/mock_runtime/mock_runtime.cpp +++ b/MockRuntime/Native~/mock_runtime/mock_runtime.cpp @@ -35,7 +35,7 @@ MockRuntime::MockRuntime(XrInstance instance, MockRuntimeCreateFlags flags) defaultViewConfig.recommendedSwapchainSampleCount = 1; defaultViewConfig.maxSwapchainSampleCount = 1; - // Initialzie stereo view + // Initialize stereo view MockViewConfiguration stereoViewConfig = {}; stereoViewConfig.primary = true; stereoViewConfig.enabled = true; @@ -91,6 +91,9 @@ MockRuntime::MockRuntime(XrInstance instance, MockRuntimeCreateFlags flags) if ((createFlags & (MR_CREATE_MSFT_THIRD_PERSON_OBSERVER_EXT | MR_CREATE_MSFT_SECONDARY_VIEW_CONFIGURATION_EXT)) == (MR_CREATE_MSFT_THIRD_PERSON_OBSERVER_EXT | MR_CREATE_MSFT_SECONDARY_VIEW_CONFIGURATION_EXT)) MSFTThirdPersonObserver_Init(); + if ((createFlags & MR_CREATE_META_PERFORMANCE_METRICS_EXT) == MR_CREATE_META_PERFORMANCE_METRICS_EXT) + MockMetaPerformanceMetrics::Init(*this, 4); + // Generate the internal strings userPaths = { {"/user/hand/left", "Left Hand", nullptr}, @@ -1675,6 +1678,9 @@ XrResult MockRuntime::GetInstanceProcAddr(const char* name, PFN_xrVoidFunction* if (IsConformanceAutomationEnabled() && XR_SUCCESS == ConformanceAutomation_GetInstanceProcAddr(name, function)) return XR_SUCCESS; + if (MockMetaPerformanceMetrics::Instance() && XR_SUCCESS == MockMetaPerformanceMetrics_GetInstanceProcAddr(name, function)) + return XR_SUCCESS; + return XR_ERROR_FUNCTION_UNSUPPORTED; } diff --git a/MockRuntime/Native~/mock_runtime/mock_runtime.h b/MockRuntime/Native~/mock_runtime/mock_runtime.h index a68434c..1eced4d 100644 --- a/MockRuntime/Native~/mock_runtime/mock_runtime.h +++ b/MockRuntime/Native~/mock_runtime/mock_runtime.h @@ -13,6 +13,8 @@ static const MockRuntimeCreateFlags MR_CREATE_MSFT_FIRST_PERSON_OBSERVER_EXT = 0 static const MockRuntimeCreateFlags MR_CREATE_EYE_GAZE_INTERACTION_EXT = 0x00000200; static const MockRuntimeCreateFlags MR_CREATE_MSFT_HAND_INTERACTION_EXT = 0x00000400; static const MockRuntimeCreateFlags MR_CREATE_MSFT_THIRD_PERSON_OBSERVER_EXT = 0x00000800; +static const MockRuntimeCreateFlags MR_CREATE_FB_TOUCH_CONTROLLER_PRO = 0x00001000; +static const MockRuntimeCreateFlags MR_CREATE_META_PERFORMANCE_METRICS_EXT = 0x00002000; static const MockRuntimeCreateFlags MR_CREATE_ALL_GFX_EXT = MR_CREATE_VULKAN_GFX_EXT | MR_CREATE_NULL_GFX_EXT | MR_CREATE_D3D11_GFX_EXT; diff --git a/MockRuntime/Native~/mock_runtime/mock_runtime_interaction_profiles.cpp b/MockRuntime/Native~/mock_runtime/mock_runtime_interaction_profiles.cpp index 66c0fb8..1d6ba2a 100644 --- a/MockRuntime/Native~/mock_runtime/mock_runtime_interaction_profiles.cpp +++ b/MockRuntime/Native~/mock_runtime/mock_runtime_interaction_profiles.cpp @@ -349,6 +349,68 @@ static std::vector s_InteractionProfiles = { {"/user/hand/right/input/squeeze/value", XR_ACTION_TYPE_FLOAT_INPUT, "Squeeze"}, {"/user/hand/right/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT, "Aim"}, {"/user/hand/right/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT, "Grip"}}}, + + // Meta Quest Pro Touch Controller Profile + { + "Quest Pro Touch Controller", + "/interaction_profiles/facebook/touch_controller_pro", + MR_CREATE_FB_TOUCH_CONTROLLER_PRO, + {"/user/hand/left", + "/user/hand/right"}, + { + {"/user/hand/left/input/x/click", XR_ACTION_TYPE_BOOLEAN_INPUT, "X"}, + {"/user/hand/left/input/x/touch", XR_ACTION_TYPE_BOOLEAN_INPUT, "X Touch"}, + {"/user/hand/left/input/y/click", XR_ACTION_TYPE_BOOLEAN_INPUT, "Y"}, + {"/user/hand/left/input/y/touch", XR_ACTION_TYPE_BOOLEAN_INPUT, "Y Touch"}, + {"/user/hand/left/input/menu/click", XR_ACTION_TYPE_BOOLEAN_INPUT, "Menu"}, + {"/user/hand/left/input/squeeze/value", XR_ACTION_TYPE_FLOAT_INPUT, "Grip"}, + {"/user/hand/left/input/trigger/value", XR_ACTION_TYPE_FLOAT_INPUT, "Trigger"}, + {"/user/hand/left/input/trigger/touch", XR_ACTION_TYPE_BOOLEAN_INPUT, "Touch"}, + {"/user/hand/left/input/thumbstick/x", XR_ACTION_TYPE_FLOAT_INPUT, "Thumbstick X"}, + {"/user/hand/left/input/thumbstick/y", XR_ACTION_TYPE_FLOAT_INPUT, "Thumbstick Y"}, + {"/user/hand/left/input/thumbstick/click", XR_ACTION_TYPE_BOOLEAN_INPUT, "Thumbstick Click"}, + {"/user/hand/left/input/thumbstick/touch", XR_ACTION_TYPE_BOOLEAN_INPUT, "Thumbstick Touch"}, + {"/user/hand/left/input/thumbstick", XR_ACTION_TYPE_VECTOR2F_INPUT, "Thumbstick"}, + {"/user/hand/left/input/thumbrest/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/left/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT, "Grip"}, + {"/user/hand/left/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT, "Aim"}, + {"/user/hand/left/output/haptic", XR_ACTION_TYPE_VIBRATION_OUTPUT, "Haptic"}, + {"/user/hand/left/input/thumbrest/force", XR_ACTION_TYPE_FLOAT_INPUT, "Thumbrest Force"}, + {"/user/hand/left/input/stylus_fb/force", XR_ACTION_TYPE_FLOAT_INPUT, "Stylus Force"}, + {"/user/hand/left/input/trigger/curl_fb", XR_ACTION_TYPE_FLOAT_INPUT, "Trigger Curl"}, + {"/user/hand/left/input/trigger/slide_fb", XR_ACTION_TYPE_FLOAT_INPUT, "Trigger Slide"}, + {"/user/hand/left/input/trigger/proximity_fb", XR_ACTION_TYPE_BOOLEAN_INPUT, "Trigger Proximity"}, + {"/user/hand/left/input/thumb_fb/proximity_fb", XR_ACTION_TYPE_BOOLEAN_INPUT, "Thumb Proximity"}, + {"/user/hand/left/output/trigger_haptic_fb", XR_ACTION_TYPE_VIBRATION_OUTPUT, "Haptic Trigger"}, + {"/user/hand/left/output/thumb_haptic_fb", XR_ACTION_TYPE_VIBRATION_OUTPUT, "Haptic Thumb"}, + + {"/user/hand/right/input/a/click", XR_ACTION_TYPE_BOOLEAN_INPUT, "A"}, + {"/user/hand/right/input/a/touch", XR_ACTION_TYPE_BOOLEAN_INPUT, "A Touch"}, + {"/user/hand/right/input/b/click", XR_ACTION_TYPE_BOOLEAN_INPUT, "B"}, + {"/user/hand/right/input/b/touch", XR_ACTION_TYPE_BOOLEAN_INPUT, "B Touch"}, + // The system ("Oculus") button is reserved for system applications + {"/user/hand/right/input/system/click", XR_ACTION_TYPE_BOOLEAN_INPUT, "System"}, + {"/user/hand/right/input/squeeze/value", XR_ACTION_TYPE_FLOAT_INPUT, "Grip"}, + {"/user/hand/right/input/trigger/value", XR_ACTION_TYPE_FLOAT_INPUT, "Trigger"}, + {"/user/hand/right/input/trigger/touch", XR_ACTION_TYPE_BOOLEAN_INPUT, "Trigger Touch"}, + {"/user/hand/right/input/thumbstick/x", XR_ACTION_TYPE_FLOAT_INPUT, "Thumbstick X"}, + {"/user/hand/right/input/thumbstick/y", XR_ACTION_TYPE_FLOAT_INPUT, "Thumbstick Y"}, + {"/user/hand/right/input/thumbstick/click", XR_ACTION_TYPE_BOOLEAN_INPUT, "Thumbstick Click"}, + {"/user/hand/right/input/thumbstick/touch", XR_ACTION_TYPE_BOOLEAN_INPUT, "Thumbstick Touch"}, + {"/user/hand/right/input/thumbstick", XR_ACTION_TYPE_VECTOR2F_INPUT, "Thumbstick"}, + {"/user/hand/right/input/thumbrest/touch", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"/user/hand/right/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT, "Grip"}, + {"/user/hand/right/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT, "Aim"}, + {"/user/hand/right/output/haptic", XR_ACTION_TYPE_VIBRATION_OUTPUT, "Haptic"}, + {"/user/hand/right/input/thumbrest/force", XR_ACTION_TYPE_FLOAT_INPUT, "Thumbrest Force"}, + {"/user/hand/right/input/stylus_fb/force", XR_ACTION_TYPE_FLOAT_INPUT, "Stylus Force"}, + {"/user/hand/right/input/trigger/curl_fb", XR_ACTION_TYPE_FLOAT_INPUT, "Trigger Curl"}, + {"/user/hand/right/input/trigger/slide_fb", XR_ACTION_TYPE_FLOAT_INPUT, "Trigger Slide"}, + {"/user/hand/right/input/trigger/proximity_fb", XR_ACTION_TYPE_BOOLEAN_INPUT, "Trigger Proximity"}, + {"/user/hand/right/input/thumb_fb/proximity_fb", XR_ACTION_TYPE_BOOLEAN_INPUT, "Thumb Proximity"}, + {"/user/hand/right/output/trigger_haptic_fb", XR_ACTION_TYPE_VIBRATION_OUTPUT, "Haptic Trigger"}, + {"/user/hand/right/output/thumb_haptic_fb", XR_ACTION_TYPE_VIBRATION_OUTPUT, "Haptic Thumb"}, + }}, }; void MockRuntime::InitializeInteractionProfiles() diff --git a/MockRuntime/android.meta b/MockRuntime/android.meta index 4988f55..fcba2c8 100644 --- a/MockRuntime/android.meta +++ b/MockRuntime/android.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 50c7d15845b84abcae748d68b9990bfd +guid: 5ef351e83987442c9023f1d17570600c folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/MockRuntime/android/arm64.meta b/MockRuntime/android/arm64.meta index e6d0011..45fe916 100644 --- a/MockRuntime/android/arm64.meta +++ b/MockRuntime/android/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ddce170ff3f641519a106125b725472b +guid: 0178db5572934885a00b12ac59000066 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/MockRuntime/android/arm64/libmock_api.so b/MockRuntime/android/arm64/libmock_api.so index f994e94..d40add1 100644 Binary files a/MockRuntime/android/arm64/libmock_api.so and b/MockRuntime/android/arm64/libmock_api.so differ diff --git a/MockRuntime/android/arm64/libmock_api.so.meta b/MockRuntime/android/arm64/libmock_api.so.meta index e4a4fdf..fbeb7e3 100644 --- a/MockRuntime/android/arm64/libmock_api.so.meta +++ b/MockRuntime/android/arm64/libmock_api.so.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 550339e4461645a6a59eafc9b90f133b +guid: 0a7aaa8ded394ee28414f01a2bf4d034 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/MockRuntime/android/arm64/libmock_runtime.so b/MockRuntime/android/arm64/libmock_runtime.so index b0f01dc..7b270b2 100644 Binary files a/MockRuntime/android/arm64/libmock_runtime.so and b/MockRuntime/android/arm64/libmock_runtime.so differ diff --git a/MockRuntime/android/arm64/libmock_runtime.so.meta b/MockRuntime/android/arm64/libmock_runtime.so.meta index 203e95d..e3884c8 100644 --- a/MockRuntime/android/arm64/libmock_runtime.so.meta +++ b/MockRuntime/android/arm64/libmock_runtime.so.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 65eeb3abbc854e668989be47142cb69f +guid: 1874d1d3bec245af96ec16365a3c1334 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/MockRuntime/android/arm64/libopenxr_loader.so.meta b/MockRuntime/android/arm64/libopenxr_loader.so.meta index a6960e4..0aee17b 100644 --- a/MockRuntime/android/arm64/libopenxr_loader.so.meta +++ b/MockRuntime/android/arm64/libopenxr_loader.so.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 65806191bbe94de0bad3b06209592165 +guid: 1f0ab2dd3c224d6596d4eaf5a05f695e PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/MockRuntime/windows.meta b/MockRuntime/windows.meta index d5e3f3c..ab8d352 100644 --- a/MockRuntime/windows.meta +++ b/MockRuntime/windows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f32d6402a9904b658c23f8cbc3e75ad5 +guid: 81982256fd1b408eba7e6eb638383ccf folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/MockRuntime/windows/x64.meta b/MockRuntime/windows/x64.meta index 3a5ced4..a4c0c7f 100644 --- a/MockRuntime/windows/x64.meta +++ b/MockRuntime/windows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8058027033d44012a982ad39d3817ee9 +guid: 2d325fe0c0c64a19873481d04105ae2e folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/MockRuntime/windows/x64/mock_api.dll b/MockRuntime/windows/x64/mock_api.dll index 2a3cfac..7448334 100644 Binary files a/MockRuntime/windows/x64/mock_api.dll and b/MockRuntime/windows/x64/mock_api.dll differ diff --git a/MockRuntime/windows/x64/mock_api.dll.meta b/MockRuntime/windows/x64/mock_api.dll.meta index dd08569..f7c3310 100644 --- a/MockRuntime/windows/x64/mock_api.dll.meta +++ b/MockRuntime/windows/x64/mock_api.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 600ae5251d6a4357a80ee64953e70368 +guid: bd56d832dcc147b0a89f7c7f17cba899 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/MockRuntime/windows/x64/mock_runtime.dll b/MockRuntime/windows/x64/mock_runtime.dll index 4004285..67fe26b 100644 Binary files a/MockRuntime/windows/x64/mock_runtime.dll and b/MockRuntime/windows/x64/mock_runtime.dll differ diff --git a/MockRuntime/windows/x64/mock_runtime.dll.meta b/MockRuntime/windows/x64/mock_runtime.dll.meta index 65efa3d..1406260 100644 --- a/MockRuntime/windows/x64/mock_runtime.dll.meta +++ b/MockRuntime/windows/x64/mock_runtime.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 934a5c64ee9148be997990cc5f69c927 +guid: b2946b0f05914ab680d96d368fa10473 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/MockRuntime/windows/x64/openxr_loader.dll b/MockRuntime/windows/x64/openxr_loader.dll index ec201b6..68bb6bb 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 eef82c2..a2e01a4 100644 --- a/MockRuntime/windows/x64/openxr_loader.dll.meta +++ b/MockRuntime/windows/x64/openxr_loader.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 99810aad098c435183b87507ddef060a +guid: 93af45e353d24348adb53ae0542258ca PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/OculusQuest/Runtime/OculusQuestFeature.cs b/OculusQuest/Runtime/OculusQuestFeature.cs index 114e630..3c087bc 100644 --- a/OculusQuest/Runtime/OculusQuestFeature.cs +++ b/OculusQuest/Runtime/OculusQuestFeature.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using UnityEditor; - #if UNITY_EDITOR using UnityEditor.XR.OpenXR.Features; using UnityEngine.XR.OpenXR.Features.Interactions; diff --git a/Runtime/Features/Interactions/MetaQuestTouchProControllerProfile.cs b/Runtime/Features/Interactions/MetaQuestTouchProControllerProfile.cs new file mode 100644 index 0000000..6064f9b --- /dev/null +++ b/Runtime/Features/Interactions/MetaQuestTouchProControllerProfile.cs @@ -0,0 +1,964 @@ +using System.Collections.Generic; +using UnityEngine.InputSystem.Controls; +using UnityEngine.InputSystem.Layouts; +using UnityEngine.InputSystem.XR; +using UnityEngine.Scripting; +using UnityEngine.XR.OpenXR.Input; + +#if UNITY_EDITOR +using UnityEditor; +#endif +#if USE_INPUT_SYSTEM_POSE_CONTROL +using PoseControl = UnityEngine.InputSystem.XR.PoseControl; +#else +using PoseControl = UnityEngine.XR.OpenXR.Input.PoseControl; +#endif + +namespace UnityEngine.XR.OpenXR.Features.Interactions +{ + /// + /// This enables the use of Meta Quest Pro controller interaction profiles in OpenXR. + /// +#if UNITY_EDITOR + [UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Meta Quest Touch Pro Controller Profile", + BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android}, + Company = "Unity", + Desc = "Allows for mapping input to the Meta Quest Touch Pro Controller interaction profile.", + DocumentationLink = Constants.k_DocumentationManualURL + "features/metaquesttouchprocontrollerprofile.html", + OpenxrExtensionStrings = "XR_FB_touch_controller_pro", + Version = "0.0.1", + Category = UnityEditor.XR.OpenXR.Features.FeatureCategory.Interaction, + FeatureId = featureId)] +#endif + public class MetaQuestTouchProControllerProfile : OpenXRInteractionFeature + { + /// + /// The feature id string. This is used to give the feature a well known id for reference. + /// + public const string featureId = "com.unity.openxr.feature.input.metaquestpro"; + + /// + /// An Input System device based on the controller interaction profile Meta Touch Controller Pro. + /// + [Preserve, InputControlLayout(displayName = "Meta Quest Pro Touch Controller(OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" })] + public class QuestProTouchController : XRControllerWithRumble + { + /// + /// A [Vector2Control](xref:UnityEngine.InputSystem.Controls.Vector2Control) that represents the OpenXR binding. + /// + [Preserve, InputControl(aliases = new[] { "Primary2DAxis", "Joystick" }, usage = "Primary2DAxis" )] + public Vector2Control thumbstick { get; private set; } + + /// + /// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the OpenXR binding. + /// + [Preserve, InputControl(aliases = new[] { "GripAxis" , "squeeze"}, usage = "Grip")] + public AxisControl grip { get; private set; } + + /// + /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR binding. + /// + [Preserve, InputControl(aliases = new[] { "GripButton", "squeezeClicked" }, usage = "GripButton")] + public ButtonControl gripPressed { get; private set; } + + /// + /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR bindings, depending on handedness. + /// + [Preserve, InputControl(aliases = new[] { "Primary", "menuButton", "systemButton" }, usage = "MenuButton")] + public ButtonControl menu { get; private set; } + + /// + /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR bindings, depending on handedness. + /// + [Preserve, InputControl(aliases = new[] { "A", "X", "buttonA", "buttonX" }, usage = "PrimaryButton")] + public ButtonControl primaryButton { get; private set; } + + /// + /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR bindings, depending on handedness. + /// + [Preserve, InputControl(aliases = new[] { "ATouched", "XTouched", "ATouch", "XTouch", "buttonATouched", "buttonXTouched" }, usage = "PrimaryTouch")] + public ButtonControl primaryTouched { get; private set; } + + /// + /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR bindings, depending on handedness. + /// + [Preserve, InputControl(aliases = new[] { "B", "Y", "buttonB", "buttonY" }, usage = "SecondaryButton")] + public ButtonControl secondaryButton { get; private set; } + + /// + /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR bindings, depending on handedness. + /// + [Preserve, InputControl(aliases = new[] { "BTouched", "YTouched", "BTouch", "YTouch", "buttonBTouched", "buttonYTouched" }, usage = "SecondaryTouch")] + public ButtonControl secondaryTouched { get; private set; } + + /// + /// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the OpenXR binding. + /// + [Preserve, InputControl(usage = "Trigger")] + public AxisControl trigger { get; private set; } + + /// + /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR binding. + /// + [Preserve, InputControl(aliases = new[] { "indexButton", "indexTouched", "triggerbutton" }, usage = "TriggerButton")] + public ButtonControl triggerPressed { get; private set; } + + /// + /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR binding. + /// + [Preserve, InputControl(aliases = new[] { "indexTouch", "indexNearTouched" }, usage = "TriggerTouch")] + public ButtonControl triggerTouched { get; private set; } + + /// + /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR binding. + /// + [Preserve, InputControl(aliases = new[] { "JoystickOrPadPressed", "thumbstickClick", "joystickClicked"}, usage = "Primary2DAxisClick")] + public ButtonControl thumbstickClicked { get; private set; } + + /// + /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR binding. + /// + [Preserve, InputControl(aliases = new[] { "JoystickOrPadTouched", "thumbstickTouch", "joystickTouched" }, usage = "Primary2DAxisTouch")] + public ButtonControl thumbstickTouched { get; private set; } + + /// + /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR binding. + /// + [Preserve, InputControl(usage = "ThumbrestTouch")] + public ButtonControl thumbrestTouched { get; private set; } + + /// + /// A that represents the OpenXR binding. + /// + [Preserve, InputControl(offset = 0, aliases = new[] { "device", "gripPose" }, usage = "Device")] + public PoseControl devicePose { get; private set; } + + /// + /// A that represents the OpenXR binding. + /// + [Preserve, InputControl(offset = 0, alias = "aimPose", usage = "Pointer")] + public PoseControl pointer { get; private set; } + + /// + /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) required for backwards compatibility with the XRSDK layouts. This represents the overall tracking state of the device. This value is equivalent to mapping devicePose/isTracked. + /// + [Preserve, InputControl(offset = 28, usage = "IsTracked")] + new public ButtonControl isTracked { get; private set; } + + /// + /// A [IntegerControl](xref:UnityEngine.InputSystem.Controls.IntegerControl) required for backwards compatibility with the XRSDK layouts. This represents the bit flag set to indicate what data is valid. This value is equivalent to mapping devicePose/trackingState. + /// + [Preserve, InputControl(offset = 32, usage = "TrackingState")] + new public IntegerControl trackingState { get; private set; } + + /// + /// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This 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 = 36, noisy = true, alias = "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 = 48, noisy = true, alias = "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, alias = "pointerOrientation")] + public QuaternionControl pointerRotation { get; private set; } + + /// + /// A that represents the binding. + /// + [Preserve, InputControl(usage = "Haptic")] + public HapticControl haptic { get; private set; } + + /// + /// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the OpenXR binding. + /// + [Preserve, InputControl(usage = "ThumbrestForce")] + public AxisControl thumbrestForce { get; private set; } + + /// + /// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the OpenXR binding. + /// + [Preserve, InputControl(usage = "StylusForce")] + public AxisControl stylusForce { get; private set; } + + /// + /// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the OpenXR binding. + /// + [Preserve, InputControl(usage = "TriggerCurl")] + public AxisControl triggerCurl { get; private set; } + + /// + /// A [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the OpenXR binding. + /// + [Preserve, InputControl(usage = "TriggerSlide")] + public AxisControl triggerSlide { get; private set; } + + /// + /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR binding. + /// + [Preserve, InputControl(usage = "TriggerProximity")] + public ButtonControl triggerProximity { get; private set; } + + /// + /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR binding. + /// + [Preserve, InputControl(usage = "ThumbProximity")] + public ButtonControl thumbProximity { get; private set; } + + /// + /// A that represents the binding. + /// + [Preserve, InputControl(usage = "HapticTrigger")] + public HapticControl hapticTrigger { get; private set; } + + /// + /// A that represents the binding. + /// + [Preserve, InputControl(usage = "HapticThumb")] + public HapticControl hapticThumb { get; private set; } + + /// + /// Internal call used to assign controls to the the correct element. + /// + protected override void FinishSetup() + { + base.FinishSetup(); + thumbstick = GetChildControl("thumbstick"); + trigger = GetChildControl("trigger"); + triggerPressed = GetChildControl("triggerPressed"); + triggerTouched = GetChildControl("triggerTouched"); + grip = GetChildControl("grip"); + gripPressed = GetChildControl("gripPressed"); + menu = GetChildControl("menu"); + primaryButton = GetChildControl("primaryButton"); + primaryTouched = GetChildControl("primaryTouched"); + secondaryButton = GetChildControl("secondaryButton"); + secondaryTouched = GetChildControl("secondaryTouched"); + thumbstickClicked = GetChildControl("thumbstickClicked"); + thumbstickTouched = GetChildControl("thumbstickTouched"); + thumbrestTouched = GetChildControl("thumbrestTouched"); + + devicePose = GetChildControl("devicePose"); + pointer = GetChildControl("pointer"); + + isTracked = GetChildControl("isTracked"); + trackingState = GetChildControl("trackingState"); + devicePosition = GetChildControl("devicePosition"); + deviceRotation = GetChildControl("deviceRotation"); + pointerPosition = GetChildControl("pointerPosition"); + pointerRotation = GetChildControl("pointerRotation"); + + haptic = GetChildControl("haptic"); + thumbrestForce = GetChildControl("thumbrestForce"); + stylusForce = GetChildControl("stylusForce"); + triggerCurl = GetChildControl("triggerCurl"); + triggerSlide = GetChildControl("triggerSlide"); + triggerProximity = GetChildControl("triggerProximity"); + thumbProximity = GetChildControl("thumbProximity"); + hapticTrigger = GetChildControl("hapticTrigger"); + hapticThumb = GetChildControl("hapticThumb"); + } + } + + /// + /// The interaction profile string used to reference Meta Quest Pro Touch Controller. + /// + public const string profile = "/interaction_profiles/facebook/touch_controller_pro"; + + // Available Bindings + // Left Hand Only + /// + /// Constant for a boolean interaction binding '.../input/x/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the user path. + /// + public const string buttonX = "/input/x/click"; + /// + /// Constant for a boolean interaction binding '.../input/x/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the user path. + /// + public const string buttonXTouch = "/input/x/touch"; + /// + /// Constant for a boolean interaction binding '.../input/y/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the user path. + /// + public const string buttonY = "/input/y/click"; + /// + /// Constant for a boolean interaction binding '.../input/y/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the user path. + /// + public const string buttonYTouch = "/input/y/touch"; + /// + /// Constant for a boolean interaction binding '.../input/menu/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the user path. + /// + public const string menu = "/input/menu/click"; + + // Right Hand Only + /// + /// Constant for a boolean interaction binding '.../input/a/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the user path. + /// + public const string buttonA = "/input/a/click"; + /// + /// Constant for a boolean interaction binding '.../input/a/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the user path. + /// + public const string buttonATouch = "/input/a/touch"; + /// + /// Constant for a boolean interaction binding '..."/input/b/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the user path. + /// + public const string buttonB = "/input/b/click"; + /// + /// Constant for a boolean interaction binding '.../input/b/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the user path. + /// + public const string buttonBTouch = "/input/b/touch"; + /// + /// Constant for a boolean interaction binding '.../input/system/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. This binding is only available for the user path. + /// + public const string system = "/input/system/click"; + + // Both Hands + /// + /// Constant for a float interaction binding '.../input/squeeze/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. + /// + public const string squeeze = "/input/squeeze/value"; + /// + /// Constant for a float interaction binding '.../input/trigger/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. + /// + public const string trigger = "/input/trigger/value"; + /// + /// Constant for a boolean interaction binding '.../input/trigger/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. + /// + public const string triggerTouch = "/input/trigger/touch"; + /// + /// Constant for a Vector2 interaction binding '...//input/thumbstick' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. + /// + public const string thumbstick = "/input/thumbstick"; + /// + /// Constant for a boolean interaction binding '.../input/thumbstick/click' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. + /// + public const string thumbstickClick = "/input/thumbstick/click"; + /// + /// Constant for a boolean interaction binding '.../input/thumbstick/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. + /// + public const string thumbstickTouch = "/input/thumbstick/touch"; + /// + /// Constant for a boolean interaction binding '.../input/thumbrest/touch' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. + /// + public const string thumbrest = "/input/thumbrest/touch"; + /// + /// Constant for a pose interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. + /// + public const string grip = "/input/grip/pose"; + /// + /// Constant for a pose interaction binding '.../input/aim/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. + /// + public const string aim = "/input/aim/pose"; + /// + /// Constant for a haptic interaction binding '.../output/haptic' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. + /// + public const string haptic = "/output/haptic"; + /// + /// Constant for a float interaction binding '.../input/thumbrest/force' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. + /// + public const string thumbrestForce = "/input/thumbrest/force"; + /// + /// Constant for a float interaction binding '.../input/stylus_fb/force' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. + /// + public const string stylusForce = "/input/stylus_fb/force"; + /// + /// Constant for a float interaction binding '.../input/trigger/curl_fb' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. + /// + public const string triggerCurl = "/input/trigger/curl_fb"; + /// + /// Constant for a float interaction binding '.../input/trigger/slide_fb' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. + /// + public const string triggerSlide = "/input/trigger/slide_fb"; + /// + /// Constant for a haptic interaction binding '.../output/haptic' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. + /// + public const string triggerProximity = "/input/trigger/proximity_fb"; + /// + /// Constant for a haptic interaction binding '.../output/haptic' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. + /// + public const string thumbProximity = "/input/thumb_fb/proximity_fb"; + /// + /// Constant for a haptic interaction binding '.../output/haptic' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. + /// + public const string hapticTrigger = "/output/trigger_haptic_fb"; + /// + /// Constant for a haptic interaction binding '.../output/haptic' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. + /// + public const string hapticThumb = "/output/thumb_haptic_fb"; + + private const string kDeviceLocalizedName = "Meta Quest Pro Touch Controller OpenXR"; + + protected internal override bool OnInstanceCreate(ulong instance) + { + // Requires the pro controller extension + if(!OpenXRRuntime.IsExtensionEnabled("XR_FB_touch_controller_pro")) + return false; + + return base.OnInstanceCreate(instance); + } + /// + /// Registers the layout with the Input System. + /// + protected override void RegisterDeviceLayout() + { +#if UNITY_EDITOR + if (!OpenXRLoaderEnabledForEditorPlayMode()) + return; +#endif + InputSystem.InputSystem.RegisterLayout(typeof(QuestProTouchController), + matches: new InputDeviceMatcher() + .WithInterface(XRUtilities.InterfaceMatchAnyVersion) + .WithProduct(kDeviceLocalizedName)); + } + + /// + /// Removes the layout from the Input System. + /// + protected override void UnregisterDeviceLayout() + { +#if UNITY_EDITOR + if (!OpenXRLoaderEnabledForEditorPlayMode()) + return; +#endif + InputSystem.InputSystem.RemoveLayout(nameof(QuestProTouchController)); + } + + /// + protected override void RegisterActionMapsWithRuntime() + { + ActionMapConfig actionMap = new ActionMapConfig() + { + name = "questprotouchcontroller", + localizedName = kDeviceLocalizedName, + desiredInteractionProfile = profile, + manufacturer = "Oculus", + serialNumber = "", + deviceInfos = new List() + { + new DeviceConfig() + { + characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left), + userPath = UserPaths.leftHand + }, + new DeviceConfig() + { + characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right), + userPath = UserPaths.rightHand + } + }, + actions = new List() + { + // Joystick + new ActionConfig() + { + name = "thumbstick", + localizedName = "Thumbstick", + type = ActionType.Axis2D, + usages = new List() + { + "Primary2DAxis" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = thumbstick, + interactionProfileName = profile, + } + } + }, + // Grip + new ActionConfig() + { + name = "grip", + localizedName = "Grip", + type = ActionType.Axis1D, + usages = new List() + { + "Grip" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = squeeze, + interactionProfileName = profile, + } + } + }, + // Grip Pressed + new ActionConfig() + { + name = "gripPressed", + localizedName = "Grip Pressed", + type = ActionType.Binary, + usages = new List() + { + "GripButton" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = squeeze, + interactionProfileName = profile, + } + } + }, + // Menu + new ActionConfig() + { + name = "menu", + localizedName = "Menu", + type = ActionType.Binary, + usages = new List() + { + "MenuButton" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = menu, + interactionProfileName = profile, + userPaths = new List() { UserPaths.leftHand } + }, + new ActionBinding() + { + interactionPath = system, + interactionProfileName = profile, + userPaths = new List() { UserPaths.rightHand } + }, + } + }, + //A / X Press + new ActionConfig() + { + name = "primaryButton", + localizedName = "Primary Button", + type = ActionType.Binary, + usages = new List() + { + "PrimaryButton" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = buttonX, + interactionProfileName = profile, + userPaths = new List() { UserPaths.leftHand } + }, + new ActionBinding() + { + interactionPath = buttonA, + interactionProfileName = profile, + userPaths = new List() { UserPaths.rightHand } + }, + } + }, + //A / X Touch + new ActionConfig() + { + name = "primaryTouched", + localizedName = "Primary Touched", + type = ActionType.Binary, + usages = new List() + { + "PrimaryTouch" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = buttonXTouch, + interactionProfileName = profile, + userPaths = new List() { UserPaths.leftHand } + }, + new ActionBinding() + { + interactionPath = buttonATouch, + interactionProfileName = profile, + userPaths = new List() { UserPaths.rightHand } + }, + } + }, + //B / Y Press + new ActionConfig() + { + name = "secondaryButton", + localizedName = "Secondary Button", + type = ActionType.Binary, + usages = new List() + { + "SecondaryButton" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = buttonY, + interactionProfileName = profile, + userPaths = new List() { UserPaths.leftHand } + }, + new ActionBinding() + { + interactionPath = buttonB, + interactionProfileName = profile, + userPaths = new List() { UserPaths.rightHand } + }, + } + }, + //B / Y Touch + new ActionConfig() + { + name = "secondaryTouched", + localizedName = "Secondary Touched", + type = ActionType.Binary, + usages = new List() + { + "SecondaryTouch" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = buttonYTouch, + interactionProfileName = profile, + userPaths = new List() { UserPaths.leftHand } + }, + new ActionBinding() + { + interactionPath = buttonBTouch, + interactionProfileName = profile, + userPaths = new List() { UserPaths.rightHand } + }, + } + }, + // Trigger + new ActionConfig() + { + name = "trigger", + localizedName = "Trigger", + type = ActionType.Axis1D, + usages = new List() + { + "Trigger" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = trigger, + interactionProfileName = profile, + } + } + }, + // Trigger Pressed + new ActionConfig() + { + name = "triggerPressed", + localizedName = "Trigger Pressed", + type = ActionType.Binary, + usages = new List() + { + "TriggerButton" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = trigger, + interactionProfileName = profile, + } + } + }, + //Trigger Touch + new ActionConfig() + { + name = "triggerTouched", + localizedName = "Trigger Touched", + type = ActionType.Binary, + usages = new List() + { + "TriggerTouch" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = triggerTouch, + interactionProfileName = profile, + } + } + }, + //Thumbstick Clicked + new ActionConfig() + { + name = "thumbstickClicked", + localizedName = "Thumbstick Clicked", + type = ActionType.Binary, + usages = new List() + { + "Primary2DAxisClick" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = thumbstickClick, + interactionProfileName = profile, + } + } + }, + //Thumbstick Touched + new ActionConfig() + { + name = "thumbstickTouched", + localizedName = "Thumbstick Touched", + type = ActionType.Binary, + usages = new List() + { + "Primary2DAxisTouch" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = thumbstickTouch, + interactionProfileName = profile, + } + } + }, + //Thumbrest Touched + new ActionConfig() + { + name = "thumbrestTouched", + localizedName = "Thumbrest Touched", + type = ActionType.Binary, + usages = new List() + { + "ThumbrestTouch" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = thumbrest, + interactionProfileName = profile, + } + } + }, + // Device Pose + new ActionConfig() + { + name = "devicePose", + localizedName = "Device Pose", + type = ActionType.Pose, + usages = new List() + { + "Device" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = grip, + interactionProfileName = profile, + } + } + }, + // Pointer Pose + new ActionConfig() + { + name = "pointer", + localizedName = "Pointer Pose", + type = ActionType.Pose, + usages = new List() + { + "Pointer" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = aim, + interactionProfileName = profile, + } + } + }, + // Haptics + new ActionConfig() + { + name = "haptic", + localizedName = "Haptic Output", + type = ActionType.Vibrate, + usages = new List() { "Haptic" }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = haptic, + interactionProfileName = profile, + } + } + }, + new ActionConfig() + { + name = "thumbrestForce", + localizedName = "Thumbrest Force", + type = ActionType.Axis1D, + usages = new List() + { + "ThumbrestForce" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = thumbrestForce, + interactionProfileName = profile, + } + } + }, + new ActionConfig() + { + name = "stylusForce", + localizedName = "Stylus Force", + type = ActionType.Axis1D, + usages = new List() + { + "StylusForce" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = stylusForce, + interactionProfileName = profile, + } + } + }, + new ActionConfig() + { + name = "triggerCurl", + localizedName = "Trigger Curl", + type = ActionType.Axis1D, + usages = new List() + { + "TriggerCurl" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = triggerCurl, + interactionProfileName = profile, + } + } + }, + new ActionConfig() + { + name = "triggerSlide", + localizedName = "Trigger Slide", + type = ActionType.Axis1D, + usages = new List() + { + "TriggerSlide" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = triggerSlide, + interactionProfileName = profile, + } + } + }, + //Trigger Proximity + new ActionConfig() + { + name = "triggerProximity", + localizedName = "Trigger Proximity", + type = ActionType.Binary, + usages = new List() + { + "TriggerProximity" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = triggerProximity, + interactionProfileName = profile, + } + } + }, + //Thumb Proximity + new ActionConfig() + { + name = "thumbProximity", + localizedName = "Thumb Proximity", + type = ActionType.Binary, + usages = new List() + { + "ThumbProximity" + }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = thumbProximity, + interactionProfileName = profile, + } + } + }, + //Haptic Trigger + new ActionConfig() + { + name = "hapticTrigger", + localizedName = "Haptic Trigger Output", + type = ActionType.Vibrate, + usages = new List() { "HapticTrigger" }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = hapticTrigger, + interactionProfileName = profile, + } + } + }, + // Haptic Thumb + new ActionConfig() + { + name = "hapticThumb", + localizedName = "Haptic Thumb Output", + type = ActionType.Vibrate, + usages = new List() { "HapticThumb" }, + bindings = new List() + { + new ActionBinding() + { + interactionPath = hapticThumb, + interactionProfileName = profile, + } + } + } + } + }; + + AddActionMap(actionMap); + } + } +} diff --git a/Runtime/Features/Interactions/MetaQuestTouchProControllerProfile.cs.meta b/Runtime/Features/Interactions/MetaQuestTouchProControllerProfile.cs.meta new file mode 100644 index 0000000..9ca6282 --- /dev/null +++ b/Runtime/Features/Interactions/MetaQuestTouchProControllerProfile.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c4b862ee14fb479fbfe5fffe655d3ed3 +timeCreated: 1667676951 \ No newline at end of file diff --git a/Runtime/Features/OpenXRFeature.cs b/Runtime/Features/OpenXRFeature.cs index 7dfd482..2999382 100644 --- a/Runtime/Features/OpenXRFeature.cs +++ b/Runtime/Features/OpenXRFeature.cs @@ -530,6 +530,11 @@ protected virtual void OnDisable() // Virtual for future expansion and to match OnEnable } + /// + protected virtual void Awake() + { + } + internal enum LoaderEvent { SubsystemCreate, @@ -603,6 +608,7 @@ internal enum NativeEvent XrInstanceLossPending, XrRestartRequested, XrRequestRestartLoop, + XrRequestGetSystemLoop, }; internal static void ReceiveNativeEvent(NativeEvent e, ulong payload) diff --git a/Runtime/OpenXRLoader.cs b/Runtime/OpenXRLoader.cs index b0522ca..ade8cde 100644 --- a/Runtime/OpenXRLoader.cs +++ b/Runtime/OpenXRLoader.cs @@ -615,7 +615,12 @@ private static void ReceiveNativeEvent(OpenXRFeature.NativeEvent e, ulong payloa break; case OpenXRFeature.NativeEvent.XrRequestRestartLoop: - OpenXRRestarter.Instance.PauseAndRestart(); + Debug.Log("XR Initialization failed, will try to restart xr periodically."); + OpenXRRestarter.Instance.PauseAndShutdownAndRestart(); + break; + + case OpenXRFeature.NativeEvent.XrRequestGetSystemLoop: + OpenXRRestarter.Instance.PauseAndRetryInitialization(); break; case OpenXRFeature.NativeEvent.XrStopping: diff --git a/Runtime/OpenXRProjectValidation.cs b/Runtime/OpenXRProjectValidation.cs index 868d1ad..1d02443 100644 --- a/Runtime/OpenXRProjectValidation.cs +++ b/Runtime/OpenXRProjectValidation.cs @@ -65,7 +65,12 @@ public static class OpenXRProjectValidation return manager.activeLoaders.OfType().Any(); }) +#if UNITY_2023_1_OR_NEWER + .Any(buildTarget => PlayerSettings.GetGraphicsAPIs(buildTarget).Any(g => g == GraphicsDeviceType.OpenGLES3)); +#else + // Keeping OpenGL ES 2 support for 2022 and older versions. .Any(buildTarget => PlayerSettings.GetGraphicsAPIs(buildTarget).Any(g => g == GraphicsDeviceType.OpenGLES2 || g == GraphicsDeviceType.OpenGLES3)); +#endif }, fixIt = () => PlayerSettings.colorSpace = ColorSpace.Linear, fixItMessage = "Set PlayerSettings.colorSpace to ColorSpace.Linear", diff --git a/Runtime/OpenXRRestarter.cs b/Runtime/OpenXRRestarter.cs index 7c0c9f9..d670c0c 100644 --- a/Runtime/OpenXRRestarter.cs +++ b/Runtime/OpenXRRestarter.cs @@ -49,8 +49,6 @@ public void ResetCallbacks () private Coroutine m_Coroutine; - private Coroutine m_pauseAndRestartCoroutine; - private static int m_pauseAndRestartAttempts = 0; public static float TimeBetweenRestartAttempts @@ -100,7 +98,7 @@ public void Shutdown () return; } - m_Coroutine = StartCoroutine(RestartCoroutine(false)); + m_Coroutine = StartCoroutine(RestartCoroutine(false, true)); } /// @@ -117,76 +115,101 @@ public void ShutdownAndRestart () return; } - m_Coroutine = StartCoroutine(RestartCoroutine(true)); + m_Coroutine = StartCoroutine(RestartCoroutine(true, true)); } /// - /// Pause and then restart. - /// If the restart triggers another restart, the pause adds some delay between restarts. + /// Start a coroutine that will pause, then shut xr down, then re-initialize xr. /// - public void PauseAndRestart() + public void PauseAndShutdownAndRestart() { if (OpenXRLoader.Instance == null) + { return; + } + + StartCoroutine(PauseAndShutdownAndRestartCoroutine(TimeBetweenRestartAttempts)); + } - if (m_pauseAndRestartCoroutine != null) + /// + /// Start a coroutine that will pause, then re-initialize xr if xr hasn't already succeeded. + /// + public void PauseAndRetryInitialization() + { + if (OpenXRLoader.Instance == null) { - Debug.LogError("Only one pause then shutdown/restart can be executed at a time"); return; } - Debug.Log("Please make sure the device is connected. Will try to restart xr periodically."); - m_pauseAndRestartCoroutine = StartCoroutine(PauseAndRestartCoroutine(TimeBetweenRestartAttempts)); + StartCoroutine(PauseAndRetryInitializationCoroutine(TimeBetweenRestartAttempts)); } - public IEnumerator PauseAndRestartCoroutine(float pauseTimeInSeconds) + public IEnumerator PauseAndShutdownAndRestartCoroutine(float pauseTimeInSeconds) { try { + // Wait a few seconds to add delay between restart requests in a restart loop. yield return new WaitForSeconds(pauseTimeInSeconds); + + yield return new WaitForRestartFinish(); + m_pauseAndRestartAttempts += 1; - if (m_Coroutine == null) - { - m_Coroutine = StartCoroutine(RestartCoroutine(true)); - } - else + m_Coroutine = StartCoroutine(RestartCoroutine(true, true)); + } + finally + { + onAfterCoroutine?.Invoke(); + } + } + + public IEnumerator PauseAndRetryInitializationCoroutine(float pauseTimeInSeconds) + { + try + { + // Wait a few seconds to add delay between restart requests in a restart loop. + yield return new WaitForSeconds(pauseTimeInSeconds); + + yield return new WaitForRestartFinish(); + + bool shouldSkipRestart = XRGeneralSettings.Instance.Manager.activeLoader != null; + if (!shouldSkipRestart) { - Debug.LogError(String.Format("Restart/Shutdown already in progress so skipping this attempt.")); + m_pauseAndRestartAttempts += 1; + m_Coroutine = StartCoroutine(RestartCoroutine(true, false)); } } finally { - m_pauseAndRestartCoroutine = null; onAfterCoroutine?.Invoke(); } } - private IEnumerator RestartCoroutine (bool shouldRestart) + private IEnumerator RestartCoroutine (bool shouldRestart, bool shouldShutdown) { try { - yield return null; + if (shouldShutdown) + { + Debug.Log("Shutting down OpenXR."); + yield return null; - // Always shutdown the loader - XRGeneralSettings.Instance.Manager.DeinitializeLoader(); - yield return null; + // Always shutdown the loader + XRGeneralSettings.Instance.Manager.DeinitializeLoader(); + yield return null; - onAfterShutdown?.Invoke(); + onAfterShutdown?.Invoke(); + } // Restart? if (shouldRestart && OpenXRRuntime.ShouldRestart()) { + Debug.Log("Initializing OpenXR."); yield return XRGeneralSettings.Instance.Manager.InitializeLoader(); XRGeneralSettings.Instance.Manager.StartSubsystems(); - if (XRGeneralSettings.Instance.Manager.activeLoader == null) - { - Debug.LogError("Failure to restart OpenXRLoader after shutdown."); - } - else + if (XRGeneralSettings.Instance.Manager.activeLoader != null) { - Debug.Log("OpenXRLoader restart successful."); m_pauseAndRestartAttempts = 0; onAfterSuccessfulRestart?.Invoke(); } diff --git a/Runtime/OpenXRRuntime.cs b/Runtime/OpenXRRuntime.cs index b4b5ce8..bcd5c16 100644 --- a/Runtime/OpenXRRuntime.cs +++ b/Runtime/OpenXRRuntime.cs @@ -97,6 +97,22 @@ public static string[] GetAvailableExtensions() /// public static event Func wantsToRestart; + /// + /// This bool controls whether or not to retry initialization if the runtime reports a + /// FORM_FACTOR_UNAVAILABLE error during initialization. + /// + public static bool retryInitializationOnFormFactorErrors + { + get + { + return Internal_GetSoftRestartLoopAtInitialization(); + } + set + { + Internal_SetSoftRestartLoopAtInitialization(value); + } + } + /// /// Invokes the given event function and returns true if all invocations return true /// @@ -166,6 +182,12 @@ internal static void ClearEvents () [DllImport(LibraryName, EntryPoint = "unity_ext_GetEnabledExtensionName", CharSet = CharSet.Ansi)] private static extern bool Internal_GetEnabledExtensionNamePtr(uint index, out IntPtr outName); + [DllImport(LibraryName, EntryPoint = "session_SetSoftRestartLoopAtInitialization")] + private static extern void Internal_SetSoftRestartLoopAtInitialization(bool value); + + [DllImport(LibraryName, EntryPoint = "session_GetSoftRestartLoopAtInitialization")] + private static extern bool Internal_GetSoftRestartLoopAtInitialization(); + private static bool Internal_GetEnabledExtensionName(uint index, out string extensionName) { if (!Internal_GetEnabledExtensionNamePtr(index, out var extensionNamePtr)) diff --git a/Runtime/UnitySubsystemsManifest.json b/Runtime/UnitySubsystemsManifest.json index 1111097..cc6f43e 100644 --- a/Runtime/UnitySubsystemsManifest.json +++ b/Runtime/UnitySubsystemsManifest.json @@ -1,6 +1,6 @@ { "name": "OpenXR XR Plugin", - "version": "1.6.0", + "version": "1.7.0", "libraryName": "UnityOpenXR", "displays": [ { diff --git a/Runtime/WaitForRestartFinish.cs b/Runtime/WaitForRestartFinish.cs new file mode 100644 index 0000000..7927916 --- /dev/null +++ b/Runtime/WaitForRestartFinish.cs @@ -0,0 +1,36 @@ +namespace UnityEngine.XR.OpenXR +{ + /// + /// Custom yield instruction that waits for the OpenXRRestarter to finish if it is running. + /// + internal sealed class WaitForRestartFinish : CustomYieldInstruction + { + private float m_Timeout = 0; + + public WaitForRestartFinish(float timeout = 5.0f) + { + m_Timeout = Time.realtimeSinceStartup + timeout; + } + + public override bool keepWaiting + { + get + { + // Wait until the restarter is finished + if (!OpenXRRestarter.Instance.isRunning) + { + return false; + } + + // Did we time out waiting? + if (Time.realtimeSinceStartup > m_Timeout) + { + Debug.LogError("WaitForRestartFinish: Timeout"); + return false; + } + + return true; + } + } + } +} diff --git a/Runtime/WaitForRestartFinish.cs.meta b/Runtime/WaitForRestartFinish.cs.meta new file mode 100644 index 0000000..04fbc9d --- /dev/null +++ b/Runtime/WaitForRestartFinish.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 81df544512fba6b4ea2cf8723ffa2063 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/android.meta b/Runtime/android.meta index 29006d9..0e32368 100644 --- a/Runtime/android.meta +++ b/Runtime/android.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e98d4bf428254c02b84722bac5689c70 +guid: 6f916eda278e405685a6e2f89b9ceff7 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Runtime/android/arm64.meta b/Runtime/android/arm64.meta index 9182f78..2c1fdca 100644 --- a/Runtime/android/arm64.meta +++ b/Runtime/android/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 58eb4ce7f41244dbb4f435a388f1014b +guid: 13853a13614e45c481452da3b3542c69 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Runtime/android/arm64/libUnityOpenXR.so b/Runtime/android/arm64/libUnityOpenXR.so index 0dd9a9e..e976223 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 1662a82..d7c8805 100644 --- a/Runtime/android/arm64/libUnityOpenXR.so.meta +++ b/Runtime/android/arm64/libUnityOpenXR.so.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a6320d36c55e45d9a581540b2e502f76 +guid: c08484e0cb4140f886dfca94fe37eb04 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/input/OpenXRInput.cs b/Runtime/input/OpenXRInput.cs index 3ca54e9..9ae599f 100644 --- a/Runtime/input/OpenXRInput.cs +++ b/Runtime/input/OpenXRInput.cs @@ -462,6 +462,31 @@ public static bool TryGetInputSourceName ( return Internal_TryGetInputSourceName(GetDeviceId(inputDevice), actionHandle, (uint) index, (uint) flags, out name); } + /// + /// Return the active state of the given action + /// + /// Input Action + /// True if the given action has any bindings that are active + public static bool GetActionIsActive(InputAction inputAction) + { + if (inputAction != null && + inputAction.controls.Count > 0 && + inputAction.controls[0].device != null) + { + for (var index = 0; index < inputAction.controls.Count; ++index) + { + var deviceId = GetDeviceId(inputAction.controls[index].device); + if (deviceId == 0) + continue; + + var controlName = GetActionHandleName(inputAction.controls[index]); + if (Internal_GetActionIsActive(deviceId, controlName)) + return true; + } + } + return false; + } + [StructLayout(LayoutKind.Explicit, Size = k_Size)] private struct GetInternalDeviceIdCommand : IInputDeviceCommandInfo { @@ -484,7 +509,7 @@ public static GetInternalDeviceIdCommand Create() => /// Source InputAction /// Optional InputDevice to filter by /// OpenXR handle that is associated with the given InputAction or 0 if not found - internal static ulong GetActionHandle(InputAction inputAction, InputSystem.InputDevice inputDevice = null) + public static ulong GetActionHandle(InputAction inputAction, InputSystem.InputDevice inputDevice = null) { if (inputAction == null || inputAction.controls.Count == 0) return 0; @@ -576,6 +601,9 @@ internal static bool Internal_TryGetInputSourceName(uint deviceId, ulong actionI return true; } + [DllImport(Library, EntryPoint = "OpenXRInputProvider_GetActionIsActive")] + private static extern bool Internal_GetActionIsActive(uint deviceId, string name); + [DllImport(Library, EntryPoint = "OpenXRInputProvider_RegisterDeviceDefinition", CharSet = CharSet.Ansi)] private static extern ulong Internal_RegisterDeviceDefinition(string userPath, string interactionProfile, uint characteristics, string name, string manufacturer, string serialNumber); diff --git a/Runtime/universalwindows.meta b/Runtime/universalwindows.meta index aab548f..26110d0 100644 --- a/Runtime/universalwindows.meta +++ b/Runtime/universalwindows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: eac47b4da46e4686958c9da45866e3f9 +guid: b2eecbccc33945b28a345614bb5f0d8a folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Runtime/universalwindows/arm32.meta b/Runtime/universalwindows/arm32.meta index 8c8fb03..90a613f 100644 --- a/Runtime/universalwindows/arm32.meta +++ b/Runtime/universalwindows/arm32.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b8b473c3db064671a4d838a0164ef018 +guid: b71c4d2c03ce4a2abb99fcc31c54fd0f folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Runtime/universalwindows/arm32/UnityOpenXR.dll b/Runtime/universalwindows/arm32/UnityOpenXR.dll index a35b507..102ad5b 100644 Binary files a/Runtime/universalwindows/arm32/UnityOpenXR.dll and b/Runtime/universalwindows/arm32/UnityOpenXR.dll differ diff --git a/Runtime/universalwindows/arm32/UnityOpenXR.dll.meta b/Runtime/universalwindows/arm32/UnityOpenXR.dll.meta index 335865a..83a5adb 100644 --- a/Runtime/universalwindows/arm32/UnityOpenXR.dll.meta +++ b/Runtime/universalwindows/arm32/UnityOpenXR.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2af0615199d34b0d80118d2f80edc965 +guid: c1f1471bb31c4df7ac2125e59ec3b60e PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/universalwindows/arm64.meta b/Runtime/universalwindows/arm64.meta index 354d635..aeaf493 100644 --- a/Runtime/universalwindows/arm64.meta +++ b/Runtime/universalwindows/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 048d0a5f58c84f198a42ef8edf3249d2 +guid: 5e8b8a9673744ddfa221c7accd7c18d2 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Runtime/universalwindows/arm64/UnityOpenXR.dll b/Runtime/universalwindows/arm64/UnityOpenXR.dll index 20b2327..bfe86b3 100644 Binary files a/Runtime/universalwindows/arm64/UnityOpenXR.dll and b/Runtime/universalwindows/arm64/UnityOpenXR.dll differ diff --git a/Runtime/universalwindows/arm64/UnityOpenXR.dll.meta b/Runtime/universalwindows/arm64/UnityOpenXR.dll.meta index 4b31e1b..426e46e 100644 --- a/Runtime/universalwindows/arm64/UnityOpenXR.dll.meta +++ b/Runtime/universalwindows/arm64/UnityOpenXR.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 86232d19a0b445259e421bbbb4b7d970 +guid: 871744fca8624bd0b19bd7fb0b4c5e16 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/universalwindows/x64.meta b/Runtime/universalwindows/x64.meta index 0bbe230..8164e33 100644 --- a/Runtime/universalwindows/x64.meta +++ b/Runtime/universalwindows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9d1383e417804afdb29fe846c3217600 +guid: 189dc6606cec427992c40a7c8b8e7775 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Runtime/universalwindows/x64/UnityOpenXR.dll b/Runtime/universalwindows/x64/UnityOpenXR.dll index 581de41..5e0744f 100644 Binary files a/Runtime/universalwindows/x64/UnityOpenXR.dll and b/Runtime/universalwindows/x64/UnityOpenXR.dll differ diff --git a/Runtime/universalwindows/x64/UnityOpenXR.dll.meta b/Runtime/universalwindows/x64/UnityOpenXR.dll.meta index 3c69f4c..0c791aa 100644 --- a/Runtime/universalwindows/x64/UnityOpenXR.dll.meta +++ b/Runtime/universalwindows/x64/UnityOpenXR.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9e27c30883714610bf0ccf9000f51668 +guid: db349ebe5a564a99835e2c016334aa1e PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/windows.meta b/Runtime/windows.meta index 674eba7..932a945 100644 --- a/Runtime/windows.meta +++ b/Runtime/windows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: fe19c2b3bf1246a0b19017479c15f6f8 +guid: e51f3348416f4a64b154c86457abd824 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Runtime/windows/x64.meta b/Runtime/windows/x64.meta index f456caf..1086263 100644 --- a/Runtime/windows/x64.meta +++ b/Runtime/windows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d963ad7a19b14fc88d03c91df038f2e9 +guid: 0d3084ed371a4ca4b074945904f7f1ed folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Runtime/windows/x64/UnityOpenXR.dll b/Runtime/windows/x64/UnityOpenXR.dll index f8f168d..6f04c11 100644 Binary files a/Runtime/windows/x64/UnityOpenXR.dll and b/Runtime/windows/x64/UnityOpenXR.dll differ diff --git a/Runtime/windows/x64/UnityOpenXR.dll.meta b/Runtime/windows/x64/UnityOpenXR.dll.meta index 045c923..b4ffd52 100644 --- a/Runtime/windows/x64/UnityOpenXR.dll.meta +++ b/Runtime/windows/x64/UnityOpenXR.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a323efa6ba6a46f382b97f2ab8a4467c +guid: 75694444ccff423e80797b703bdc8d7c PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/RuntimeDebugger/RuntimeDebuggerOpenXRFeature.cs b/RuntimeDebugger/RuntimeDebuggerOpenXRFeature.cs index b073903..17c0b74 100644 --- a/RuntimeDebugger/RuntimeDebuggerOpenXRFeature.cs +++ b/RuntimeDebugger/RuntimeDebuggerOpenXRFeature.cs @@ -73,7 +73,8 @@ internal void RecvMsg(MessageEventArgs args) Native_GetDataForRead(out var ptr2, out var size2); byte[] data = new byte[size1 + size2]; - Marshal.Copy(ptr1, data, 0, (int)size1); + if(size1 > 0) + Marshal.Copy(ptr1, data, 0, (int)size1); if (size2 > 0) Marshal.Copy(ptr2, data, (int)size1, (int)size2); diff --git a/RuntimeDebugger/android.meta b/RuntimeDebugger/android.meta index a0c8b83..8a7a931 100644 --- a/RuntimeDebugger/android.meta +++ b/RuntimeDebugger/android.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 90e025acba09464f9c51c2894511250d +guid: ded756ddf8c54bf1b9916b0d6e4915f2 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeDebugger/android/arm64.meta b/RuntimeDebugger/android/arm64.meta index 0ca3b41..5c70421 100644 --- a/RuntimeDebugger/android/arm64.meta +++ b/RuntimeDebugger/android/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 844c7eb4fd914616aaedb8713f496b4c +guid: e18efeaa3d10448fb13ba3384a80c3a8 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeDebugger/android/arm64/libopenxr_runtime_debugger.so.meta b/RuntimeDebugger/android/arm64/libopenxr_runtime_debugger.so.meta index 6aa256c..8e6a2f0 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: 8338931bc7ca4010b5240cf1565db534 +guid: 1f9d8c4700ce45ed8058b5859ef6cc52 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/RuntimeDebugger/universalwindows.meta b/RuntimeDebugger/universalwindows.meta index eb31420..3ad3a66 100644 --- a/RuntimeDebugger/universalwindows.meta +++ b/RuntimeDebugger/universalwindows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 94ac55d1352f4d658bfc942cdc22019d +guid: 51d332dcd57041d19afc7c94a623e3f9 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeDebugger/universalwindows/arm32.meta b/RuntimeDebugger/universalwindows/arm32.meta index dbb04ca..d5ce1d9 100644 --- a/RuntimeDebugger/universalwindows/arm32.meta +++ b/RuntimeDebugger/universalwindows/arm32.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 51925f4508824f0da05291d12539e03f +guid: c635a85d57f043ff9230d58e658d9191 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeDebugger/universalwindows/arm32/openxr_runtime_debugger.dll b/RuntimeDebugger/universalwindows/arm32/openxr_runtime_debugger.dll index 66dab45..2a3567e 100644 Binary files a/RuntimeDebugger/universalwindows/arm32/openxr_runtime_debugger.dll 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 index dc2a7cd..33761e5 100644 --- a/RuntimeDebugger/universalwindows/arm32/openxr_runtime_debugger.dll.meta +++ b/RuntimeDebugger/universalwindows/arm32/openxr_runtime_debugger.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b44ba2d558cc41418e0ece23df9fbb0a +guid: f704da867fe046c69d506f61ad0a20a5 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/RuntimeDebugger/universalwindows/arm64.meta b/RuntimeDebugger/universalwindows/arm64.meta index d429982..d1f4948 100644 --- a/RuntimeDebugger/universalwindows/arm64.meta +++ b/RuntimeDebugger/universalwindows/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d70b335e59904b04ae314842091d8669 +guid: 16de837032fd4882a7b061765c276d76 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeDebugger/universalwindows/arm64/openxr_runtime_debugger.dll b/RuntimeDebugger/universalwindows/arm64/openxr_runtime_debugger.dll index efd9484..55df878 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 678e331..eb84545 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: 3beee7a518c241f482179b79effa2c1c +guid: 52f9e6ada2464f29a0e28f0eb8361909 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/RuntimeDebugger/universalwindows/x64.meta b/RuntimeDebugger/universalwindows/x64.meta index 2404e63..b2dc89d 100644 --- a/RuntimeDebugger/universalwindows/x64.meta +++ b/RuntimeDebugger/universalwindows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f0d7eb9fdc8046c393266fb7a94122bf +guid: f7bc53eed44c434096e5f5bc9dd81d94 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeDebugger/universalwindows/x64/openxr_runtime_debugger.dll b/RuntimeDebugger/universalwindows/x64/openxr_runtime_debugger.dll index f9dee76..1698e67 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 abf3862..e5989ed 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: 51a0fb384ef44c069cac7b246dd7d5c0 +guid: 6e59444b78414e9c8390e7c4a95479fc PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/RuntimeDebugger/windows.meta b/RuntimeDebugger/windows.meta index 29ff405..6a90373 100644 --- a/RuntimeDebugger/windows.meta +++ b/RuntimeDebugger/windows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9665b47b9c034524b2489bb7c792fbc2 +guid: e525be948e834851992168525c8c89e0 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeDebugger/windows/x64.meta b/RuntimeDebugger/windows/x64.meta index 7dadebb..09fb4d1 100644 --- a/RuntimeDebugger/windows/x64.meta +++ b/RuntimeDebugger/windows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9679574138c8446a8291e1e90b8526f2 +guid: 12792df5eeb045bf8edf8ceb9e2f5390 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeDebugger/windows/x64/openxr_runtime_debugger.dll b/RuntimeDebugger/windows/x64/openxr_runtime_debugger.dll index 89acb5b..357c8ab 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 4354f8a..5dcba65 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: 83d317d317fc4ba3a45175463398d7c1 +guid: d96ffdabeb964e17b8f6a25b1e26be0e PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/RuntimeLoaders/android.meta b/RuntimeLoaders/android.meta index a39d073..e8f7f5e 100644 --- a/RuntimeLoaders/android.meta +++ b/RuntimeLoaders/android.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e977a972b96c4b08b903d4451b76be98 +guid: 40e65cae34a049909b29eb0306583380 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeLoaders/android/openxr_loader.aar.meta b/RuntimeLoaders/android/openxr_loader.aar.meta index 557c3e1..a848b28 100644 --- a/RuntimeLoaders/android/openxr_loader.aar.meta +++ b/RuntimeLoaders/android/openxr_loader.aar.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 46e16f8eecdd4d928918eb7fe33dc3bc +guid: 08b1ec94b3a94a01b14e893bae5e1bbe PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/RuntimeLoaders/universalwindows.meta b/RuntimeLoaders/universalwindows.meta index ad8027d..0f55b39 100644 --- a/RuntimeLoaders/universalwindows.meta +++ b/RuntimeLoaders/universalwindows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c94c368ca2f0400eb826794980167220 +guid: 76844eb339f0418092648522ddc2449b folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeLoaders/universalwindows/arm32.meta b/RuntimeLoaders/universalwindows/arm32.meta index c670492..c135445 100644 --- a/RuntimeLoaders/universalwindows/arm32.meta +++ b/RuntimeLoaders/universalwindows/arm32.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 0d9f243c39d6466d9faddd029aff8fb6 +guid: 4e076237e6d54c0bb9bf9082614149b5 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeLoaders/universalwindows/arm32/openxr_loader.dll.meta b/RuntimeLoaders/universalwindows/arm32/openxr_loader.dll.meta index fbf9866..74601fa 100644 --- a/RuntimeLoaders/universalwindows/arm32/openxr_loader.dll.meta +++ b/RuntimeLoaders/universalwindows/arm32/openxr_loader.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 84f5450f5f014498ae8dba7566ce11e6 +guid: 2e207282634048b6803389975e17b89d PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/RuntimeLoaders/universalwindows/arm64.meta b/RuntimeLoaders/universalwindows/arm64.meta index 546c57f..f4cd6f6 100644 --- a/RuntimeLoaders/universalwindows/arm64.meta +++ b/RuntimeLoaders/universalwindows/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: adf3673faabe4aff84768087d83f05a4 +guid: 24413fc9f55d47e29e0077bb131fe2ce folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeLoaders/universalwindows/arm64/openxr_loader.dll.meta b/RuntimeLoaders/universalwindows/arm64/openxr_loader.dll.meta index 5807128..7f91d96 100644 --- a/RuntimeLoaders/universalwindows/arm64/openxr_loader.dll.meta +++ b/RuntimeLoaders/universalwindows/arm64/openxr_loader.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 578afa6087cd4dd88f0050eae71ac76d +guid: 54d271a2894443889320ba11cbc57b72 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/RuntimeLoaders/universalwindows/x64.meta b/RuntimeLoaders/universalwindows/x64.meta index 72bfc63..bf5a5f7 100644 --- a/RuntimeLoaders/universalwindows/x64.meta +++ b/RuntimeLoaders/universalwindows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d439872f2c8f40deb03a0e2948a057e9 +guid: a17dd88fc98c4e02bb836d0129cb6600 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeLoaders/universalwindows/x64/openxr_loader.dll.meta b/RuntimeLoaders/universalwindows/x64/openxr_loader.dll.meta index e037dc2..78b3469 100644 --- a/RuntimeLoaders/universalwindows/x64/openxr_loader.dll.meta +++ b/RuntimeLoaders/universalwindows/x64/openxr_loader.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b14753bad8684a9f948faf56259ca5e6 +guid: cc4095fa0253427e8e9cc0746690c9fd PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/RuntimeLoaders/windows.meta b/RuntimeLoaders/windows.meta index de4ece2..01aeb45 100644 --- a/RuntimeLoaders/windows.meta +++ b/RuntimeLoaders/windows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 97a0dd52f8a54c2986b34bafec6396b0 +guid: 32d4c8a5682b4d138860439df7262296 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeLoaders/windows/x64.meta b/RuntimeLoaders/windows/x64.meta index 254fa02..d2b9558 100644 --- a/RuntimeLoaders/windows/x64.meta +++ b/RuntimeLoaders/windows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: fb63a5f4fbd24a05b5f79f1c7d35cf4b +guid: 9da3f25a6b3b44dc806ccd5f7c4e56ea folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/RuntimeLoaders/windows/x64/openxr_loader.dll.meta b/RuntimeLoaders/windows/x64/openxr_loader.dll.meta index 946da4a..dd2d611 100644 --- a/RuntimeLoaders/windows/x64/openxr_loader.dll.meta +++ b/RuntimeLoaders/windows/x64/openxr_loader.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 91ba89f0c454459286d3e72b0224f475 +guid: b796136a58ce49dba8b49a3248bed803 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/Samples~/InterceptFeature/InterceptFeaturePlugin/android.meta b/Samples~/InterceptFeature/InterceptFeaturePlugin/android.meta index 3d3b89a..a230ffb 100644 --- a/Samples~/InterceptFeature/InterceptFeaturePlugin/android.meta +++ b/Samples~/InterceptFeature/InterceptFeaturePlugin/android.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 205ec23d3d76403783ba32996fc05eb4 +guid: e0e118cbc583407996f89bd710c2c3b0 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/InterceptFeature/InterceptFeaturePlugin/android/arm64.meta b/Samples~/InterceptFeature/InterceptFeaturePlugin/android/arm64.meta index e32da3e..0efd6cd 100644 --- a/Samples~/InterceptFeature/InterceptFeaturePlugin/android/arm64.meta +++ b/Samples~/InterceptFeature/InterceptFeaturePlugin/android/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b07f513112764843bf001781159f6105 +guid: 3ed4c719bc784b3b9a5acef715497caa folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows.meta b/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows.meta index 5d37cde..080418e 100644 --- a/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows.meta +++ b/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: dee40d59569149de8ac40130f2d9d4c6 +guid: fbcf8daea7054e88b7519a488595fb18 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/arm32.meta b/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/arm32.meta index 9a92573..5ccf0b9 100644 --- a/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/arm32.meta +++ b/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/arm32.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 11fe78a565ac4181a1a140a48559d186 +guid: 295431c062284b07bc2bb3f1b9257363 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/arm64.meta b/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/arm64.meta index c0927dd..a3dd5af 100644 --- a/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/arm64.meta +++ b/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c2feeb1cba864c02813460f71fb2bb39 +guid: c0f3a0814a3c4490a1e2387dfcca5694 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/x64.meta b/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/x64.meta index c25a6fe..c4a414f 100644 --- a/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/x64.meta +++ b/Samples~/InterceptFeature/InterceptFeaturePlugin/universalwindows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2847c1354f6643d6a3b03737bbba665a +guid: 05d170bc11fc405db7d677d123237d62 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/InterceptFeature/InterceptFeaturePlugin/windows.meta b/Samples~/InterceptFeature/InterceptFeaturePlugin/windows.meta index 2213922..27b2e40 100644 --- a/Samples~/InterceptFeature/InterceptFeaturePlugin/windows.meta +++ b/Samples~/InterceptFeature/InterceptFeaturePlugin/windows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c1ed3e5f6e984525979b3a0f318ed716 +guid: ad9c4db0ea6c466880412d74ae33b454 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/InterceptFeature/InterceptFeaturePlugin/windows/x64.meta b/Samples~/InterceptFeature/InterceptFeaturePlugin/windows/x64.meta index daff5cc..61d560a 100644 --- a/Samples~/InterceptFeature/InterceptFeaturePlugin/windows/x64.meta +++ b/Samples~/InterceptFeature/InterceptFeaturePlugin/windows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c37335b62caa4702afab759577806ea6 +guid: 4a60c208cb2c4f4888c3c5f0c4528eac folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/InterceptFeature/InterceptFeaturePlugin/windows/x64/InterceptFeaturePlugin.dll b/Samples~/InterceptFeature/InterceptFeaturePlugin/windows/x64/InterceptFeaturePlugin.dll index 72a3eed..f3d349c 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 b018309..ee396fd 100644 --- a/Samples~/MeshingFeature/MeshingFeaturePlugin/android.meta +++ b/Samples~/MeshingFeature/MeshingFeaturePlugin/android.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: fc644e4b6e5044f39bb9a76ebf9586b5 +guid: 9ce357b55f244a81a2e354af3098bcde folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/android/arm64.meta b/Samples~/MeshingFeature/MeshingFeaturePlugin/android/arm64.meta index 73389fb..12a1794 100644 --- a/Samples~/MeshingFeature/MeshingFeaturePlugin/android/arm64.meta +++ b/Samples~/MeshingFeature/MeshingFeaturePlugin/android/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: fb1ca2ce6afb4299818440a430f66bae +guid: ca00ef0546aa4098b95dd060f9950735 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 a387b15..f3c660f 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: 2ca0fb99e9ac4722aac0995704291830 +guid: fabcb0e1247d423aa1c40443108c0f69 PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows.meta b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows.meta index 6a917b2..5c17b6e 100644 --- a/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows.meta +++ b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 56005c54f9854411b3ee532588ff0320 +guid: bb2171b7410a4d15a7e3274a3f0a94ea folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/arm32.meta b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/arm32.meta index c88d73d..1910efe 100644 --- a/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/arm32.meta +++ b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/arm32.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 4dd9a441c9fb433b96885b32c5a00bb0 +guid: daa7634499554469a922dcac1ebf8c1c folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/arm64.meta b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/arm64.meta index cc61721..644820f 100644 --- a/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/arm64.meta +++ b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/arm64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 36acc326b95e48d3bede30b4730c0aaf +guid: 33903de750894dd0bcd3ecae22d3c0e0 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/x64.meta b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/x64.meta index 9b21389..9a6620b 100644 --- a/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/x64.meta +++ b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e8a5eeb8ac934ee2835f25c2f9073f64 +guid: d7ef1dd6f5754799a189002bf8460b67 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/x64/MeshingFeaturePlugin.dll b/Samples~/MeshingFeature/MeshingFeaturePlugin/universalwindows/x64/MeshingFeaturePlugin.dll index c96b66a..06138ef 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 63db163..1ab1783 100644 --- a/Samples~/MeshingFeature/MeshingFeaturePlugin/windows.meta +++ b/Samples~/MeshingFeature/MeshingFeaturePlugin/windows.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 7f7387a39b044c468e0c80bfb80b1126 +guid: 85535a420128458fb30310994b212e94 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/windows/x64.meta b/Samples~/MeshingFeature/MeshingFeaturePlugin/windows/x64.meta index faac0c6..286c0ce 100644 --- a/Samples~/MeshingFeature/MeshingFeaturePlugin/windows/x64.meta +++ b/Samples~/MeshingFeature/MeshingFeaturePlugin/windows/x64.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c8fc01eadcef4546998ba061b053ac22 +guid: 35c06ee67621401faba7191f66a7593f folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Samples~/MeshingFeature/MeshingFeaturePlugin/windows/x64/MeshingFeaturePlugin.dll b/Samples~/MeshingFeature/MeshingFeaturePlugin/windows/x64/MeshingFeaturePlugin.dll index e439d02..db7ef58 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 f8178d3..92cd3c0 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: 27df427bd6004ce8bcef883c35d898b0 +guid: 68d86c3a78b942b89e8e3dd135bb696d PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/Tests/Editor/zBuildSamplesYamatoOnly.cs b/Tests/Editor/zBuildSamplesYamatoOnly.cs index a6e61ea..cd0c010 100644 --- a/Tests/Editor/zBuildSamplesYamatoOnly.cs +++ b/Tests/Editor/zBuildSamplesYamatoOnly.cs @@ -55,7 +55,7 @@ static void EnableQuestFeature() return; } } - Assert.IsTrue(false, "Could not enable oculus quest extension - if you're not on build machine you must copy dir OculusQuest to your project."); + Assert.IsTrue(false, "Could not enable meta quest extension - if you're not on build machine you must copy dir MetaQuest to your project."); } static void EnableMSFTObserverFeature() @@ -214,7 +214,7 @@ static void EnableAndroidProfiles() #endif new SampleBuildTargetSetup { - sampleRegex = new Regex(".*Render.*"), // Only build vulkan variant for Render Samples + sampleRegex = new Regex(".*RenderSample.*|.*MetaSample.*"), // Only build vulkan variant for Render Samples buildTarget = BuildTarget.Android, targetGroup = BuildTargetGroup.Android, setupPlayerSettings = (outputFile, identifier) => @@ -237,6 +237,7 @@ static void EnableAndroidProfiles() }, new SampleBuildTargetSetup { + sampleRegex = new Regex("^(?!.*MetaSample).*$"), // Don't build the Meta Sample for GLES buildTarget = BuildTarget.Android, targetGroup = BuildTargetGroup.Android, setupPlayerSettings = (outputFile, identifier) => diff --git a/Tests/Runtime/MetaPerformanceMetricsTest.cs b/Tests/Runtime/MetaPerformanceMetricsTest.cs new file mode 100644 index 0000000..6ace551 --- /dev/null +++ b/Tests/Runtime/MetaPerformanceMetricsTest.cs @@ -0,0 +1,187 @@ +using System.Collections; +using System.Collections.Generic; +using NUnit.Framework; +using UnityEngine.TestTools; +using UnityEngine.TestTools.Utils; +using UnityEngine.XR.OpenXR.Features; +using UnityEngine.XR.OpenXR.Features.Mock; +using UnityEngine.XR.OpenXR; +using UnityEngine.XR.OpenXR.NativeTypes; +using ProviderXRStats = UnityEngine.XR.Provider.XRStats; + +namespace UnityEngine.XR.OpenXR.Tests +{ + internal class MetaPerformanceMetricsTests : OpenXRLoaderSetup + { + private static readonly string kAppCPUFrametimeStr = "/perfmetrics_meta/app/cpu_frametime"; + private static readonly string kAppGPUFrametimeStr = "/perfmetrics_meta/app/gpu_frametime"; + private static readonly string kAppMotionToPhotonLatencyStr = + "/perfmetrics_meta/app/motion_to_photon_latency"; + private static readonly string kCompositorCPUFrametimeStr = + "/perfmetrics_meta/compositor/cpu_frametime"; + private static readonly string kCompositorGPUFrametimeStr = + "/perfmetrics_meta/compositor/gpu_frametime"; + private static readonly string kCompositorDroppedFrameCountStr = + "/perfmetrics_meta/compositor/dropped_frame_count"; + private static readonly string kDeviceCPUUtilizationAvgStr = + "/perfmetrics_meta/device/cpu_utilization_average"; + private static readonly string kDeviceCPUUtilizationWorstStr = + "/perfmetrics_meta/device/cpu_utilization_worst"; + private static readonly string kDeviceGPUUtilizationStr = + "/perfmetrics_meta/device/gpu_utilization"; + + private readonly List xrPathStrings = new List + { + kAppCPUFrametimeStr, + kAppGPUFrametimeStr, + kAppGPUFrametimeStr, // because we have 2 consumers + kAppMotionToPhotonLatencyStr, + kCompositorCPUFrametimeStr, + kCompositorGPUFrametimeStr, + kCompositorGPUFrametimeStr, // because we have 2 consumers + kCompositorDroppedFrameCountStr, + kCompositorDroppedFrameCountStr, // because we have 2 consumers + kDeviceCPUUtilizationAvgStr, + kDeviceCPUUtilizationWorstStr, + kDeviceGPUUtilizationStr + }; + + private static readonly string kUnityStatsAppCpuTime = "perfmetrics.appcputime"; + private static readonly string kUnityStatsAppGpuTime = "perfmetrics.appgputime"; + private static readonly string kUnityStatsAppGpuTimeFunc = "GPUAppLastFrameTime"; + private static readonly string kUnityStatsCompositorCpuTime = + "perfmetrics.compositorcputime"; + private static readonly string kUnityStatsCompositorGpuTime = + "perfmetrics.compositorgputime"; + private static readonly string kUnityStatsCompositorGpuTimeFunc = + "GPUCompositorLastFrameTime"; + private static readonly string kUnityStatsGpuUtil = "perfmetrics.gpuutil"; + private static readonly string kUnityStatsCpuUtilAvg = "perfmetrics.cpuutilavg"; + private static readonly string kUnityStatsCpuUtilWorst = "perfmetrics.cpuutilworst"; + private static readonly string kUnityStatsDroppedFrameCount = "appstats.compositordroppedframes"; + private static readonly string kUnityStatsDroppedFrameCountFunc = "droppedFrameCount"; + private static readonly string kUnityStatsMotionToPhotonFunc = "motionToPhoton"; + + private readonly List unityStatStrings = new List + { + kUnityStatsAppCpuTime, + kUnityStatsAppGpuTime, + kUnityStatsAppGpuTimeFunc, + kUnityStatsCompositorCpuTime, + kUnityStatsCompositorGpuTime, + kUnityStatsCompositorGpuTimeFunc, + kUnityStatsGpuUtil, + kUnityStatsCpuUtilAvg, + kUnityStatsCpuUtilWorst, + kUnityStatsDroppedFrameCount, + kUnityStatsDroppedFrameCountFunc, + kUnityStatsMotionToPhotonFunc + }; + + private readonly Dictionary unitMap = + new Dictionary() + { + { + kAppCPUFrametimeStr, + XrPerformanceMetricsCounterUnitMETA.XR_PERFORMANCE_METRICS_COUNTER_UNIT_MILLISECONDS_META + }, + { + kAppGPUFrametimeStr, + XrPerformanceMetricsCounterUnitMETA.XR_PERFORMANCE_METRICS_COUNTER_UNIT_MILLISECONDS_META + }, + { + kAppMotionToPhotonLatencyStr, + XrPerformanceMetricsCounterUnitMETA.XR_PERFORMANCE_METRICS_COUNTER_UNIT_MILLISECONDS_META + }, + { + kCompositorCPUFrametimeStr, + XrPerformanceMetricsCounterUnitMETA.XR_PERFORMANCE_METRICS_COUNTER_UNIT_MILLISECONDS_META + }, + { + kCompositorGPUFrametimeStr, + XrPerformanceMetricsCounterUnitMETA.XR_PERFORMANCE_METRICS_COUNTER_UNIT_MILLISECONDS_META + }, + { + kCompositorDroppedFrameCountStr, + XrPerformanceMetricsCounterUnitMETA.XR_PERFORMANCE_METRICS_COUNTER_UNIT_GENERIC_META + }, + { + kDeviceCPUUtilizationAvgStr, + XrPerformanceMetricsCounterUnitMETA.XR_PERFORMANCE_METRICS_COUNTER_UNIT_PERCENTAGE_META + }, + { + kDeviceCPUUtilizationWorstStr, + XrPerformanceMetricsCounterUnitMETA.XR_PERFORMANCE_METRICS_COUNTER_UNIT_PERCENTAGE_META + }, + { + kDeviceGPUUtilizationStr, + XrPerformanceMetricsCounterUnitMETA.XR_PERFORMANCE_METRICS_COUNTER_UNIT_PERCENTAGE_META + } + }; + + private enum XrPerformanceMetricsCounterUnitMETA + { + XR_PERFORMANCE_METRICS_COUNTER_UNIT_GENERIC_META = 0, + XR_PERFORMANCE_METRICS_COUNTER_UNIT_PERCENTAGE_META = 1, + XR_PERFORMANCE_METRICS_COUNTER_UNIT_MILLISECONDS_META = 2, + XR_PERFORMANCE_METRICS_COUNTER_UNIT_BYTES_META = 3, + XR_PERFORMANCE_METRICS_COUNTER_UNIT_HERTZ_META = 4, + XR_PERFORMANCE_METRICS_COUNTER_UNIT_MAX_ENUM_META = 0x7FFFFFFF + } + + [UnityTest] + public IEnumerator TestAllMetrics() + { + base.InitializeAndStart(); + yield return new WaitForXrFrame(2); + + List displays = new List(); + SubsystemManager.GetInstances(displays); + + Assert.That(displays.Count, Is.EqualTo(1)); + + // insert a -1 dummy value to ensure we're ready when we start querying + for (int i = -1; i < 5; i++) + { + foreach (string xrPathString in xrPathStrings) + { + MockRuntime.MetaPerformanceMetrics_SeedCounterOnce_Float( + xrPathString, + (float)i, + (uint)unitMap[xrPathString] + ); + } + } + + // wait for results to come available. + bool didSync = false; + for (int i = 0; i < 60; i++) + { + if (ProviderXRStats.TryGetStat(displays[0], kUnityStatsAppCpuTime, out var sync)) { + if (sync < 0) + { + didSync = true; + break; + } + if (sync > 0) + Assert.Fail("did not receive -1.f sync stat"); + } + yield return new WaitForXrFrame(); + } + Assert.True(didSync, "did not receive -1.f sync stat"); + + for (int i = 0; i < 5; i++) + { + yield return new WaitForXrFrame(); + foreach (string unityStatString in unityStatStrings) + { + Assert.True( + ProviderXRStats.TryGetStat(displays[0], unityStatString, out var stat), + "did not get stat for " + unityStatString + ); + Assert.That(stat, Is.EqualTo((float)i).Within(0.001)); + } + } + } + } +} diff --git a/Tests/Runtime/MetaPerformanceMetricsTest.cs.meta b/Tests/Runtime/MetaPerformanceMetricsTest.cs.meta new file mode 100644 index 0000000..ae01369 --- /dev/null +++ b/Tests/Runtime/MetaPerformanceMetricsTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 30581af4aa5c63b4493cfae0e77f36d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Runtime/OpenXRInputTests.cs b/Tests/Runtime/OpenXRInputTests.cs index 061056a..b23c7e3 100644 --- a/Tests/Runtime/OpenXRInputTests.cs +++ b/Tests/Runtime/OpenXRInputTests.cs @@ -42,6 +42,7 @@ protected static readonly (Type featureType, Type layoutType, string layoutNameO (typeof(EyeGazeInteraction), typeof(EyeGazeInteraction.EyeGazeDevice), "EyeGaze"), (typeof(MicrosoftHandInteraction), typeof(MicrosoftHandInteraction.HoloLensHand), null), (typeof(KHRSimpleControllerProfile), typeof(KHRSimpleControllerProfile.KHRSimpleController), null), + (typeof(MetaQuestTouchProControllerProfile), typeof(MetaQuestTouchProControllerProfile.QuestProTouchController), null), #if !UNITY_ANDROID (typeof(HTCViveControllerProfile), typeof(HTCViveControllerProfile.ViveController), null), (typeof(MicrosoftMotionControllerProfile), typeof(MicrosoftMotionControllerProfile.WMRSpatialController), null), diff --git a/Tests/Runtime/OpenXRRuntimeTests.cs b/Tests/Runtime/OpenXRRuntimeTests.cs index 3baa821..47d995a 100644 --- a/Tests/Runtime/OpenXRRuntimeTests.cs +++ b/Tests/Runtime/OpenXRRuntimeTests.cs @@ -33,7 +33,8 @@ public void TestAvailableExtensions() "XR_MSFT_secondary_view_configuration", "XR_EXT_eye_gaze_interaction", "XR_MSFT_hand_interaction", - "XR_MSFT_first_person_observer" + "XR_MSFT_first_person_observer", + "XR_META_performance_metrics" }; foreach (string expectedExtension in expectedExtensions) @@ -677,6 +678,29 @@ public IEnumerator FirstPersonObserver() Assert.IsTrue(secondaryLayerCount == 0); } + [UnityTest] + public IEnumerator FirstPersonObserverInvalidSecondaryView() + { + AddExtension("XR_MSFT_secondary_view_configuration"); + AddExtension("XR_MSFT_first_person_observer"); + base.InitializeAndStart(); + + MockRuntime.ActivateSecondaryView(XrViewConfigurationType.SecondaryMonoFirstPersonObserver, true); + + yield return new WaitForXrFrame(2); + + MockRuntime.GetEndFrameStats(out var primaryLayerCount, out var secondaryLayerCount); + Assert.IsTrue(secondaryLayerCount == 1); + + // make mock runtime return invalid state for xrWaitFrame to make sure we don't crash + MockRuntime.ActivateSecondaryView(0, false); + + yield return new WaitForXrFrame(2); + + MockRuntime.GetEndFrameStats(out primaryLayerCount, out secondaryLayerCount); + Assert.IsTrue(secondaryLayerCount == 0); + } + [UnityTest] public IEnumerator ThirdPersonObserver() @@ -811,6 +835,12 @@ public void InitializeException() Assert.IsTrue(OpenXRLoaderBase.Instance == null); } + /// + /// Simulates what can happen when trying to reconnect Link mode after a link disconnect, but + /// the headset is reporting a form factor unavailable error. + /// In this case, the app has already been started, and the runtime reports a form factor unavailable + /// error when trying to initialize xr again. What should happen is a restart loop to restart XR. + /// [UnityTest] public IEnumerator RestartLoopTest() { @@ -861,6 +891,9 @@ public IEnumerator RestartLoopTest() } } + /// + /// Tests that OpenXRRuntime.wantsToRestart is a global switch for de-activating the restarting of XR. + /// [UnityTest] public IEnumerator RestartLoopDisabledTest() { @@ -903,6 +936,11 @@ public IEnumerator RestartLoopDisabledTest() } } + /// + /// Simulates what happens when trying to initialize xr for the first time, and the headset is disconnected, + /// causing xrGetSystem to report a form factor unavailable error. + /// By default, xr initialization should not be retried per feedback from Microsoft. + /// [UnityTest] public IEnumerator RestartLoopDisabledBeforeInitializationTest() { @@ -942,6 +980,70 @@ public IEnumerator RestartLoopDisabledBeforeInitializationTest() } } + /// + /// Simulates what happens when trying to initialize xr for the first time, and the runtime reports + /// a form factor unavailable error for xrGetSystem. If the user chooses, the runtime can retry initialization again and again + /// while waiting for the xrGetSystem call to succeed. + /// By default, xr initialization should not be retried per feedback from Microsoft. This loop needs to be + /// explicitly enabled. + /// + [UnityTest] + public IEnumerator XRGetSystemLoopBeforeInitializationTest() + { + OpenXRLoaderBase.Internal_SetSuccessfullyInitialized(false); + float initialTimeBetweenRestarts = OpenXRRestarter.TimeBetweenRestartAttempts; + bool initialKeepFunctionCallbacks = MockRuntime.KeepFunctionCallbacks; + var initialXRGetSystemCallback = MockRuntime.GetBeforeFunctionCallback("xrGetSystem"); + bool initialRetryInitializationOnFormFactorErrors = OpenXRRuntime.retryInitializationOnFormFactorErrors; + try + { + yield return null; + + // Enable this to prevent xrGetSystem callback override from getting overwritten. + MockRuntime.KeepFunctionCallbacks = true; + + // Reduce the time between restarts to reduce the time of this test. + OpenXRRestarter.TimeBetweenRestartAttempts = 1.0f; + + // Enable the xrGetSystem retry loop per this test. + OpenXRRuntime.retryInitializationOnFormFactorErrors = true; + + // Enable this to ignore the error messages in the logs. + LogAssert.ignoreFailingMessages = true; + + int resetAttempts = 0; + MockRuntime.SetFunctionCallback("xrGetSystem", (name) => + { + MockRuntime.KeepFunctionCallbacks = true; + + resetAttempts += 1; + if (resetAttempts <= 2) + { + return XrResult.FormFactorUnavailable; + } + else + { + return XrResult.Success; + } + }); + + // Trigger initialize, which should call into xrGetSystem. + base.InitializeAndStart(); + + yield return new WaitForLoaderRestart(10, true); + Assert.AreEqual(3, resetAttempts); + Assert.IsTrue(OpenXRLoader.Instance != null, "OpenXR should be initialized"); + } + finally + { + MockRuntime.KeepFunctionCallbacks = initialKeepFunctionCallbacks; + OpenXRRuntime.retryInitializationOnFormFactorErrors = initialRetryInitializationOnFormFactorErrors; + OpenXRRestarter.TimeBetweenRestartAttempts = initialTimeBetweenRestarts; + MockRuntime.SetFunctionCallback("xrGetSystem", initialXRGetSystemCallback); + OpenXRLoaderBase.Internal_SetSuccessfullyInitialized(false); + } + } + [UnityTest] public IEnumerator WantsToRestartTrue () { @@ -1053,6 +1155,11 @@ public IEnumerator CreateSwapChainRuntimeError() Assert.IsTrue(OpenXRLoader.Instance == null, "OpenXR should not be initialized"); } + /// + /// Simulates what can happen when trying to reconnect Link mode after a link disconnect. During + /// xr re-initialization, creating the swapchain can result in a session lost error, which should trigger a + /// loop to attempt to restart xr. + /// [UnityTest] public IEnumerator CreateSwapChainSessionLostError() { @@ -1067,7 +1174,6 @@ public IEnumerator CreateSwapChainSessionLostError() OpenXRRestarter.TimeBetweenRestartAttempts = timeBetweenRestarts; MockRuntime.SetFunctionCallback("xrCreateSwapchain", (func) => XrResult.SessionLost); - LogAssert.Expect(LogType.Log, "OpenXRLoader restart successful."); InitializeAndStart(); yield return new WaitForLoaderRestart(10, true); diff --git a/Tests/Runtime/Unity.XR.OpenXR.Tests.asmdef b/Tests/Runtime/Unity.XR.OpenXR.Tests.asmdef index de39908..4fe6513 100644 --- a/Tests/Runtime/Unity.XR.OpenXR.Tests.asmdef +++ b/Tests/Runtime/Unity.XR.OpenXR.Tests.asmdef @@ -25,4 +25,5 @@ ], "versionDefines": [], "noEngineReferences": false -} \ No newline at end of file +} + diff --git a/package.json b/package.json index ab3794e..bf48388 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.unity.xr.openxr", "displayName": "OpenXR Plugin", - "version": "1.6.0", + "version": "1.7.0", "unity": "2020.3", "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.", @@ -33,13 +33,16 @@ "path": "Samples~/InterceptFeature" } ], + "_upm": { + "changelog": "### Fixed\n* Fixed - Meta builds now don't include Bluetooth permissions in Android manifest by default when using Microphone class in script code.\n* Fixed crash in OpenXR runtime debugger when cache size is set to 0.\n* Fixed OpenXR project validation to check for correct versions of OpenGLES in Unity 2023 and up.\n* Fixed crash when runtime reports an invalid view configuration from xrWaitFrame.\n* Fixed - OpenXR plugin will only look up functions from supported and enabled extensions.\n* Fixed GPU selection in multi-GPU scenarios.\n\n### Updated\n* Updated documentation for the Meta Quest feature.\n\n### Added\n* Added API `OpenXRRuntime.retryInitializationOnFormFactorErrors` to retry xrGetSystem during initialization if xrGetSystem returns a form factor error.\n* Enable XR_META_performance_metrics. This enables performance stats for Meta Quest devices on OpenXR. \n* Add class MetaQuestTouchProControllerProfile.QuestProTouchController new interaction profile to support Meta Quest pro controllers.\n* Added ability for OpenXRFeature derived classes to add Awake() functions.\n* Added API `OpenXRInput.GetActionIsActive` to check whether an InputAction has any bindings which are currently active.\n* Added API `OpenXRInput.GetActionHandle` to get the action handle of an InputAction and returns 0 if not found." + }, "upmCi": { - "footprint": "0307ba2a0127885fc426b598c4b292b6e9d11a60" + "footprint": "cbe9b1c244e0898ca817801a74902e4497ea0892" }, - "documentationUrl": "https://docs.unity3d.com/Packages/com.unity.xr.openxr@1.6/manual/index.html", + "documentationUrl": "https://docs.unity3d.com/Packages/com.unity.xr.openxr@1.7/manual/index.html", "repository": { "url": "https://github.cds.internal.unity3d.com/unity/xr.sdk.openxr.git", "type": "git", - "revision": "e0ea9b26e3d27a43f8e54b4d93d561b0aa739d58" + "revision": "6a1990de9658294dd93a1f786585fe9878eac1eb" } }