Skip to content
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

Calls to DirectInput EnumDevices sometimes very slow! #1

Closed
MrTimcakes opened this issue Aug 26, 2021 · 4 comments
Closed

Calls to DirectInput EnumDevices sometimes very slow! #1

MrTimcakes opened this issue Aug 26, 2021 · 4 comments
Labels
bug Something isn't working DirectInput Bug

Comments

@MrTimcakes
Copy link
Owner

Typically calls to EnumerateDevices which calls _DirectInput->EnumDevices take about 85ms on average on my system.
Sometimes, this is orders of magnitude larger taking in the range of minutes instead of milliseconds.
This is due to device drivers on the system, in my case a Corsair K70 Keyboard, hanging and not replying to Window's request to query attached devices.

The issue has already been discussed on StackOverflow: https://stackoverflow.com/q/10967795

@MrTimcakes MrTimcakes added the bug Something isn't working label Aug 26, 2021
@MrTimcakes
Copy link
Owner Author

Perhaps the DLL should start a watchdog and timeout the EnumerateDevices function if it takes more than ~300ms?
Hopefully, as each enumerated device adds to _DeviceInstances in its own callback instance, if we cancel EnumDevices early _DeviceInstances will still be populated with the correct information. 🤞 other devices are processed before we hang. Otherwise, the timeout will need to be in the callback, which will be more difficult to implement.

This is a very difficult issue to debug as it only happens occasionally.
Luckily, when it does happen and EnumerateDevices hangs, I can slide the Hz slider on my K70 and EnumerateDevices immediately returns.

@MrTimcakes
Copy link
Owner Author

_EnumDevicesCallback Statistics from 54 samples:

min = 4900ns
max = 417400ns
Range R = 412500ns
Count n = 54
sum = 2455500ns
Mean x̄ = 45472.222ns
Median x˜ = 44050ns
mode = 5300, 45200
Standard Deviation = 75339.76ns
Variance s2 = 5676079400

@MrTimcakes
Copy link
Owner Author

Upon further inspection, the delay doesn't occur within _EnumDevicesCallback
Tested when issue is occurring

_EnumDevicesCallback MrT USB Handbreak: 16000ns
_EnumDevicesCallback FANATEC CSL Elite Wheel Base PlayStation: 45300ns
_EnumDevicesCallback FANATEC CSL Elite Wheel Base PlayStation: 43400ns

EnumDevices: 40192473100ns

@MrTimcakes
Copy link
Owner Author

Made an attempt to exit EnumDevices early but _DeviceInstances isn't populated 😭

#include <chrono>
#include <type_traits>
#include <future>
#include <thread>
#include <condition_variable>
DeviceInfo* EnumerateDevices(int& deviceCount) {
  DEBUGDATA.push_back(L"Started Spoofer");
  try {
    std::mutex m;
    std::condition_variable cv;
    DeviceInfo* retValue;

    std::thread t([&cv, &retValue, &deviceCount]()
    {
      retValue = EnumerateDevicesReal(deviceCount);
      cv.notify_one();
    });

    t.detach();

    {
      std::unique_lock<std::mutex> l(m);
      if (cv.wait_for(l, std::chrono::milliseconds(500)) == std::cv_status::timeout)
        throw std::runtime_error("Timeout");
    }

    return retValue;
  } catch (std::runtime_error& e) {
    DEBUGDATA.push_back(L"EA! " + _DeviceInstances.size());
    DEBUGDATA.push_back(std::to_wstring(_DeviceInstances.size()));
    return &_DeviceInstances[0];
  }
}

// Return a vector of all attached devices
//DeviceInfo* EnumerateDevices(/*[out]*/ int& deviceCount) {
//  return run_with_timeout(EnumerateDevicesReal, std::chrono::milliseconds(500), deviceCount);
//}

DeviceInfo* EnumerateDevicesReal(/*[out]*/ int& deviceCount) {
  TimeVar t1 = timeNow();
  HRESULT hr = E_FAIL;
  if (_DirectInput == NULL) { return NULL; } // If DI not ready, return nothing
  _DeviceInstances.clear();                  // Clear devices

  // First fetch all devices
  hr = _DirectInput->EnumDevices(    // Invoke device enumeration to the _EnumDevicesCallback callback
    DI8DEVCLASS_GAMECTRL,                    // List devices of type GameController
    _EnumDevicesCallback,                    // Callback executed for each device found
    NULL,                                    // Passed to callback as optional arg
    DIEDFL_ATTACHEDONLY //| DIEDFL_FORCEFEEDBACK
  );

  // Next update FFB devices (Important this happens after as it modifies existing entries)
  hr = _DirectInput->EnumDevices(    // Invoke device enumeration to the _EnumDevicesCallback callback
    DI8DEVCLASS_GAMECTRL,                    // List devices of type GameController
    _EnumDevicesCallbackFFB,                 // Callback executed for each device found
    NULL,                                    // Passed to callback as optional arg
    DIEDFL_ATTACHEDONLY | DIEDFL_FORCEFEEDBACK
  );

  DEBUGDATA.push_back(L"EnumDevices:" + std::to_wstring(duration(timeNow() - t1)));

  if (_DeviceInstances.size() > 0) {
    deviceCount = (int)_DeviceInstances.size();
    return &_DeviceInstances[0]; // Return 1st element, structure size & deviceCount are used to find next elements
  } else {
    deviceCount = 0;
  }
  return NULL;
}

@MrTimcakes MrTimcakes closed this as not planned Won't fix, can't repro, duplicate, stale Dec 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working DirectInput Bug
Projects
None yet
Development

No branches or pull requests

1 participant