Skip to content

Fixed GetHapticCapabilitiesCommand always failing to execute #1776

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Assets/Tests/InputSystem/Plugins/XRTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,7 @@ public void Controls_OptimizedControls_PoseControl_IsOptimized()

// ISXB-405
[Test]
[Category("Devices")]
public void Devices_AddingUnusualDevice_ShouldntCrashTheSystem()
{
var deviceDescr =
Expand All @@ -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
1 change: 1 addition & 0 deletions Packages/com.unity.inputsystem/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ however, it has to be formatted properly to pass verification tests.
- Fixed case [ISXB-580](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-580) (UI Submit / Cancel not working with Switch Pro controller) by adding "Submit" & "Cancel" usages to the Switch Pro controller input controls.
- Fixed an issue where undoing deletion of Action Maps did not restore Actions correctly.
- Fixed case [ISXB-628](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-628) (OnIMECompositionChange does not return an empty string on accept when using Microsoft IME) by clarifying expectations and intended usage for the IME composition change event.
- 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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,104 @@
// ENABLE_VR is not defined on Game Core but the assembly is available with limited features when the XR module is enabled.
#if UNITY_INPUT_SYSTEM_ENABLE_XR && (ENABLE_VR || UNITY_GAMECORE) || PACKAGE_DOCS_GENERATION
using System;
using System.Runtime.InteropServices;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.Utilities;

namespace UnityEngine.InputSystem.XR.Haptics
{
public struct HapticCapabilities
/// <summary>
/// Describes the haptic capabilities of a specific device.
/// </summary>
public readonly struct HapticCapabilities
{
public HapticCapabilities(uint numChannels, uint frequencyHz, uint maxBufferSize)
/// <summary>
/// Initializes and returns an instance of <see cref="HapticCapabilities"/>.
/// </summary>
/// <param name="numChannels">The number of haptic channels available on this device.</param>
/// <param name="supportsImpulse">This device supports sending a haptic impulse.</param>
/// <param name="supportsBuffer">This device supports sending a haptic buffer.</param>
/// <param name="bufferFrequencyHz">The buffer frequency the device operates at in Hertz.</param>
/// <param name="bufferMaxSize">The max amount of buffer data that can be stored by the device.</param>
/// <param name="bufferOptimalSize">The optimal size of a device's buffer, taking into account frequency and latency.</param>
public HapticCapabilities(uint numChannels, bool supportsImpulse, bool supportsBuffer, uint bufferFrequencyHz, uint bufferMaxSize, uint bufferOptimalSize)
{
this.numChannels = numChannels;
this.frequencyHz = frequencyHz;
this.maxBufferSize = maxBufferSize;
this.supportsImpulse = supportsImpulse;
this.supportsBuffer = supportsBuffer;
this.bufferFrequencyHz = bufferFrequencyHz;
this.bufferMaxSize = bufferMaxSize;
this.bufferOptimalSize = bufferOptimalSize;
}

/// <summary>
/// Deprecated. Use <see cref="HapticCapabilities(uint, bool, bool, uint, uint, uint)"/> instead.
/// This constructor did not match the native haptic capabilities struct and was missing properties.
/// </summary>
/// <param name="numChannels">The number of haptic channels available on this device.</param>
/// <param name="frequencyHz">The buffer frequency the device operates at in Hertz.</param>
/// <param name="maxBufferSize">The max amount of buffer data that can be stored by the device.</param>
[Obsolete("Deprecated. Use other constructor with all properties instead.")]
public HapticCapabilities(uint numChannels, uint frequencyHz, uint maxBufferSize)
: this(numChannels, false, false, frequencyHz, maxBufferSize, 0U)
{
}

public uint numChannels { get; private set; }
public uint frequencyHz { get; private set; }
public uint maxBufferSize { get; private set; }
/// <summary>
/// The number of haptic channels available on this device.
/// </summary>
public uint numChannels { get; }

/// <summary>
/// This device supports sending a haptic impulse.
/// </summary>
/// <seealso cref="SendHapticImpulseCommand"/>
public bool supportsImpulse { get; }

/// <summary>
/// This device supports sending a haptic buffer.
/// </summary>
/// <seealso cref="SendBufferedHapticCommand"/>
public bool supportsBuffer { get; }

/// <summary>
/// The buffer frequency the device operates at in Hertz. This impacts how fast the device consumes buffered haptic data.
/// </summary>
/// <remarks>
/// This value is greater than 0 if <see cref="supportsBuffer"/> is <see langword="true"/>, and 0 otherwise.
/// </remarks>
public uint bufferFrequencyHz { get; }

/// <summary>
/// The max amount of buffer data that can be stored by the device.
/// </summary>
public uint bufferMaxSize { get; }

/// <summary>
/// The optimal size of a device's buffer, taking into account frequency and latency.
/// </summary>
public uint bufferOptimalSize { get; }

/// <summary>
/// Deprecated. Use <see cref="bufferFrequencyHz"/> instead.
/// </summary>
[Obsolete("frequencyHz has been deprecated. Use bufferFrequencyHz instead. (UnityUpgradable) -> bufferFrequencyHz")]
public uint frequencyHz => bufferFrequencyHz;

/// <summary>
/// Deprecated. Use <see cref="bufferMaxSize"/> instead.
/// </summary>
[Obsolete("maxBufferSize has been deprecated. Use bufferMaxSize instead. (UnityUpgradable) -> bufferMaxSize")]
public uint maxBufferSize => bufferMaxSize;
}

[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;

Expand All @@ -35,13 +108,42 @@ public struct GetHapticCapabilitiesCommand : IInputDeviceCommandInfo
[FieldOffset(InputDeviceCommand.kBaseCommandSize)]
public uint numChannels;

[FieldOffset(InputDeviceCommand.kBaseCommandSize + sizeof(uint))]
public uint frequencyHz;
[FieldOffset(InputDeviceCommand.kBaseCommandSize + 4)]
public bool supportsImpulse;

[FieldOffset(InputDeviceCommand.kBaseCommandSize + 5)]
public bool supportsBuffer;

[FieldOffset(InputDeviceCommand.kBaseCommandSize + 8)]
public uint bufferFrequencyHz;

[FieldOffset(InputDeviceCommand.kBaseCommandSize + 12)]
public uint bufferMaxSize;

[FieldOffset(InputDeviceCommand.kBaseCommandSize + 16)]
public uint bufferOptimalSize;

[FieldOffset(InputDeviceCommand.kBaseCommandSize + (sizeof(uint) * 2))]
public uint maxBufferSize;
/// <summary>
/// Deprecated. Use <see cref="bufferFrequencyHz"/> instead.
/// </summary>
[Obsolete("frequencyHz has been deprecated. Use bufferFrequencyHz instead.")]
public uint frequencyHz
{
get => bufferFrequencyHz;
set => bufferFrequencyHz = value;
}

/// <summary>
/// Deprecated. Use <see cref="bufferMaxSize"/> instead.
/// </summary>
[Obsolete("maxBufferSize has been deprecated. Use bufferMaxSize instead.")]
public uint maxBufferSize
{
get => bufferMaxSize;
set => bufferMaxSize = value;
}

public HapticCapabilities capabilities => new HapticCapabilities(numChannels, frequencyHz, maxBufferSize);
public HapticCapabilities capabilities => new HapticCapabilities(numChannels, supportsImpulse, supportsBuffer, bufferFrequencyHz, bufferMaxSize, bufferOptimalSize);

public static GetHapticCapabilitiesCommand Create()
{
Expand Down