Browse Source

Make changing raw input mode on Windows a very fast operation

Previously we would spin up and shut down a thread every time we changed raw input mode, which can take several ms. Now we'll just put the raw input thread to sleep when disabling raw input and wake it up when enabling raw input.
Sam Lantinga 2 days ago
parent
commit
43e90c7b1c

+ 2 - 2
src/video/windows/SDL_windowsevents.c

@@ -786,7 +786,7 @@ static void WIN_HandleRawKeyboardInput(Uint64 timestamp, SDL_VideoData *data, HA
     SDL_SendKeyboardKey(timestamp, keyboardID, rawcode, code, down);
     SDL_SendKeyboardKey(timestamp, keyboardID, rawcode, code, down);
 }
 }
 
 
-void WIN_PollRawInput(SDL_VideoDevice *_this, Uint64 poll_start)
+void WIN_PollRawInput(SDL_VideoDevice *_this, Uint64 poll_start, bool process_input)
 {
 {
     SDL_VideoData *data = _this->internal;
     SDL_VideoData *data = _this->internal;
     UINT size, i, count, total = 0;
     UINT size, i, count, total = 0;
@@ -832,7 +832,7 @@ void WIN_PollRawInput(SDL_VideoDevice *_this, Uint64 poll_start)
         }
         }
     }
     }
 
 
-    if (total > 0) {
+    if (total > 0 && process_input) {
         Uint64 delta = poll_finish - poll_start;
         Uint64 delta = poll_finish - poll_start;
         UINT mouse_total = 0;
         UINT mouse_total = 0;
         for (i = 0, input = (RAWINPUT *)data->rawinput; i < total; ++i, input = NEXTRAWINPUTBLOCK(input)) {
         for (i = 0, input = (RAWINPUT *)data->rawinput; i < total; ++i, input = NEXTRAWINPUTBLOCK(input)) {

+ 1 - 1
src/video/windows/SDL_windowsevents.h

@@ -29,7 +29,7 @@ extern HINSTANCE SDL_Instance;
 
 
 extern LRESULT CALLBACK WIN_KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam);
 extern LRESULT CALLBACK WIN_KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam);
 extern LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
 extern LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
-extern void WIN_PollRawInput(SDL_VideoDevice *_this, Uint64 poll_start);
+extern void WIN_PollRawInput(SDL_VideoDevice *_this, Uint64 poll_start, bool process_input);
 extern void WIN_PumpEvents(SDL_VideoDevice *_this);
 extern void WIN_PumpEvents(SDL_VideoDevice *_this);
 extern void WIN_PumpEventsForHWND(SDL_VideoDevice *_this, HWND hwnd);
 extern void WIN_PumpEventsForHWND(SDL_VideoDevice *_this, HWND hwnd);
 extern void WIN_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window);
 extern void WIN_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window);

+ 63 - 28
src/video/windows/SDL_windowsrawinput.c

