Valve Index xrSuggestInteractionProfileBindings not changing grip point when specified

So like a few other people I’ve noticed that the grip point and haptic feedback for the Valve Index initiates at a very low value which is configured/set outside of Unity. This post outlines that you need to capture the xrSuggestInteractionProfileBindings and attach/create a XrInteractionProfileAnalogThresholdVALVE per binding that you would like to modify. I’ve done this and nothing changes, I’ve even created seemingly invalid data according to the documentation (onThreshold/offThreshold values) and it still passes validation so I’m wondering if there is something I’m missing with how you’re supposed to be binding it. I can confirm that it runs the code as I’ve hit it with break points and trying to bind to other bindings will result in failures as you can’t bind the thresholds to non float input types.

I’m using the OpenXRPlugin 1.12.1 and Unity 6000.0.23f1 in case that makes any difference, but it’s been like this in previous versions too.

Here’s the code that I’m using to do the modifications, obviously there are some types that I’m leaving out, but they are mostly non consequential as far as I can tell.

private XrResult xrSuggestInteractionProfileBindingsCustom(XrInstance instance, ref XrInteractionProfileSuggestedBinding suggestedBindings)
{
    var interactionProfileName = suggestedBindings.InteractionProfile;
    if (interactionProfileName == ValveIndexInteractionProfileName)
    {
        // Try and add binding modifications to change where the haptics should fire for the valve index.
        // TODO: Does not work for whatever reason - It should report that it fails if you input onThreshold lower than the offThreshold but it doesn't.
        // TODO: Maybe that is the best thing to try and investigate first
        var suggestedBindingsArray = suggestedBindings.SuggestedBindings;
        var bindingsToModify = new List<XrActionSuggestedBinding>();

        // Filter only actions that we want to modify
        foreach (var suggestedBinding in suggestedBindingsArray)
        {
            var validToModify = false;
            if (XrActionContexts.TryGetValue(suggestedBinding.action, out var actionContext))
            {
                validToModify = actionContext.ActionType is XrActionType.XR_ACTION_TYPE_FLOAT_INPUT && actionContext.ActionName == GripActionName;
            }
            if (validToModify)
            {
                bindingsToModify.Add(suggestedBinding);
            }
        }
        var actionCountToModify = bindingsToModify.Count;
        // Create an array of modifications
        XrInteractionProfileAnalogThresholdValveArray = new XrInteractionProfileAnalogThresholdVALVE[actionCountToModify];
        XrInteractionProfileAnalogThresholdValveArrayHandle = GCHandle.Alloc(XrInteractionProfileAnalogThresholdValveArray, GCHandleType.Pinned);
        // Create an array of pointers that should point at the modifications
        XrBindingModificationsArray = new IntPtr[actionCountToModify];
        XrBindingModificationsArrayHandle = GCHandle.Alloc(XrBindingModificationsArray, GCHandleType.Pinned);
        for (var i = 0; i < actionCountToModify; i++)
        {
            var bufferOffset = Marshal.SizeOf<XrInteractionProfileAnalogThresholdVALVE>() * i;
            // Set the pointer to an offset into the array. If you marshal this pointer back out it gets you the same struct so it works
            XrBindingModificationsArray[i] = XrInteractionProfileAnalogThresholdValveArrayHandle.AddrOfPinnedObject() + bufferOffset;
            var bindingToModify = bindingsToModify[i];
            XrInteractionProfileAnalogThresholdValveArray[i] = new XrInteractionProfileAnalogThresholdVALVE
            {
                type = XrStructureType.XR_TYPE_INTERACTION_PROFILE_ANALOG_THRESHOLD_VALVE,
                action = bindingToModify.action,
                binding = bindingToModify.binding,
                onThreshold = GripPressedPoint,
                offThreshold = GripPressedPoint,
                onHaptic = HapticVibrationHandle.AddrOfPinnedObject(),
                offHaptic = HapticVibrationHandle.AddrOfPinnedObject(),
                next = IntPtr.Zero // Maybe you need to set the next pointer?
            };
        }

        XrBindingModificationsKHR = new XrBindingModificationsKHR()
        {
            type = XrStructureType.XR_TYPE_BINDING_MODIFICATIONS_KHR,
            next = IntPtr.Zero,
            bindingModificationCount = (uint)actionCountToModify,
            bindingModifications = XrBindingModificationsArrayHandle.AddrOfPinnedObject()
        };
        XrBindingModificationsKHRHandle = GCHandle.Alloc(XrBindingModificationsKHR, GCHandleType.Pinned);
        suggestedBindings.next = XrBindingModificationsKHRHandle.AddrOfPinnedObject();
    }

    var result = xrSuggestInteractionProfileBindingsOverride.originalFunc(instance, ref suggestedBindings);
    if (result != XrResult.XR_SUCCESS)
    {
        Debug.LogWarning($"xrSuggestInteractionProfileBindingsCustom did not succeed with result: {result}");
    }
    return result;
}

I’ve tried marshalling the data back from the raw pointers into the structs/arrays and everything matches as I’d expect so I’m fairly sure nothing is going wrong in the C# to C/C++ memory layout. I can provide more code if it would assist, but for the most part I think most things are fairly straight forward considering I’m trying to deal with a C/C++ API using C#. Maybe someone has some insight into how I’m using the API incorrectly, I tried doing the old unsupported method of adding chained XR_TYPE_INTERACTION_PROFILE_ANALOG_THRESHOLD_VALVE structures to the next of the xrSuggestInteractionProfileBindings but that also doesn’t work.

Documentation I’m working from:
https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrInteractionProfileAnalogThresholdVALVE.html
https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrBindingModificationsKHR.html
https://registry.khronos.org/OpenXR/specs/1.0/man/html/xrSuggestInteractionProfileBindings.html
https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrInteractionProfileSuggestedBinding.html

I can also confirm that modifying other things in this way lets me change various aspects of how things work. IE if I capture the xrGetActionStateFloat and xrGetActionStateBoolean events I can modify the gripPressed to only be triggered when the float value reaches a specified value. The haptics still fire at the old point, but the gripPressed isn’t triggered until the value reaches the specified point. So it isn’t ignoring/discarding my callbacks - I just can’t make the threshold valve payload stick and it seems like a bug to me, maybe in the SteamVR runtime. If it is in the SteamVR runtime how would I go about trying to resolve that?