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
This commit is contained in:
@@ -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<u64> = LazyLock::new(|| {
|
||||
@@ -35,20 +22,6 @@ static QPC_TICKS_PER_SECOND: LazyLock<u64> = 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<dyn Fn() -> 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<Duration> {
|
||||
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<Duration> {
|
||||
let mut timing_info = DWM_TIMING_INFO {
|
||||
cbSize: std::mem::size_of::<DWM_TIMING_INFO>() as u32,
|
||||
|
||||
Reference in New Issue
Block a user