Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Presentation Timestamp Correlation #3240

Merged
merged 17 commits into from
Dec 20, 2022
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ Additionally `Surface::get_default_config` now returns an Option and returns Non
- Implement `Clone` for `ShaderSource` and `ShaderModuleDescriptor` in `wgpu`. By @daxpedda in [#3086](https://github.com/gfx-rs/wgpu/pull/3086).
- Add `get_default_config` for `Surface` to simplify user creation of `SurfaceConfiguration`. By @jinleili in [#3034](https://github.com/gfx-rs/wgpu/pull/3034)
- Native adapters can now use MSAA x2 and x8 if it's supported , previously only x1 and x4 were supported . By @39ali in [3140](https://github.com/gfx-rs/wgpu/pull/3140)
- Implemented correleation between user timestamps and platform specific presentation timestamps via [`Adapter::correlate_presentation_timestamp`]. By @cwfitzgerald in [#3240](https://github.com/gfx-rs/wgpu/pull/3240)
cwfitzgerald marked this conversation as resolved.
Show resolved Hide resolved

#### GLES

Expand Down Expand Up @@ -163,7 +164,6 @@ Additionally `Surface::get_default_config` now returns an Option and returns Non

- Make `wgpu::TextureFormat::Depth24PlusStencil8` available on all backends by making the feature unconditionally available and the feature unneeded to use the format. By @Healthire and @cwfitzgerald in [#3165](https://github.com/gfx-rs/wgpu/pull/3165)


## wgpu-0.14.0 (2022-10-05)

### Major Changes
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ futures-intrusive = "0.4"
fxhash = "0.2.1"
glam = "0.21.3"
libloading = "0.7"
libc = "0.2"
log = "0.4"
nanorand = { version = "0.7", default-features = false }
# Opt out of noise's "default-features" to avoid "image" feature as a dependency count optimization.
Expand Down
18 changes: 18 additions & 0 deletions wgpu-core/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,24 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.map_err(|_| InvalidAdapter)
}

pub fn adapter_correlate_presentation_timestamp<A: HalApi>(
&self,
adapter_id: AdapterId,
user_timestamp_function: &mut dyn FnMut(),
) -> Result<wgt::PresentationTimestamp, InvalidAdapter> {
let hub = A::hub(self);
let mut token = Token::root();
let (adapter_guard, _) = hub.adapters.read(&mut token);
let adapter = adapter_guard.get(adapter_id).map_err(|_| InvalidAdapter)?;

Ok(unsafe {
adapter
.raw
.adapter
.correlate_presentation_timestamp(user_timestamp_function)
})
}

pub fn adapter_drop<A: HalApi>(&self, adapter_id: AdapterId) {
profiling::scope!("Adapter::drop");

Expand Down
5 changes: 4 additions & 1 deletion wgpu-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ egl = { workspace = true, features = ["static", "no-pkg-config"] }
libloading = { workspace = true, optional = true }

[target.'cfg(windows)'.dependencies]
winapi = { workspace = true, features = ["libloaderapi", "windef", "winuser", "dcomp"] }
winapi = { workspace = true, features = ["profileapi", "libloaderapi", "windef", "winuser", "dcomp"] }
native = { workspace = true, features = ["libloading"], optional = true }

[target.'cfg(any(target_os="macos", target_os="ios"))'.dependencies]
Expand All @@ -99,6 +99,9 @@ wasm-bindgen.workspace = true
web-sys = { workspace = true, features = ["Window", "HtmlCanvasElement", "WebGl2RenderingContext", "OffscreenCanvas"] }
js-sys.workspace = true

[target.'cfg(unix)'.dependencies]
libc.workspace = true

[target.'cfg(target_os = "android")'.dependencies]
android_system_properties.workspace = true

Expand Down
1 change: 1 addition & 0 deletions wgpu-hal/src/auxil/dxgi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ pub mod conv;
pub mod exception;
pub mod factory;
pub mod result;
pub mod time;
94 changes: 94 additions & 0 deletions wgpu-hal/src/auxil/dxgi/time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#![allow(dead_code)] // IPresentationManager is unused currently

use std::mem;

use winapi::um::{
profileapi::{QueryPerformanceCounter, QueryPerformanceFrequency},
winnt::LARGE_INTEGER,
};

pub enum PresentationTimer {
/// DXGI uses QueryPerformanceCounter
Dxgi {
/// How many ticks of QPC per second
frequency: u64,
},
/// IPresentationManager uses QueryInterruptTimePrecise
#[allow(non_snake_case)]
IPresentationManager {
fnQueryInterruptTimePrecise: unsafe extern "system" fn(*mut winapi::ctypes::c_ulonglong),
},
}

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: LARGE_INTEGER = unsafe { mem::zeroed() };
let success = unsafe { QueryPerformanceFrequency(&mut frequency) };
assert_ne!(success, 0);

Self::Dxgi {
frequency: unsafe { *frequency.QuadPart() } as u64,
}
}

/// 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.
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").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: LARGE_INTEGER = unsafe { mem::zeroed() };
let success = unsafe { QueryPerformanceCounter(&mut counter) };
assert_ne!(success, 0);

// counter * (1_000_000_000 / freq) but re-ordered to make more precise
(unsafe { *counter.QuadPart() } 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
}
}
}
}
7 changes: 7 additions & 0 deletions wgpu-hal/src/dx11/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ impl crate::Adapter<super::Api> for super::Adapter {
) -> Option<crate::SurfaceCapabilities> {
todo!()
}

