From 317d7cc2b90c6cf06e15e0d3faa9397a5b0e13ab Mon Sep 17 00:00:00 2001 From: Chris Massie <67029035+chris-massie@users.noreply.github.com> Date: Wed, 8 Nov 2023 06:54:55 -0800 Subject: [PATCH] FIX: GetHapticCapabilitiesCommand always failing to execute (#1776) * Fixed GetHapticCapabilitiesCommand always failing to execute and added missing haptic capabilities properties * Added setter to make migration better if the user was setting the old field * Fixed API_MinorVersionsHaveNoBreakingChanges by reverting renames The names won't match the UnityEngine.XR and IUnityXRInput.h names, but this is needed to avoid a breaking change. Renamed the optimal size field to more closely match the naming of maxBufferSize for consistency within Input System. * Reverted redundant explicit field initialization since it was causing API_MinorVersionsHaveNoBreakingChanges to fail. The value remains the same. The test could be updated in the future to recognize the value is the same, but for now it's easier to just revert the change. * Added xml-doc to public fields and methods --------- Co-authored-by: James McGill --- Assets/Tests/InputSystem/Plugins/XRTests.cs | 15 +++ Packages/com.unity.inputsystem/CHANGELOG.md | 2 + .../Actions/Composites/Vector2Composite.cs | 2 +- .../Commands/IInputDeviceCommandInfo.cs | 3 + .../Haptics/GetHapticCapabilitiesCommand.cs | 120 ++++++++++++++++-- 5 files changed, 133 insertions(+), 9 deletions(-) diff --git a/Assets/Tests/InputSystem/Plugins/XRTests.cs b/Assets/Tests/InputSystem/Plugins/XRTests.cs index 66fc47062e..65bc96c166 100644 --- a/Assets/Tests/InputSystem/Plugins/XRTests.cs +++ b/Assets/Tests/InputSystem/Plugins/XRTests.cs @@ -1140,6 +1140,7 @@ public void Controls_OptimizedControls_PoseControl_IsOptimized() // ISXB-405 [Test] + [Category("Devices")] public void Devices_AddingUnusualDevice_ShouldntCrashTheSystem() { var deviceDescr = @@ -1153,5 +1154,19 @@ public void Devices_AddingUnusualDevice_ShouldntCrashTheSystem() Assert.That(device, Is.Not.Null); } + + [Test] + [Category("Commands")] + public void Commands_GetHapticCapabilitiesCommand_UsesCorrectPayloadSize() + { + unsafe + { + // Check that the payload of the command matches the low-level struct defined in IUnityXRInput.h (UnityXRHapticCapabilities) + // and used in XRInputSubsystem by checking the size. The sizes are required to match for the event to be + // sent to the device. + Assert.That(sizeof(UnityEngine.InputSystem.XR.Haptics.HapticCapabilities), Is.EqualTo(sizeof(UnityEngine.XR.HapticCapabilities))); + Assert.That(sizeof(UnityEngine.InputSystem.XR.Haptics.GetHapticCapabilitiesCommand) - InputDeviceCommand.BaseCommandSize, Is.EqualTo(sizeof(UnityEngine.XR.HapticCapabilities))); + } + } } #endif diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md index 73c085d9d1..fc8bbf4d47 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -46,6 +46,8 @@ however, it has to be formatted properly to pass verification tests. - Fixed issue of visual elements being null during editing project-wide actions in project settings which prompted console errors. - Fixed case ISX-1436 (UI TK Input Action Asset Editor - Error deleting Bindings with DeleteKey on Windows). - Fixed issue with UI Toolkit based Input Action Editor not restoring it's selected items after Domain Reload. +- Fixed the [`GetHapticCapabilitiesCommand`](xref:UnityEngine.InputSystem.XR.Haptics.GetHapticCapabilitiesCommand) always failing to execute due to a mismatch in the size in bytes of the payload and the size expected by XR devices. Changed [`HapticCapabilities`](xref:UnityEngine.InputSystem.XR.Haptics.HapticCapabilities) to include all properties returned by the XR input subsystem. This makes Input System match the functionality provided by the [XR](https://docs.unity3d.com/Manual/com.unity.modules.xr.html) module's [`InputDevice.TryGetHapticCapabilities`](https://docs.unity3d.com/ScriptReference/XR.InputDevice.TryGetHapticCapabilities.html) and [`HapticCapabilities`](https://docs.unity3d.com/ScriptReference/XR.HapticCapabilities.html). + ## [1.8.0-pre.1] - 2023-09-04 diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/Vector2Composite.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/Vector2Composite.cs index ae09cb1057..c5f001587b 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/Vector2Composite.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/Vector2Composite.cs @@ -124,7 +124,7 @@ public class Vector2Composite : InputBindingComposite /// /// /// - public Mode mode = Mode.DigitalNormalized; + public Mode mode; /// public override Vector2 ReadValue(ref InputBindingCompositeContext context) diff --git a/Packages/com.unity.inputsystem/InputSystem/Devices/Commands/IInputDeviceCommandInfo.cs b/Packages/com.unity.inputsystem/InputSystem/Devices/Commands/IInputDeviceCommandInfo.cs index 11128f29a3..534d1ab29b 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Devices/Commands/IInputDeviceCommandInfo.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Devices/Commands/IInputDeviceCommandInfo.cs @@ -7,6 +7,9 @@ namespace UnityEngine.InputSystem.LowLevel /// public interface IInputDeviceCommandInfo { + /// + /// The data format identifier of the device command as a code. + /// FourCC typeStatic { get; } } } diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/XR/Haptics/GetHapticCapabilitiesCommand.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/XR/Haptics/GetHapticCapabilitiesCommand.cs index 081aa58c7d..670ccd158f 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/XR/Haptics/GetHapticCapabilitiesCommand.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/XR/Haptics/GetHapticCapabilitiesCommand.cs @@ -6,43 +6,147 @@ namespace UnityEngine.InputSystem.XR.Haptics { + /// + /// Describes the haptic capabilities of a specific device. + /// public struct HapticCapabilities { - public HapticCapabilities(uint numChannels, uint frequencyHz, uint maxBufferSize) + /// + /// Initializes and returns an instance of . + /// + /// The number of haptic channels available on this device. + /// This device supports sending a haptic impulse. + /// This device supports sending a haptic buffer. + /// The buffer frequency the device operates at in Hertz. + /// The max amount of buffer data that can be stored by the device. + /// The optimal size of a device's buffer, taking into account frequency and latency. + public HapticCapabilities(uint numChannels, bool supportsImpulse, bool supportsBuffer, uint frequencyHz, uint maxBufferSize, uint optimalBufferSize) { this.numChannels = numChannels; + this.supportsImpulse = supportsImpulse; + this.supportsBuffer = supportsBuffer; this.frequencyHz = frequencyHz; this.maxBufferSize = maxBufferSize; + this.optimalBufferSize = optimalBufferSize; } - public uint numChannels { get; private set; } - public uint frequencyHz { get; private set; } - public uint maxBufferSize { get; private set; } + /// + /// Deprecated. Use instead. + /// This constructor did not match the native haptic capabilities struct and was missing properties. + /// + /// The number of haptic channels available on this device. + /// The buffer frequency the device operates at in Hertz. + /// The max amount of buffer data that can be stored by the device. + public HapticCapabilities(uint numChannels, uint frequencyHz, uint maxBufferSize) + : this(numChannels, false, false, frequencyHz, maxBufferSize, 0U) + { + } + + /// + /// The number of haptic channels available on this device. + /// + public uint numChannels { get; } + + /// + /// This device supports sending a haptic impulse. + /// + /// + public bool supportsImpulse { get; } + + /// + /// This device supports sending a haptic buffer. + /// + /// + public bool supportsBuffer { get; } + + /// + /// The buffer frequency the device operates at in Hertz. This impacts how fast the device consumes buffered haptic data. + /// + /// + /// This value is greater than 0 if is , and 0 otherwise. + /// + public uint frequencyHz { get; } + + /// + /// The max amount of buffer data that can be stored by the device. + /// + public uint maxBufferSize { get; } + + /// + /// The optimal size of a device's buffer, taking into account frequency and latency. + /// + public uint optimalBufferSize { get; } } + /// + /// Input device command struct for retrieving the haptic capabilities of a device. + /// [StructLayout(LayoutKind.Explicit, Size = kSize)] public struct GetHapticCapabilitiesCommand : IInputDeviceCommandInfo { static FourCC Type => new FourCC('X', 'H', 'C', '0'); - const int kSize = InputDeviceCommand.kBaseCommandSize + sizeof(uint) * 3; + // 20 bytes of data from uint(4) + bool(1) + bool(1) + padding + uint(4) + uint(4) + uint(4) + const int kSize = InputDeviceCommand.kBaseCommandSize + 20; + /// public FourCC typeStatic => Type; [FieldOffset(0)] InputDeviceCommand baseCommand; + /// + /// The number of haptic channels available on this device. + /// [FieldOffset(InputDeviceCommand.kBaseCommandSize)] public uint numChannels; - [FieldOffset(InputDeviceCommand.kBaseCommandSize + sizeof(uint))] + /// + /// This device supports sending a haptic impulse. + /// + /// + [FieldOffset(InputDeviceCommand.kBaseCommandSize + 4)] + public bool supportsImpulse; + + /// + /// This device supports sending a haptic buffer. + /// + /// + [FieldOffset(InputDeviceCommand.kBaseCommandSize + 5)] + public bool supportsBuffer; + + /// + /// The buffer frequency the device operates at in Hertz. This impacts how fast the device consumes buffered haptic data. + /// + /// + /// This value is greater than 0 if is , and 0 otherwise. + /// + [FieldOffset(InputDeviceCommand.kBaseCommandSize + 8)] public uint frequencyHz; - [FieldOffset(InputDeviceCommand.kBaseCommandSize + (sizeof(uint) * 2))] + /// + /// The max amount of buffer data that can be stored by the device. + /// + [FieldOffset(InputDeviceCommand.kBaseCommandSize + 12)] public uint maxBufferSize; - public HapticCapabilities capabilities => new HapticCapabilities(numChannels, frequencyHz, maxBufferSize); + /// + /// The optimal size of a device's buffer, taking into account frequency and latency. + /// + [FieldOffset(InputDeviceCommand.kBaseCommandSize + 16)] + public uint optimalBufferSize; + + /// + /// The haptic capabilities of the device, populated after this command is executed. + /// + public HapticCapabilities capabilities => new HapticCapabilities(numChannels, supportsImpulse, supportsBuffer, frequencyHz, maxBufferSize, optimalBufferSize); + /// + /// Creates and returns a new initialized input device command struct for retrieving + /// the haptic capabilities of a device when executed. + /// + /// Returns a new command struct with the data header initialized, making it ready to execute. + /// public static GetHapticCapabilitiesCommand Create() { return new GetHapticCapabilitiesCommand