-
Notifications
You must be signed in to change notification settings - Fork 4
Fix low polling rate precision issues in WinInput.cs #5
Description
thank gemini help to translate 👍 , but more more more error 👎 , and add more is not my think content 👎👎👎
// in 1 hour, deepseek 👍👍👍
I've put all the descriptions in. If you don't understand, maybe you can take a look inside.
Hi there,
I discovered an issue in WinInput.cs at line 51 where the Thread.Sleep() call causes polling rate inaccuracies when the PollingRate is set below 500.
Problem
Thread.Sleep() on Windows has a default resolution of approximately 15.625ms (64Hz). When PollingRate is low, the actual execution frequency is limited to around 64 times per second, preventing the intended polling intervals from being achieved.
Proposed Solutions
I've identified two potential approaches to resolve this issue:
Option 1: System-Wide Timer Resolution (Using winmm.dll)
This approach increases system timer resolution temporarily for more precise sleep intervals.
public static class WinInput {
[DllImport("winmm.dll")]
private static extern uint timeBeginPeriod(int period);
[DllImport("winmm.dll")]
private static extern uint timeEndPeriod(int period);
public static void StartPolling(PollingRate rate) {
// At line 51:
timeBeginPeriod(2); // Set precision to 2ms
Thread.Sleep(1);
timeEndPeriod(2); // Restore default precision
}
}Pros:
- Simple implementation
- Works on all Windows versions
Cons:
- Affects entire system timer resolution
- May increase power consumption
Option 2: High-Resolution Waitable Timer (Using kernel32.dll)
This approach uses Windows' high-resolution waitable timers introduced in Windows 10 (1803/RS4).
public static class HighPrecisionTimer
{
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateWaitableTimerEx(
IntPtr lpTimerAttributes,
string lpTimerName,
uint dwFlags,
uint dwDesiredAccess);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetWaitableTimerEx(
IntPtr hTimer,
[In] ref long lpDueTime,
int lPeriod,
IntPtr pfnCompletionRoutine,
IntPtr lpArgToCompletionRoutine,
IntPtr WakeContext,
uint lTolerableDelay);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
private const uint SYNCHRONIZE = 0x00100000;
private const uint TIMER_MODIFY_STATE = 0x0002;
private const uint CREATE_WAITABLE_TIMER_HIGH_RESOLUTION = 0x00000002;
private const uint INFINITE = 0xFFFFFFFF;
public static void WaitSleep(long tick)
{
IntPtr hTimer = CreateWaitableTimerEx(
IntPtr.Zero,
null,
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION,
SYNCHRONIZE | TIMER_MODIFY_STATE);
if (hTimer != IntPtr.Zero)
{
long dueTime = -tick;
if (SetWaitableTimerEx(hTimer, ref dueTime, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, 0))
{
WaitForSingleObject(hTimer, INFINITE);
}
CloseHandle(hTimer);
}
}
}
public static class WinInput {
[DllImport("winmm.dll")]
private static extern uint timeBeginPeriod(int period);
[DllImport("winmm.dll")]
private static extern uint timeEndPeriod(int period);
public static void StartPolling(PollingRate rate) {
// At line 51:
HighPrecisionTimer.WaitSleep(1 * 10000000); // 10,000,000 = 1 second in file time units
}
}Pros:
- Affects only the current handle/process
- More precise and efficient
Cons:
- Requires Windows 10 version 1803 (RS4) or later
- Behavior may vary depending on system power management settings
Additional Suggestion
I also noticed that at line 53, there's a busy-wait loop without Thread.Yield(). Adding Thread.Yield() could help reduce CPU usage:
Thread.Yield(); // Add this in the busy-wait loopThis may help lower CPU consumption during polling (though I'm not 100% certain of the impact).
Would appreciate any feedback or suggestions on which approach aligns best with the project's goals.
Thank you for your time and for maintaining this great project!