unsafe fn correlate_presentation_timestamp(
&self,
user_timestamp_function: &mut dyn FnMut(),
) -> wgt::PresentationTimestamp {
todo!()
}
}

impl super::Adapter {
Expand Down
12 changes: 12 additions & 0 deletions wgpu-hal/src/dx12/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,9 @@ impl super::Adapter {
shader_model_support.HighestShaderModel >= d3d12::D3D_SHADER_MODEL_5_1,
);

// TODO: Determine if IPresentationManager is supported
let presentation_timer = auxil::dxgi::time::PresentationTimer::new_dxgi();

let base = wgt::Limits::default();

Some(crate::ExposedAdapter {
Expand All @@ -237,6 +240,7 @@ impl super::Adapter {
device,
library: Arc::clone(library),
private_caps,
presentation_timer,
workarounds,
},
info,
Expand Down Expand Up @@ -540,4 +544,12 @@ impl crate::Adapter<super::Api> for super::Adapter {
composite_alpha_modes: vec![wgt::CompositeAlphaMode::Opaque],
})
}

unsafe fn correlate_presentation_timestamp(
&self,
user_timestamp_function: &mut dyn FnMut(),
) -> wgt::PresentationTimestamp {
user_timestamp_function();
wgt::PresentationTimestamp(self.presentation_timer.get_timestamp_ns())
}
}
1 change: 1 addition & 0 deletions wgpu-hal/src/dx12/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ pub struct Adapter {
device: native::Device,
library: Arc<native::D3D12Lib>,
private_caps: PrivateCapabilities,
presentation_timer: auxil::dxgi::time::PresentationTimer,
//Note: this isn't used right now, but we'll need it later.
#[allow(unused)]
workarounds: Workarounds,
Expand Down
8 changes: 8 additions & 0 deletions wgpu-hal/src/empty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ impl crate::Adapter<Api> for Context {
unsafe fn surface_capabilities(&self, surface: &Context) -> Option<crate::SurfaceCapabilities> {
None
}

unsafe fn correlate_presentation_timestamp(
&self,
user_timestamp_function: &mut dyn FnMut(),
) -> wgt::PresentationTimestamp {
user_timestamp_function();
wgt::PresentationTimestamp::INVALID_TIMESTAMP
}
}

impl crate::Queue<Api> for Context {
Expand Down
8 changes: 8 additions & 0 deletions wgpu-hal/src/gles/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,14 @@ impl crate::Adapter<super::Api> for super::Adapter {
None
}
}

unsafe fn correlate_presentation_timestamp(
&self,
user_timestamp_function: &mut dyn FnMut(),
) -> wgt::PresentationTimestamp {
user_timestamp_function();
wgt::PresentationTimestamp::INVALID_TIMESTAMP
}
}