@@ -40,13 +40,15 @@
 typedef struct
 typedef struct
 {
 {
     bool done;
     bool done;
+    bool paused;
     Uint32 flags;
     Uint32 flags;
     HANDLE ready_event;
     HANDLE ready_event;
-    HANDLE done_event;
+    HANDLE signal_event;
     HANDLE thread;
     HANDLE thread;
 } RawInputThreadData;
 } RawInputThreadData;
 
 
 static RawInputThreadData thread_data = {
 static RawInputThreadData thread_data = {
+    false,
     false,
     false,
     0,
     0,
     INVALID_HANDLE_VALUE,
     INVALID_HANDLE_VALUE,
@@ -104,22 +106,34 @@ static DWORD WINAPI WIN_RawInputThread(LPVOID param)
     // Tell the parent we're ready to go!
     // Tell the parent we're ready to go!
     SetEvent(data->ready_event);
     SetEvent(data->ready_event);
 
 
-    Uint64 idle_begin = SDL_GetTicksNS();
-    while (!data->done &&
-            // The high-order word of GetQueueStatus() will let us know if there's currently raw input to be processed.
-            // If there isn't, then we'll wait for new data to arrive with MsgWaitForMultipleObjects().
-           ((HIWORD(GetQueueStatus(QS_RAWINPUT)) & QS_RAWINPUT) ||
-            (MsgWaitForMultipleObjects(1, &data->done_event, FALSE, INFINITE, QS_RAWINPUT) == WAIT_OBJECT_0 + 1))) {
+    while (!data->done) {
+        Uint64 idle_begin = SDL_GetTicksNS();
+        while (!data->done && !data->paused &&
+               // The high-order word of GetQueueStatus() will let us know if there's currently raw input to be processed.
+               // If there isn't, then we'll wait for new data to arrive with MsgWaitForMultipleObjects().
+               ((HIWORD(GetQueueStatus(QS_RAWINPUT)) & QS_RAWINPUT) ||
+                (MsgWaitForMultipleObjects(1, &data->signal_event, FALSE, INFINITE, QS_RAWINPUT) == WAIT_OBJECT_0 + 1))) {
+
+            Uint64 idle_end = SDL_GetTicksNS();
+            Uint64 idle_time = idle_end - idle_begin;
+            Uint64 usb_8khz_interval = SDL_US_TO_NS(125);
+            Uint64 poll_start = idle_time < usb_8khz_interval ? _this->internal->last_rawinput_poll : idle_end;
 
 
-        Uint64 idle_end = SDL_GetTicksNS();
-        Uint64 idle_time = idle_end - idle_begin;
-        Uint64 usb_8khz_interval = SDL_US_TO_NS(125);
-        Uint64 poll_start = idle_time < usb_8khz_interval ? _this->internal->last_rawinput_poll : idle_end;
+            WIN_PollRawInput(_this, poll_start, true);
 
 
-        WIN_PollRawInput(_this, poll_start);
+            // Reset idle_begin for the next go-around
+            idle_begin = SDL_GetTicksNS();
+        }
 
 
-        // Reset idle_begin for the next go-around
-        idle_begin = SDL_GetTicksNS();
+        if (data->paused) {
+            // Wait for the resume signal
+            while (data->paused) {
+                WaitForSingleObject(data->signal_event, INFINITE);
+            }
+
+            // Flush raw input queued while paused
+            WIN_PollRawInput(_this, SDL_GetTicksNS(), false);
+        }
     }
     }
 
 
     if (_this->internal->raw_input_fake_pen_id) {
     if (_this->internal->raw_input_fake_pen_id) {
@@ -141,8 +155,9 @@ static DWORD WINAPI WIN_RawInputThread(LPVOID param)
 static void CleanupRawInputThreadData(RawInputThreadData *data)
 static void CleanupRawInputThreadData(RawInputThreadData *data)
 {
 {
     if (data->thread != INVALID_HANDLE_VALUE) {
     if (data->thread != INVALID_HANDLE_VALUE) {
+        data->paused = false;
         data->done = true;
         data->done = true;
-        SetEvent(data->done_event);
+        SetEvent(data->signal_event);
         WaitForSingleObject(data->thread, 3000);
         WaitForSingleObject(data->thread, 3000);
         CloseHandle(data->thread);
         CloseHandle(data->thread);
         data->thread = INVALID_HANDLE_VALUE;
         data->thread = INVALID_HANDLE_VALUE;
@@ -153,16 +168,26 @@ static void CleanupRawInputThreadData(RawInputThreadData *data)
         data->ready_event = INVALID_HANDLE_VALUE;
         data->ready_event = INVALID_HANDLE_VALUE;
     }
     }
 
 
-    if (data->done_event != INVALID_HANDLE_VALUE) {
-        CloseHandle(data->done_event);
-        data->done_event = INVALID_HANDLE_VALUE;
+    if (data->signal_event != INVALID_HANDLE_VALUE) {
+        CloseHandle(data->signal_event);
+        data->signal_event = INVALID_HANDLE_VALUE;
     }
     }
 }
 }
 
 
-static bool WIN_SetRawInputEnabled(SDL_VideoDevice *_this, Uint32 flags)
+bool WIN_SetRawInputEnabled(SDL_VideoDevice *_this, Uint32 flags, bool force)
 {
 {
     bool result = false;
     bool result = false;
 
 
+    if (thread_data.thread != INVALID_HANDLE_VALUE && !force) {
+        if (flags) {
+            thread_data.paused = false;
+        } else {
+            thread_data.paused = true;
+        }
+        SetEvent(thread_data.signal_event);
+        return true;
+    }
+
     CleanupRawInputThreadData(&thread_data);
     CleanupRawInputThreadData(&thread_data);
 
 
     if (flags) {
     if (flags) {
@@ -176,8 +201,8 @@ static bool WIN_SetRawInputEnabled(SDL_VideoDevice *_this, Uint32 flags)
         }
         }
 
 
         thread_data.done = false;
         thread_data.done = false;
-        thread_data.done_event = CreateEvent(NULL, FALSE, FALSE, NULL);
-        if (thread_data.done_event == INVALID_HANDLE_VALUE) {
+        thread_data.signal_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+        if (thread_data.signal_event == INVALID_HANDLE_VALUE) {
             WIN_SetError("CreateEvent");
             WIN_SetError("CreateEvent");
             goto done;
             goto done;
         }
         }
@@ -216,15 +241,20 @@ static bool WIN_UpdateRawInputEnabled(SDL_VideoDevice *_this)
     }
     }
     if (data->raw_keyboard_enabled) {
     if (data->raw_keyboard_enabled) {
         flags |= ENABLE_RAW_KEYBOARD_INPUT;
         flags |= ENABLE_RAW_KEYBOARD_INPUT;
-        if (data->raw_keyboard_flag_nohotkeys) {
-            flags |= RAW_KEYBOARD_FLAG_NOHOTKEYS;
-        }
-        if (data->raw_keyboard_flag_inputsink) {
-            flags |= RAW_KEYBOARD_FLAG_INPUTSINK;
-        }
+    }
+    if (data->raw_keyboard_flag_nohotkeys) {
+        flags |= RAW_KEYBOARD_FLAG_NOHOTKEYS;
+    }
+    if (data->raw_keyboard_flag_inputsink) {
+        flags |= RAW_KEYBOARD_FLAG_INPUTSINK;
     }
     }
     if (flags != data->raw_input_enabled) {
     if (flags != data->raw_input_enabled) {
-        if (WIN_SetRawInputEnabled(_this, flags)) {
+        bool force = false;
+        if ((flags ^ data->raw_input_enabled) & (RAW_KEYBOARD_FLAG_NOHOTKEYS | RAW_KEYBOARD_FLAG_INPUTSINK)) {
+            // The keyboard flags have changed
+            force = true;
+        }
+        if (WIN_SetRawInputEnabled(_this, flags, force)) {
             data->raw_input_enabled = flags;
             data->raw_input_enabled = flags;
         } else {
         } else {
             return false;
             return false;
@@ -308,6 +338,11 @@ bool WIN_SetRawKeyboardFlag_Inputsink(SDL_VideoDevice *_this, bool enabled)
 
 
 #else
 #else
 
 
+bool WIN_SetRawInputEnabled(SDL_VideoDevice *_this, Uint32 flags, bool force)
+{
+    return SDL_Unsupported();
+}
+
 bool WIN_SetRawMouseEnabled(SDL_VideoDevice *_this, bool enabled)
 bool WIN_SetRawMouseEnabled(SDL_VideoDevice *_this, bool enabled)
 {
 {
     return SDL_Unsupported();
     return SDL_Unsupported();

+ 1 - 0
src/video/windows/SDL_windowsrawinput.h

@@ -23,6 +23,7 @@
 #ifndef SDL_windowsrawinput_h_
 #ifndef SDL_windowsrawinput_h_
 #define SDL_windowsrawinput_h_
 #define SDL_windowsrawinput_h_
 
 
+extern bool WIN_SetRawInputEnabled(SDL_VideoDevice *_this, Uint32 flags, bool force);
 extern bool WIN_SetRawMouseEnabled(SDL_VideoDevice *_this, bool enabled);
 extern bool WIN_SetRawMouseEnabled(SDL_VideoDevice *_this, bool enabled);
 extern bool WIN_SetRawKeyboardEnabled(SDL_VideoDevice *_this, bool enabled);
 extern bool WIN_SetRawKeyboardEnabled(SDL_VideoDevice *_this, bool enabled);
 extern bool WIN_SetRawKeyboardFlag_NoHotkeys(SDL_VideoDevice *_this, bool enabled);
 extern bool WIN_SetRawKeyboardFlag_NoHotkeys(SDL_VideoDevice *_this, bool enabled);

+ 1 - 2
src/video/windows/SDL_windowsvideo.c

@@ -671,8 +671,7 @@ void WIN_VideoQuit(SDL_VideoDevice *_this)
     SDL_RemoveHintCallback(SDL_HINT_WINDOWS_ENABLE_MENU_MNEMONICS, UpdateWindowsEnableMenuMnemonics, NULL);
     SDL_RemoveHintCallback(SDL_HINT_WINDOWS_ENABLE_MENU_MNEMONICS, UpdateWindowsEnableMenuMnemonics, NULL);
     SDL_RemoveHintCallback(SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN, UpdateWindowFrameUsableWhileCursorHidden, NULL);
     SDL_RemoveHintCallback(SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN, UpdateWindowFrameUsableWhileCursorHidden, NULL);
 
 
-    WIN_SetRawMouseEnabled(_this, false);
-    WIN_SetRawKeyboardEnabled(_this, false);
+    WIN_SetRawInputEnabled(_this, 0, true);
     WIN_QuitGameInput(_this);
     WIN_QuitGameInput(_this);
 
 
 #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
 #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)