From 677d6acc9d49d88901ecc5499937f17c3b2ad470 Mon Sep 17 00:00:00 2001 From: John Tur Date: Mon, 13 Oct 2025 12:40:00 -0400 Subject: [PATCH] Use `DwmFlush` unconditionally for Windows vsync (#39913) Closes #36934 I'm still experiencing bugs with the `DCompositionWaitForCompositorClock` API. Let's back out the support for now until the fixes are identified and widely available. `DwmFlush` does various things that aren't just waiting for VSync, so it's not ideal, but it's not bad enough that it's worth a bigger refactor right now. Release Notes: - N/A --- crates/gpui/src/platform/windows/vsync.rs | 113 ++-------------------- 1 file changed, 10 insertions(+), 103 deletions(-) diff --git a/crates/gpui/src/platform/windows/vsync.rs b/crates/gpui/src/platform/windows/vsync.rs index 5cbcb8e99e2741c4b37cad4d550e290c4cab869f..73c32cf9b92b93278d4f88a8c784fd3b2a8fc2d3 100644 --- a/crates/gpui/src/platform/windows/vsync.rs +++ b/crates/gpui/src/platform/windows/vsync.rs @@ -5,23 +5,10 @@ use std::{ use anyhow::{Context, Result}; use util::ResultExt; -use windows::{ - Win32::{ - Foundation::{HANDLE, HWND}, - Graphics::{ - DirectComposition::{ - COMPOSITION_FRAME_ID_COMPLETED, COMPOSITION_FRAME_ID_TYPE, COMPOSITION_FRAME_STATS, - COMPOSITION_TARGET_ID, - }, - Dwm::{DWM_TIMING_INFO, DwmFlush, DwmGetCompositionTimingInfo}, - }, - System::{ - LibraryLoader::{GetModuleHandleA, GetProcAddress}, - Performance::QueryPerformanceFrequency, - Threading::INFINITE, - }, - }, - core::{HRESULT, s}, +use windows::Win32::{ + Foundation::HWND, + Graphics::Dwm::{DWM_TIMING_INFO, DwmFlush, DwmGetCompositionTimingInfo}, + System::Performance::QueryPerformanceFrequency, }; static QPC_TICKS_PER_SECOND: LazyLock = LazyLock::new(|| { @@ -35,20 +22,6 @@ static QPC_TICKS_PER_SECOND: LazyLock = LazyLock::new(|| { const VSYNC_INTERVAL_THRESHOLD: Duration = Duration::from_millis(1); const DEFAULT_VSYNC_INTERVAL: Duration = Duration::from_micros(16_666); // ~60Hz -// Here we are using dynamic loading of DirectComposition functions, -// or the app will refuse to start on windows systems that do not support DirectComposition. -type DCompositionGetFrameId = - unsafe extern "system" fn(frameidtype: COMPOSITION_FRAME_ID_TYPE, frameid: *mut u64) -> HRESULT; -type DCompositionGetStatistics = unsafe extern "system" fn( - frameid: u64, - framestats: *mut COMPOSITION_FRAME_STATS, - targetidcount: u32, - targetids: *mut COMPOSITION_TARGET_ID, - actualtargetidcount: *mut u32, -) -> HRESULT; -type DCompositionWaitForCompositorClock = - unsafe extern "system" fn(count: u32, handles: *const HANDLE, timeoutinms: u32) -> u32; - pub(crate) struct VSyncProvider { interval: Duration, f: Box bool>, @@ -56,35 +29,12 @@ pub(crate) struct VSyncProvider { impl VSyncProvider { pub(crate) fn new() -> Self { - if let Some((get_frame_id, get_statistics, wait_for_comp_clock)) = - initialize_direct_composition() - .context("Retrieving DirectComposition functions") - .log_with_level(log::Level::Warn) - { - let interval = get_dwm_interval_from_direct_composition(get_frame_id, get_statistics) - .context("Failed to get DWM interval from DirectComposition") - .log_err() - .unwrap_or(DEFAULT_VSYNC_INTERVAL); - log::info!( - "DirectComposition is supported for VSync, interval: {:?}", - interval - ); - let f = Box::new(move || unsafe { - wait_for_comp_clock(0, std::ptr::null(), INFINITE) == 0 - }); - Self { interval, f } - } else { - let interval = get_dwm_interval() - .context("Failed to get DWM interval") - .log_err() - .unwrap_or(DEFAULT_VSYNC_INTERVAL); - log::info!( - "DirectComposition is not supported for VSync, falling back to DWM, interval: {:?}", - interval - ); - let f = Box::new(|| unsafe { DwmFlush().is_ok() }); - Self { interval, f } - } + let interval = get_dwm_interval() + .context("Failed to get DWM interval") + .log_err() + .unwrap_or(DEFAULT_VSYNC_INTERVAL); + let f = Box::new(|| unsafe { DwmFlush().is_ok() }); + Self { interval, f } } pub(crate) fn wait_for_vsync(&self) { @@ -105,49 +55,6 @@ impl VSyncProvider { } } -fn initialize_direct_composition() -> Result<( - DCompositionGetFrameId, - DCompositionGetStatistics, - DCompositionWaitForCompositorClock, -)> { - unsafe { - // Load DLL at runtime since older Windows versions don't have dcomp. - let hmodule = GetModuleHandleA(s!("dcomp.dll")).context("Loading dcomp.dll")?; - let get_frame_id_addr = GetProcAddress(hmodule, s!("DCompositionGetFrameId")) - .context("Function DCompositionGetFrameId not found")?; - let get_statistics_addr = GetProcAddress(hmodule, s!("DCompositionGetStatistics")) - .context("Function DCompositionGetStatistics not found")?; - let wait_for_compositor_clock_addr = - GetProcAddress(hmodule, s!("DCompositionWaitForCompositorClock")) - .context("Function DCompositionWaitForCompositorClock not found")?; - let get_frame_id: DCompositionGetFrameId = std::mem::transmute(get_frame_id_addr); - let get_statistics: DCompositionGetStatistics = std::mem::transmute(get_statistics_addr); - let wait_for_compositor_clock: DCompositionWaitForCompositorClock = - std::mem::transmute(wait_for_compositor_clock_addr); - Ok((get_frame_id, get_statistics, wait_for_compositor_clock)) - } -} - -fn get_dwm_interval_from_direct_composition( - get_frame_id: DCompositionGetFrameId, - get_statistics: DCompositionGetStatistics, -) -> Result { - let mut frame_id = 0; - unsafe { get_frame_id(COMPOSITION_FRAME_ID_COMPLETED, &mut frame_id) }.ok()?; - let mut stats = COMPOSITION_FRAME_STATS::default(); - unsafe { - get_statistics( - frame_id, - &mut stats, - 0, - std::ptr::null_mut(), - std::ptr::null_mut(), - ) - } - .ok()?; - Ok(retrieve_duration(stats.framePeriod, *QPC_TICKS_PER_SECOND)) -} - fn get_dwm_interval() -> Result { let mut timing_info = DWM_TIMING_INFO { cbSize: std::mem::size_of::() as u32,