diff --git a/Cargo.lock b/Cargo.lock index f76d4d520d..8a7d512b83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12344,18 +12344,18 @@ dependencies = [ [[package]] name = "profiling" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" dependencies = [ "profiling-procmacros", ] [[package]] name = "profiling-procmacros" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" +checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" dependencies = [ "quote", "syn 2.0.101", diff --git a/Cargo.toml b/Cargo.toml index 9a05d89e53..0ad7a8e5ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -537,7 +537,7 @@ portable-pty = "0.9.0" postage = { version = "0.5", features = ["futures-traits"] } pretty_assertions = { version = "1.3.0", features = ["unstable"] } proc-macro2 = "1.0.93" -profiling = "1" +profiling = "1.0.17" prost = "0.9" prost-build = "0.9" prost-types = "0.9" diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index fcd52b6956..0823bf17e0 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -207,7 +207,7 @@ impl DirectXRenderer { fn present(&mut self) -> Result<()> { unsafe { - let result = self.resources.swap_chain.Present(1, DXGI_PRESENT(0)); + let result = self.resources.swap_chain.Present(0, DXGI_PRESENT(0)); // Presenting the swap chain can fail if the DirectX device was removed or reset. if result == DXGI_ERROR_DEVICE_REMOVED || result == DXGI_ERROR_DEVICE_RESET { let reason = self.devices.device.GetDeviceRemovedReason(); @@ -286,6 +286,7 @@ impl DirectXRenderer { Ok(()) } + #[profiling::function] pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> { self.pre_draw()?; for batch in scene.batches() { diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index 61f410a8c6..ad211b827f 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -238,6 +238,7 @@ fn handle_timer_msg( } } +#[profiling::function] fn handle_paint_msg(handle: HWND, state_ptr: Rc) -> Option { draw_window(handle, false, state_ptr) } @@ -373,6 +374,7 @@ fn handle_syskeyup_msg( // It's a known bug that you can't trigger `ctrl-shift-0`. See: // https://superuser.com/questions/1455762/ctrl-shift-number-key-combination-has-stopped-working-for-a-few-numbers +#[profiling::function] fn handle_keydown_msg( handle: HWND, wparam: WPARAM, @@ -380,6 +382,8 @@ fn handle_keydown_msg( state_ptr: Rc, ) -> Option { let mut lock = state_ptr.state.borrow_mut(); + lock.keydown_time = Some(std::time::Instant::now()); + println!("WM_KEYDOWN"); let Some(input) = handle_key_event(handle, wparam, lparam, &mut lock, |keystroke| { PlatformInput::KeyDown(KeyDownEvent { keystroke, @@ -1237,10 +1241,18 @@ fn draw_window( .request_frame .take()?; request_frame(RequestFrameOptions { - require_presentation: false, + require_presentation: true, force_render, }); - state_ptr.state.borrow_mut().callbacks.request_frame = Some(request_frame); + let mut lock = state_ptr.state.borrow_mut(); + if let Some(keydown_time) = lock.keydown_time.take() { + let elapsed = keydown_time.elapsed(); + println!( + "Elapsed keydown time: {:.02} ms", + elapsed.as_secs_f64() * 1000.0 + ); + } + lock.callbacks.request_frame = Some(request_frame); unsafe { ValidateRect(Some(handle), None).ok().log_err() }; Some(0) } @@ -1292,6 +1304,7 @@ fn translate_message(handle: HWND, wparam: WPARAM, lparam: LPARAM) { unsafe { TranslateMessage(&msg).ok().log_err() }; } +#[profiling::function] fn handle_key_event( handle: HWND, wparam: WPARAM, diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index 8433e29c6d..5dfa631189 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -14,25 +14,27 @@ use itertools::Itertools; use parking_lot::RwLock; use smallvec::SmallVec; use windows::{ - UI::ViewManagement::UISettings, - Win32::{ + core::*, Win32::{ Foundation::*, Graphics::{ + DirectComposition::DCompositionWaitForCompositorClock, + Dxgi::{ + CreateDXGIFactory2, IDXGIAdapter1, IDXGIFactory6, IDXGIOutput, DXGI_CREATE_FACTORY_FLAGS, DXGI_GPU_PREFERENCE_MINIMUM_POWER + }, Gdi::*, Imaging::{CLSID_WICImagingFactory, IWICImagingFactory}, }, Security::Credentials::*, System::{Com::*, LibraryLoader::*, Ole::*, SystemInformation::*, Threading::*}, UI::{Input::KeyboardAndMouse::*, Shell::*, WindowsAndMessaging::*}, - }, - core::*, + }, UI::ViewManagement::UISettings }; use crate::*; pub(crate) struct WindowsPlatform { state: RefCell, - raw_window_handles: RwLock>, + raw_window_handles: Arc>>, // The below members will never change throughout the entire lifecycle of the app. icon: HICON, main_receiver: flume::Receiver, @@ -109,7 +111,7 @@ impl WindowsPlatform { }; let icon = load_icon().unwrap_or_default(); let state = RefCell::new(WindowsPlatformState::new()); - let raw_window_handles = RwLock::new(SmallVec::new()); + let raw_window_handles = Arc::new(RwLock::new(SmallVec::new())); let windows_version = WindowsVersion::new().context("Error retrieve windows version")?; Ok(Self { @@ -128,10 +130,29 @@ impl WindowsPlatform { }) } + fn begin_vsync_thread(&self) { + let raw_window_handles = self.raw_window_handles.clone(); + std::thread::spawn(move || { + let vsync_provider = VSyncProvider::new(); + loop { + unsafe { + // DCompositionWaitForCompositorClock(None, INFINITE); + vsync_provider.wait_for_vsync(); + for handle in raw_window_handles.read().iter() { + RedrawWindow(Some(**handle), None, None, RDW_INVALIDATE) + .ok() + .log_err(); + // PostMessageW(Some(**handle), WM_GPUI_FORCE_DRAW_WINDOW, WPARAM(0), LPARAM(0)).log_err(); + } + } + } + }); + } + fn redraw_all(&self) { for handle in self.raw_window_handles.read().iter() { unsafe { - RedrawWindow(Some(*handle), None, None, RDW_INVALIDATE | RDW_UPDATENOW) + RedrawWindow(Some(**handle), None, None, RDW_INVALIDATE | RDW_UPDATENOW) .ok() .log_err(); } @@ -142,8 +163,8 @@ impl WindowsPlatform { self.raw_window_handles .read() .iter() - .find(|entry| *entry == &hwnd) - .and_then(|hwnd| try_get_window_inner(*hwnd)) + .find(|entry| ***entry == hwnd) + .and_then(|hwnd| try_get_window_inner(**hwnd)) } #[inline] @@ -152,7 +173,7 @@ impl WindowsPlatform { .read() .iter() .for_each(|handle| unsafe { - PostMessageW(Some(*handle), message, wparam, lparam).log_err(); + PostMessageW(Some(**handle), message, wparam, lparam).log_err(); }); } @@ -160,7 +181,7 @@ impl WindowsPlatform { let mut lock = self.raw_window_handles.write(); let index = lock .iter() - .position(|handle| *handle == target_window) + .position(|handle| **handle == target_window) .unwrap(); lock.remove(index); @@ -223,7 +244,8 @@ impl WindowsPlatform { fn handle_events(&self) -> bool { let mut msg = MSG::default(); unsafe { - while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() { + // while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() { + while GetMessageW(&mut msg, None, 0, 0).as_bool() { match msg.message { WM_QUIT => return true, WM_INPUTLANGCHANGE @@ -305,11 +327,17 @@ impl WindowsPlatform { if active_window_hwnd.is_invalid() { return None; } - self.raw_window_handles + if self + .raw_window_handles .read() .iter() - .find(|&&hwnd| hwnd == active_window_hwnd) - .copied() + .find(|&&hwnd| *hwnd == active_window_hwnd) + .is_some() + { + Some(active_window_hwnd) + } else { + None + } } } @@ -340,12 +368,13 @@ impl Platform for WindowsPlatform { fn run(&self, on_finish_launching: Box) { on_finish_launching(); - loop { - if self.handle_events() { - break; - } - self.redraw_all(); + self.begin_vsync_thread(); + // loop { + if self.handle_events() { + // break; } + // self.redraw_all(); + // } if let Some(ref mut callback) = self.state.borrow_mut().callbacks.quit { callback(); @@ -438,7 +467,7 @@ impl Platform for WindowsPlatform { ) -> Result> { let window = WindowsWindow::new(handle, options, self.generate_creation_info())?; let handle = window.get_raw_handle(); - self.raw_window_handles.write().push(handle); + self.raw_window_handles.write().push(handle.into()); Ok(Box::new(window)) } @@ -717,6 +746,46 @@ pub(crate) struct WindowCreationInfo { pub(crate) main_thread_id_win32: u32, } +struct VSyncProvider { + dxgi_output: IDXGIOutput, +} + +impl VSyncProvider { + fn new() -> Self { + let dxgi_factory: IDXGIFactory6 = + unsafe { CreateDXGIFactory2(DXGI_CREATE_FACTORY_FLAGS::default()) }.unwrap(); + let adapter: IDXGIAdapter1 = get_adapter(&dxgi_factory); + unsafe { + let dxgi_output = adapter.EnumOutputs(0).unwrap(); + Self { dxgi_output } + } + } + + fn wait_for_vsync(&self) { + unsafe { + self.dxgi_output.WaitForVBlank().unwrap(); + } + } +} + +fn get_adapter(dxgi_factory: &IDXGIFactory6) -> IDXGIAdapter1 { + unsafe { + for index in 0.. { + let adapter = dxgi_factory + .EnumAdapterByGpuPreference(index, DXGI_GPU_PREFERENCE_MINIMUM_POWER) + .unwrap(); + return adapter; + } + } + unreachable!("No DXGI adapter found") +} + +impl Default for VSyncProvider { + fn default() -> Self { + Self::new() + } +} + fn open_target(target: &str) { unsafe { let ret = ShellExecuteW( diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 1141e93565..42f6847a23 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -49,6 +49,7 @@ pub struct WindowsWindowState { pub hovered: bool, pub renderer: DirectXRenderer, + pub keydown_time: Option, pub click_state: ClickState, pub system_settings: WindowsSystemSettings, @@ -115,6 +116,7 @@ impl WindowsWindowState { let nc_button_pressed = None; let fullscreen = None; let initial_placement = None; + let keydown_time = None; Ok(Self { origin, @@ -133,6 +135,7 @@ impl WindowsWindowState { system_key_handled, hovered, renderer, + keydown_time, click_state, system_settings, current_cursor, diff --git a/crates/gpui/src/platform/windows/wrapper.rs b/crates/gpui/src/platform/windows/wrapper.rs index 6015dffdab..9d9df3a796 100644 --- a/crates/gpui/src/platform/windows/wrapper.rs +++ b/crates/gpui/src/platform/windows/wrapper.rs @@ -1,6 +1,9 @@ use std::ops::Deref; -use windows::Win32::{Foundation::HANDLE, UI::WindowsAndMessaging::HCURSOR}; +use windows::Win32::{ + Foundation::{HANDLE, HWND}, + UI::WindowsAndMessaging::HCURSOR, +}; #[derive(Debug, Clone, Copy)] pub(crate) struct SafeHandle { @@ -45,3 +48,31 @@ impl Deref for SafeCursor { &self.raw } } + +#[derive(Clone, Copy)] +pub(crate) struct SafeHwnd { + raw: HWND, +} + +unsafe impl Send for SafeHwnd {} +unsafe impl Sync for SafeHwnd {} + +impl From for SafeHwnd { + fn from(value: HWND) -> Self { + SafeHwnd { raw: value } + } +} + +impl From for HWND { + fn from(value: SafeHwnd) -> Self { + value.raw + } +} + +impl Deref for SafeHwnd { + type Target = HWND; + + fn deref(&self) -> &Self::Target { + &self.raw + } +}