impl super::AdapterShared {
Expand Down
10 changes: 10 additions & 0 deletions wgpu-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,16 @@ pub trait Adapter<A: Api>: Send + Sync {
///
/// `None` means presentation is not supported for it.
unsafe fn surface_capabilities(&self, surface: &A::Surface) -> Option<SurfaceCapabilities>;

/// Creates a [PresentationTimestamp](wgt::PresentationTimestamp) then immediately calls the user_timestamp_function,
/// allowing the user to correlate the presentation timestamp with another set of timestamps (like `Instant`s).
///
/// While user_timestamp_function is an FnMut, this is because of higher level internal details. It will only ever be called once
/// and it will unconditionally be called.
cwfitzgerald marked this conversation as resolved.
Show resolved Hide resolved
unsafe fn correlate_presentation_timestamp(
&self,
user_timestamp_function: &mut dyn FnMut(),
) -> wgt::PresentationTimestamp;
}

pub trait Device<A: Api>: Send + Sync {
Expand Down
10 changes: 10 additions & 0 deletions wgpu-hal/src/metal/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,16 @@ impl crate::Adapter<super::Api> for super::Adapter {
usage: crate::TextureUses::COLOR_TARGET | crate::TextureUses::COPY_DST, //TODO: expose more
})
}

unsafe fn correlate_presentation_timestamp(
&self,
user_tiemstamp_function: &mut dyn FnMut(),
) -> wgt::PresentationTimestamp {
user_tiemstamp_function();
cwfitzgerald marked this conversation as resolved.
Show resolved Hide resolved
let timestamp = self.shared.presentation_timer.get_timestamp_ns();

wgt::PresentationTimestamp(timestamp)
}
}

const RESOURCE_HEAP_SUPPORT: &[MTLFeatureSet] = &[
Expand Down
3 changes: 3 additions & 0 deletions wgpu-hal/src/metal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod command;
mod conv;
mod device;
mod surface;
mod time;

use std::{
fmt, iter, ops,
Expand Down Expand Up @@ -252,6 +253,7 @@ struct AdapterShared {
disabilities: PrivateDisabilities,
private_caps: PrivateCapabilities,
settings: Settings,
presentation_timer: time::PresentationTimer,
}

unsafe impl Send for AdapterShared {}
Expand All @@ -267,6 +269,7 @@ impl AdapterShared {
private_caps,
device: Mutex::new(device),
settings: Settings::default(),
presentation_timer: time::PresentationTimer::new(),
}
}
}
Expand Down
38 changes: 38 additions & 0 deletions wgpu-hal/src/metal/time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//! Handling of global timestamps.

#[repr(C)]
#[derive(Debug)]
struct MachTimebaseInfo {
numerator: u32,
denominator: u32,
}
extern "C" {
fn mach_timebase_info(out: *mut MachTimebaseInfo) -> u32;
fn mach_absolute_time() -> u64;
}

/// A timer which uses mach_absolute_time to get its time. This is what the metal callbacks use.
#[derive(Debug)]
pub struct PresentationTimer {
scale: MachTimebaseInfo,
}
impl PresentationTimer {
/// Generates a new timer.
pub fn new() -> Self {
// Default to 1 / 1 incase the call to timebase_info fails.
cwfitzgerald marked this conversation as resolved.
Show resolved Hide resolved
let mut scale = MachTimebaseInfo {
numerator: 1,
denominator: 1,
};
unsafe { mach_timebase_info(&mut scale) };

Self { scale }
}

/// Gets the current time in nanoseconds.
pub fn get_timestamp_ns(&self) -> u128 {
let time = unsafe { mach_absolute_time() };

(time as u128 * self.scale.numerator as u128) / self.scale.denominator as u128
}
}
30 changes: 30 additions & 0 deletions wgpu-hal/src/vulkan/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1582,6 +1582,36 @@ impl crate::Adapter<super::Api> for super::Adapter {
composite_alpha_modes: conv::map_vk_composite_alpha(caps.supported_composite_alpha),
})
}

unsafe fn correlate_presentation_timestamp(
&self,
user_timestamp_function: &mut dyn FnMut(),
) -> wgt::PresentationTimestamp {
// VK_GOOGLE_display_timing is the only way to get presentation
// timestamps on vulkan right now and it is only ever available
// on android and linux. This includes mac, but there's no alternative
// on mac, so this is fine.
#[cfg(unix)]
{
let mut timespec = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
user_timestamp_function();
unsafe {
libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut timespec);
}

wgt::PresentationTimestamp(
timespec.tv_sec as u128 * 1_000_000_000 + timespec.tv_nsec as u128,
)
}
#[cfg(not(unix))]
{
user_timestamp_function();
wgt::PresentationTimestamp::INVALID_TIMESTAMP
}
}
}

fn is_format_16bit_norm_supported(instance: &ash::Instance, phd: vk::PhysicalDevice) -> bool {
Expand Down
Loading