windows: more frequent frame requests (#8921)

Ezekiel Warren created

Note rust analyzer running in background now without keyboard/mouse
movement.

![](https://media.discordapp.net/attachments/1208481909676576818/1214769879098597416/high-framerate-windows.gif?ex=65fa519c&is=65e7dc9c&hm=4c9ba72fa3c3c548964e46d9c07f0c0bf9545ed9a9ae11495101dcae5db06d59&=)

Release Notes:

- Improved frame rate on Windows

Change summary

Cargo.toml                                   |  1 
crates/gpui/src/platform/windows/platform.rs | 74 ++++++++++++---------
crates/gpui/src/platform/windows/window.rs   | 41 +++++------
3 files changed, 63 insertions(+), 53 deletions(-)

Detailed changes

Cargo.toml 🔗

@@ -313,6 +313,7 @@ sys-locale = "0.3.1"
 version = "0.53.0"
 features = [
     "Win32_Graphics_Gdi",
+    "Win32_Graphics_DirectComposition",
     "Win32_UI_WindowsAndMessaging",
     "Win32_UI_Input_KeyboardAndMouse",
     "Win32_System_SystemServices",

crates/gpui/src/platform/windows/platform.rs 🔗

@@ -18,12 +18,13 @@ use parking_lot::Mutex;
 use time::UtcOffset;
 use util::{ResultExt, SemanticVersion};
 use windows::Win32::{
-    Foundation::{CloseHandle, GetLastError, HANDLE, HWND, WAIT_EVENT},
-    System::Threading::{CreateEventW, INFINITE},
+    Foundation::{CloseHandle, BOOL, HANDLE, HWND, LPARAM, TRUE},
+    Graphics::DirectComposition::DCompositionWaitForCompositorClock,
+    System::Threading::{CreateEventW, GetCurrentThreadId, INFINITE},
     UI::WindowsAndMessaging::{
-        DispatchMessageW, GetMessageW, MsgWaitForMultipleObjects, PostQuitMessage,
-        SystemParametersInfoW, TranslateMessage, MSG, QS_ALLINPUT, SPI_GETWHEELSCROLLCHARS,
-        SPI_GETWHEELSCROLLLINES, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, WM_QUIT, WM_SETTINGCHANGE,
+        DispatchMessageW, EnumThreadWindows, PeekMessageW, PostQuitMessage, SystemParametersInfoW,
+        TranslateMessage, MSG, PM_REMOVE, SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES,
+        SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, WM_QUIT, WM_SETTINGCHANGE,
     },
 };
 
@@ -169,26 +170,31 @@ impl WindowsPlatform {
         }
     }
 
-    fn wait_message(&self) -> WindowsMessageWaitResult {
-        let wait_result = unsafe {
-            MsgWaitForMultipleObjects(Some(&[self.inner.event]), false, INFINITE, QS_ALLINPUT)
-        };
-
-        match wait_result {
-            WAIT_EVENT(0) => WindowsMessageWaitResult::ForegroundExecution,
-            WAIT_EVENT(1) => {
-                let mut msg = MSG::default();
-                unsafe { GetMessageW(&mut msg, HWND::default(), 0, 0) };
-                WindowsMessageWaitResult::WindowsMessage(msg)
-            }
-            _ => {
-                log::error!("unhandled windows wait message: {}", wait_result.0);
-                WindowsMessageWaitResult::Error
-            }
+    fn run_foreground_tasks(&self) {
+        for runnable in self.inner.main_receiver.drain() {
+            runnable.run();
         }
     }
 }
 
+unsafe extern "system" fn invalidate_window_callback(hwnd: HWND, _: LPARAM) -> BOOL {
+    if let Some(inner) = try_get_window_inner(hwnd) {
+        inner.invalidate_client_area();
+    }
+    TRUE
+}
+
+/// invalidates all windows belonging to a thread causing a paint message to be scheduled
+fn invalidate_thread_windows(win32_thread_id: u32) {
+    unsafe {
+        EnumThreadWindows(
+            win32_thread_id,
+            Some(invalidate_window_callback),
+            LPARAM::default(),
+        )
+    };
+}
+
 impl Platform for WindowsPlatform {
     fn background_executor(&self) -> BackgroundExecutor {
         self.inner.background_executor.clone()
@@ -204,16 +210,21 @@ impl Platform for WindowsPlatform {
 
     fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>) {
         on_finish_launching();
-        loop {
-            match self.wait_message() {
-                WindowsMessageWaitResult::ForegroundExecution => {
-                    for runnable in self.inner.main_receiver.drain() {
-                        runnable.run();
-                    }
-                }
-                WindowsMessageWaitResult::WindowsMessage(msg) => {
+        'a: loop {
+            let mut msg = MSG::default();
+            // will be 0 if woken up by self.inner.event or 1 if the compositor clock ticked
+            // SEE: https://learn.microsoft.com/en-us/windows/win32/directcomp/compositor-clock/compositor-clock
+            let wait_result =
+                unsafe { DCompositionWaitForCompositorClock(Some(&[self.inner.event]), INFINITE) };
+
+            // compositor clock ticked so we should draw a frame
+            if wait_result == 1 {
+                unsafe { invalidate_thread_windows(GetCurrentThreadId()) };
+
+                while unsafe { PeekMessageW(&mut msg, HWND::default(), 0, 0, PM_REMOVE) }.as_bool()
+                {
                     if msg.message == WM_QUIT {
-                        break;
+                        break 'a;
                     }
 
                     if !self.run_immediate_msg_handlers(&msg) {
@@ -221,8 +232,9 @@ impl Platform for WindowsPlatform {
                         unsafe { DispatchMessageW(&msg) };
                     }
                 }
-                WindowsMessageWaitResult::Error => {}
             }
+
+            self.run_foreground_tasks();
         }
 
         let mut callbacks = self.inner.callbacks.lock();

crates/gpui/src/platform/windows/window.rs 🔗

@@ -17,7 +17,8 @@ use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
 use windows::{
     core::{w, HSTRING, PCWSTR},
     Win32::{
-        Foundation::{HINSTANCE, HWND, LPARAM, LRESULT, WPARAM},
+        Foundation::{FALSE, HINSTANCE, HWND, LPARAM, LRESULT, WPARAM},
+        Graphics::Gdi::{BeginPaint, EndPaint, InvalidateRect, PAINTSTRUCT},
         System::SystemServices::{
             MK_LBUTTON, MK_MBUTTON, MK_RBUTTON, MK_XBUTTON1, MK_XBUTTON2, MODIFIERKEYS_FLAGS,
         },
@@ -158,6 +159,12 @@ impl WindowsWindowInner {
         }
     }
 
+    /// mark window client rect to be re-drawn
+    /// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-invalidaterect
+    pub(crate) fn invalidate_client_area(&self) {
+        unsafe { InvalidateRect(self.hwnd, None, FALSE) };
+    }
+
     /// returns true if message is handled and should not dispatch
     pub(crate) fn handle_immediate_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> bool {
         match msg {
@@ -245,16 +252,20 @@ impl WindowsWindowInner {
                     height: Pixels(height.0),
                 },
                 1.0,
-            )
+            );
         }
+        self.invalidate_client_area();
         LRESULT(0)
     }
 
     fn handle_paint_msg(&self) -> LRESULT {
+        let mut paint_struct = PAINTSTRUCT::default();
+        let hdc = unsafe { BeginPaint(self.hwnd, &mut paint_struct) };
         let mut callbacks = self.callbacks.borrow_mut();
-        if let Some(callback) = callbacks.request_frame.as_mut() {
-            callback()
+        if let Some(request_frame) = callbacks.request_frame.as_mut() {
+            request_frame();
         }
+        unsafe { EndPaint(self.hwnd, &paint_struct) };
         LRESULT(0)
     }
 
@@ -403,18 +414,12 @@ impl WindowsWindowInner {
                 };
 
                 if callback(PlatformInput::KeyDown(event)) {
-                    if let Some(request_frame) = callbacks.request_frame.as_mut() {
-                        request_frame();
-                    }
                     CallbackResult::Handled { by_callback: true }
                 } else if let Some(mut input_handler) = self.input_handler.take() {
                     if let Some(ime_key) = ime_key {
                         input_handler.replace_text_in_range(None, &ime_key);
                     }
                     self.input_handler.set(Some(input_handler));
-                    if let Some(request_frame) = callbacks.request_frame.as_mut() {
-                        request_frame();
-                    }
                     CallbackResult::Handled { by_callback: true }
                 } else {
                     CallbackResult::Handled { by_callback: false }
@@ -433,9 +438,8 @@ impl WindowsWindowInner {
         if let Some(keystroke) = keystroke {
             if let Some(callback) = callbacks.input.as_mut() {
                 let event = KeyUpEvent { keystroke };
-                CallbackResult::Handled {
-                    by_callback: callback(PlatformInput::KeyUp(event)),
-                }
+                let by_callback = callback(PlatformInput::KeyUp(event));
+                CallbackResult::Handled { by_callback }
             } else {
                 CallbackResult::Handled { by_callback: false }
             }
@@ -527,12 +531,8 @@ impl WindowsWindowInner {
                 modifiers: self.current_modifiers(),
                 touch_phase: TouchPhase::Moved,
             };
-            if callback(PlatformInput::ScrollWheel(event)) {
-                if let Some(request_frame) = callbacks.request_frame.as_mut() {
-                    request_frame();
-                }
-                return LRESULT(0);
-            }
+            callback(PlatformInput::ScrollWheel(event));
+            return LRESULT(0);
         }
         LRESULT(1)
     }
@@ -554,9 +554,6 @@ impl WindowsWindowInner {
                 touch_phase: TouchPhase::Moved,
             };
             if callback(PlatformInput::ScrollWheel(event)) {
-                if let Some(request_frame) = callbacks.request_frame.as_mut() {
-                    request_frame();
-                }
                 return LRESULT(0);
             }
         }