Sampling XR controller positions and linear velocity independent of frame rate and physics time step

Hi, I’m working on a Unity v2021.3 LTS VR game with Quest 2. I would like to sample the Quest 2 controller positions and linear velocity independent of frame rate / physics time step. Ideally I want to sample the Quest 2 controllers every 2 milliseconds. Right now they are getting sampled every 13.8ms via monobehavior.Update calls.

I have determined that Update, FixedUpdate, Corountines, and WaitForSeconds are not a valid approach because all of these approaches are tied to the frame rate / physics time step.

I’m struggling to find a working solution for this problem. I’m looking into using the new input system and setting the pollingFrequency to be higher. I’m not sure if the new input system will work for my use case?

Does anyone have any insight to my problem?

This would be great. Where you able to make any progress with this?

I am currently trying to get more input values using the “InputActionTrace”.
With InputSystem.pollingFrequency = 1000;
With the Application.targetFrameRate = 1;
using the keyboard I can get multiple actions from the keyboard in 1 frame to be logged. But for OpenXR + SteamVR + Valve index + in editor, the controller velocity the trace.count is always just 1 :face_with_spiral_eyes:.

I’ve now also tried the “InputEventTrace”, but it seems to also just produce 1 value per frame.

From the input debugger it looks like all openXR data is jammed into 1 data struct, in this case since you can only provide a Device to the constructor of the trace, this might be expected.
Also the events in the input debugger also seem to be framerate dependent, its not like these are running at normal frequency while target framerate is 1.

Edit: Adding to this, I also tried running the input in the fixed update, so that it could be sampled multiple times per frame. But this also did not do anything. (not with the input system update mode set to fixedupdate, or with manually calling InputSystem.Update(); in the fixedupdate().
It does not matter much, since the fixed update steps all run at the start of the frame and then idle until the targetframerate.

Filed a bug report for this (IN-43099).

Here is the code I used to show keyboard input being framerate independent, while openXR input is not. I don’t have the skill level to dig any deeper into the code. I also was not expecting input to be an issue, since valve already stopped supporting their unity plugin in favor of openXR years ago.

Keyboard working with framerate independence (inspired by : How the Unity’s new Input System liberates the input detection from frame-rate | C. Plug’s Blog) :

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Utilities;

public class KeyboardFramerateIndependantInputWorks : MonoBehaviour
{
 
    private InputActionTrace _trace;
    public InputActionReference theActionRef;
    private int frame;

    private void OnEnable()
    {
        theActionRef.action.Enable();
        Application.targetFrameRate = 1;
    }

    private void OnDisable()
    {
        theActionRef.action.Disable();
    }


    private void Awake()
    {
        _trace = new InputActionTrace();
        _trace.SubscribeTo(theActionRef.action);
        InputSystem.pollingFrequency = 500;
    }

    private void Update()
    {
        Debug.Log($"Frame : {frame}");
        frame++;

        InputSystem.Update();

        Debug.Log(_trace.count);

        foreach (var action in _trace)
        {      
             var diff = Time.realtimeSinceStartupAsDouble - action.time;
             Debug.Log($"This action has occurred {diff.ToString("F3")} seconds before this frame.");       
        }

        _trace.Clear();
    }

    private void OnDestroy()
    {
        _trace.UnsubscribeFromAll();
        _trace.Dispose();
    }
}

VR controller not being framerate independant :

using Unity.Mathematics;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Utilities;

public class VrControllerFramerateIndependantInputFails : MonoBehaviour
{
    private InputActionTrace _trace;
    public InputActionReference theActionRef;
    private int frame;

    private void OnEnable()
    {
        theActionRef.action.Enable();  
        Application.targetFrameRate = 1;
    }

    private void OnDisable()
    {
        theActionRef.action.Disable();
    }


    private void Awake()
    {
        _trace = new InputActionTrace();
        _trace.SubscribeTo(theActionRef.action);
        InputSystem.pollingFrequency = 500;
    }

    private void Update()
    {
        Debug.Log($"Frame : {frame}");
        frame++;

        InputSystem.Update();

        Debug.Log(_trace.count);

        foreach (var action in _trace)
        {
            Debug.Log($"veclocity in action = {action.ReadValue<float3>()}"); // not needed for the bug, but a sanity check to see its not returning strange values.
         
             var diff = Time.realtimeSinceStartupAsDouble - action.time;
             Debug.Log($"This action has occurred {diff.ToString("F4")} seconds before this frame.");       
        }

        _trace.Clear();
    }

    private void OnDestroy()
    {
        _trace.UnsubscribeFromAll();
        _trace.Dispose();
    }
}

Input system low level code to also not be framerate independant for VR :

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.LowLevel;

public class VrControllerViaLowLevelAlsoFails : MonoBehaviour
{
    public InputActionReference theActionRef;
    private InputEventTrace _trace;
    private int frame;

    private void OnEnable()
    {   
        Application.targetFrameRate = 1;
    }

    private void OnDisable()
    {
        theActionRef.action.Disable();
    }


    private void Awake()
    {    
        _trace = new InputEventTrace();
        _trace.Enable();

        theActionRef.action.Enable();   
        InputSystem.pollingFrequency = 500;
    }

    private void Update()
    {
    
        if (theActionRef.action.activeControl == null)
        {
            Debug.Log("no active device");
            return;
        }

        if (_trace.deviceId == InputDevice.InvalidDeviceId)
        {
            var device = theActionRef.action.activeControl.device;
            _trace.deviceId = device.deviceId;
        }

        Debug.Log($"Frame : {frame}");
        frame++;

        InputSystem.Update();

        Debug.Log($"eventCount : {_trace.eventCount}");

        foreach (var inputevent in _trace)
        {
            Debug.Log(inputevent.ToString());

            var diff = Time.realtimeSinceStartupAsDouble - inputevent.time;
            Debug.Log($"This event has occurred {diff.ToString("F4")} seconds before this frame.");
        
        }

        _trace.Clear();
    }

    private void OnDestroy()
    {
    
        _trace.Disable();
        _trace.Dispose();
    }
}

Hey, I have not made progress on this issue. I might be working on the problem again in a few weeks. If I ever find anything out I will reply to this thread and tag you.

1 Like

Did you find solution? I’m dealing with exactly same issue with you. I want to get Controller velocity data every 2ms too, regardless of frame. If you solved this, could you please share your method?