Source code
Revision control
Copy as Markdown
Other Tools
#![allow(dead_code)] // IPresentationManager is unused currently
use windows::Win32::System::Performance::{QueryPerformanceCounter, QueryPerformanceFrequency};
pub enum PresentationTimer {
/// DXGI uses [`QueryPerformanceCounter()`]
Dxgi {
/// How many ticks of QPC per second
frequency: u64,
},
/// [`IPresentationManager`] uses [`QueryInterruptTimePrecise()`]
///
/// [`IPresentationManager`]: https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Graphics/CompositionSwapchain/struct.IPresentationManager.html
/// [`QueryInterruptTimePrecise()`]: https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/WindowsProgramming/fn.QueryInterruptTimePrecise.html
#[allow(non_snake_case)]
IPresentationManager {
fnQueryInterruptTimePrecise: unsafe extern "system" fn(*mut u64),
},
}
impl std::fmt::Debug for PresentationTimer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Self::Dxgi { frequency } => f
.debug_struct("DXGI")
.field("frequency", &frequency)
.finish(),
Self::IPresentationManager {
fnQueryInterruptTimePrecise,
} => f
.debug_struct("IPresentationManager")
.field(
"QueryInterruptTimePrecise",
&(fnQueryInterruptTimePrecise as usize),
)
.finish(),
}
}
}
impl PresentationTimer {
/// Create a presentation timer using QueryPerformanceFrequency (what DXGI uses for presentation times)
pub fn new_dxgi() -> Self {
let mut frequency = 0;
unsafe { QueryPerformanceFrequency(&mut frequency) }.unwrap();
Self::Dxgi {
frequency: frequency
.try_into()
.expect("Frequency should not be negative"),
}
}
/// Create a presentation timer using QueryInterruptTimePrecise (what IPresentationManager uses for presentation times)
///
/// Panics if QueryInterruptTimePrecise isn't found (below Win10)
pub fn new_ipresentation_manager() -> Self {
// We need to load this explicitly, as QueryInterruptTimePrecise is only available on Windows 10+
//
// Docs say it's in kernel32.dll, but it's actually in kernelbase.dll.
// api-ms-win-core-realtime-l1-1-1.dll
let kernelbase =
libloading::os::windows::Library::open_already_loaded("kernelbase.dll").unwrap();
// No concerns about lifetimes here as kernelbase is always there.
let ptr = unsafe { kernelbase.get(b"QueryInterruptTimePrecise\0").unwrap() };
Self::IPresentationManager {
fnQueryInterruptTimePrecise: *ptr,
}
}
/// Gets the current time in nanoseconds.
pub fn get_timestamp_ns(&self) -> u128 {
// Always do u128 math _after_ hitting the timing function.
match *self {
PresentationTimer::Dxgi { frequency } => {
let mut counter = 0;
unsafe { QueryPerformanceCounter(&mut counter) }.unwrap();
// counter * (1_000_000_000 / freq) but re-ordered to make more precise
(counter as u128 * 1_000_000_000) / frequency as u128
}
PresentationTimer::IPresentationManager {
fnQueryInterruptTimePrecise,
} => {
let mut counter = 0;
unsafe { fnQueryInterruptTimePrecise(&mut counter) };
// QueryInterruptTimePrecise uses units of 100ns for its tick.
counter as u128 * 100
}
}
}
}