From ea05b44f384077a1a466eeb9a58c523379225a01 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Thu, 30 Jun 2022 14:24:17 -0400 Subject: [PATCH] Improve Presentation Api (#2803) * Add new presentation modes * Expand presentation formats * Add Surface::get_supported_modes * Update to rust 2021 and improve some metal surface code * Update wgpu-types/src/lib.rs Co-authored-by: bjorn3 <17426603+bjorn3@users.noreply.github.com> * Fix windows build errors * Fix issues with ALLOW_TEARING Co-authored-by: bjorn3 <17426603+bjorn3@users.noreply.github.com> --- cts_runner/Cargo.toml | 2 +- deno_webgpu/Cargo.toml | 2 +- dummy/Cargo.toml | 2 +- player/Cargo.toml | 2 +- run-wasm/Cargo.toml | 2 +- wgpu-core/Cargo.toml | 2 +- wgpu-core/src/device/mod.rs | 63 +++++++++++++++++++--- wgpu-core/src/instance.rs | 23 ++++++-- wgpu-core/src/present.rs | 5 ++ wgpu-hal/Cargo.toml | 2 +- wgpu-hal/src/dx12/adapter.rs | 19 ++----- wgpu-hal/src/dx12/instance.rs | 22 +++++++- wgpu-hal/src/dx12/mod.rs | 17 +++--- wgpu-hal/src/metal/adapter.rs | 16 ++++-- wgpu-hal/src/metal/mod.rs | 1 + wgpu-hal/src/metal/surface.rs | 6 ++- wgpu-hal/src/vulkan/adapter.rs | 19 ++----- wgpu-hal/src/vulkan/conv.rs | 23 +++++++- wgpu-info/Cargo.toml | 2 +- wgpu-types/Cargo.toml | 2 +- wgpu-types/src/lib.rs | 81 +++++++++++++++++++++++----- wgpu/Cargo.toml | 2 +- wgpu/examples/hello-triangle/main.rs | 2 +- wgpu/src/backend/direct.rs | 17 +++++- wgpu/src/backend/web.rs | 11 ++++ wgpu/src/lib.rs | 12 +++++ 26 files changed, 277 insertions(+), 80 deletions(-) diff --git a/cts_runner/Cargo.toml b/cts_runner/Cargo.toml index 5487a64768..87db098717 100644 --- a/cts_runner/Cargo.toml +++ b/cts_runner/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = [ "Luca Casonato ", ] -edition = "2018" +edition = "2021" description = "CTS runner for wgpu" license = "MIT OR Apache-2.0" publish = false diff --git a/deno_webgpu/Cargo.toml b/deno_webgpu/Cargo.toml index 5d901c5c0f..3278cfc7da 100644 --- a/deno_webgpu/Cargo.toml +++ b/deno_webgpu/Cargo.toml @@ -4,7 +4,7 @@ name = "deno_webgpu" version = "0.54.0" authors = ["the Deno authors"] -edition = "2018" +edition = "2021" license = "MIT" readme = "README.md" repository = "https://github.com/gfx-rs/wgpu" diff --git a/dummy/Cargo.toml b/dummy/Cargo.toml index 40d1cfe696..796bde96f2 100644 --- a/dummy/Cargo.toml +++ b/dummy/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = [ "Dzmitry Malyshau ", ] -edition = "2018" +edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/player/Cargo.toml b/player/Cargo.toml index 21b2933f28..d5e712f4f7 100644 --- a/player/Cargo.toml +++ b/player/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = [ "Dzmitry Malyshau ", ] -edition = "2018" +edition = "2021" description = "WebGPU trace player" homepage = "https://github.com/gfx-rs/wgpu" repository = "https://github.com/gfx-rs/wgpu" diff --git a/run-wasm/Cargo.toml b/run-wasm/Cargo.toml index 02e9d46d21..eb7aea5cdc 100644 --- a/run-wasm/Cargo.toml +++ b/run-wasm/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "run-wasm" version = "0.1.0" -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml index 6ad988218d..31747dbef6 100644 --- a/wgpu-core/Cargo.toml +++ b/wgpu-core/Cargo.toml @@ -2,7 +2,7 @@ name = "wgpu-core" version = "0.12.0" authors = ["wgpu developers"] -edition = "2018" +edition = "2021" description = "WebGPU core logic on wgpu-hal" homepage = "https://github.com/gfx-rs/wgpu" repository = "https://github.com/gfx-rs/wgpu" diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 5c89c09430..e86bb201e9 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -3134,7 +3134,7 @@ impl Global { &self, surface_id: id::SurfaceId, adapter_id: id::AdapterId, - ) -> Result, instance::GetSurfacePreferredFormatError> { + ) -> Result, instance::GetSurfaceSupportError> { profiling::scope!("Surface::get_supported_formats"); let hub = A::hub(self); let mut token = Token::root(); @@ -3143,13 +3143,33 @@ impl Global { let (adapter_guard, mut _token) = hub.adapters.read(&mut token); let adapter = adapter_guard .get(adapter_id) - .map_err(|_| instance::GetSurfacePreferredFormatError::InvalidAdapter)?; + .map_err(|_| instance::GetSurfaceSupportError::InvalidAdapter)?; let surface = surface_guard .get(surface_id) - .map_err(|_| instance::GetSurfacePreferredFormatError::InvalidSurface)?; + .map_err(|_| instance::GetSurfaceSupportError::InvalidSurface)?; surface.get_supported_formats(adapter) } + pub fn surface_get_supported_modes( + &self, + surface_id: id::SurfaceId, + adapter_id: id::AdapterId, + ) -> Result, instance::GetSurfaceSupportError> { + profiling::scope!("Surface::get_supported_modes"); + let hub = A::hub(self); + let mut token = Token::root(); + + let (surface_guard, mut token) = self.surfaces.read(&mut token); + let (adapter_guard, mut _token) = hub.adapters.read(&mut token); + let adapter = adapter_guard + .get(adapter_id) + .map_err(|_| instance::GetSurfaceSupportError::InvalidAdapter)?; + let surface = surface_guard + .get(surface_id) + .map_err(|_| instance::GetSurfaceSupportError::InvalidSurface)?; + + surface.get_supported_modes(adapter) + } pub fn device_features( &self, @@ -4955,11 +4975,40 @@ impl Global { ); } if !caps.present_modes.contains(&config.present_mode) { - log::warn!( - "Surface does not support present mode: {:?}, falling back to FIFO", - config.present_mode, + let new_mode = loop { + // Automatic present mode checks. + // + // The "Automatic" modes are never supported by the backends. + match config.present_mode { + wgt::PresentMode::AutoVsync => { + if caps.present_modes.contains(&wgt::PresentMode::FifoRelaxed) { + break wgt::PresentMode::FifoRelaxed; + } + if caps.present_modes.contains(&wgt::PresentMode::Fifo) { + break wgt::PresentMode::Fifo; + } + } + wgt::PresentMode::AutoNoVsync => { + if caps.present_modes.contains(&wgt::PresentMode::Immediate) { + break wgt::PresentMode::Immediate; + } + if caps.present_modes.contains(&wgt::PresentMode::Mailbox) { + break wgt::PresentMode::Mailbox; + } + } + _ => {} + } + return Err(E::UnsupportedPresentMode { + requested: config.present_mode, + available: caps.present_modes.clone(), + }); + }; + + log::info!( + "Automatically choosing presentation mode by rule {:?}. Chose {new_mode:?}", + config.present_mode ); - config.present_mode = wgt::PresentMode::Fifo; + config.present_mode = new_mode; } if !caps.formats.contains(&config.format) { return Err(E::UnsupportedFormat { diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index 9efbf947f3..d7d86ea25a 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -155,7 +155,7 @@ impl Surface { pub fn get_supported_formats( &self, adapter: &Adapter, - ) -> Result, GetSurfacePreferredFormatError> { + ) -> Result, GetSurfaceSupportError> { let suf = A::get_surface(self); let mut caps = unsafe { profiling::scope!("surface_capabilities"); @@ -163,7 +163,7 @@ impl Surface { .raw .adapter .surface_capabilities(&suf.raw) - .ok_or(GetSurfacePreferredFormatError::UnsupportedQueueFamily)? + .ok_or(GetSurfaceSupportError::UnsupportedQueueFamily)? }; // TODO: maybe remove once we support texture view changing srgb-ness @@ -171,6 +171,23 @@ impl Surface { Ok(caps.formats) } + + pub fn get_supported_modes( + &self, + adapter: &Adapter, + ) -> Result, GetSurfaceSupportError> { + let suf = A::get_surface(self); + let caps = unsafe { + profiling::scope!("surface_capabilities"); + adapter + .raw + .adapter + .surface_capabilities(&suf.raw) + .ok_or(GetSurfaceSupportError::UnsupportedQueueFamily)? + }; + + Ok(caps.present_modes) + } } pub struct Adapter { @@ -341,7 +358,7 @@ pub enum IsSurfaceSupportedError { } #[derive(Clone, Debug, Error)] -pub enum GetSurfacePreferredFormatError { +pub enum GetSurfaceSupportError { #[error("invalid adapter")] InvalidAdapter, #[error("invalid surface")] diff --git a/wgpu-core/src/present.rs b/wgpu-core/src/present.rs index 71e79cfea9..79a2f97c38 100644 --- a/wgpu-core/src/present.rs +++ b/wgpu-core/src/present.rs @@ -75,6 +75,11 @@ pub enum ConfigureSurfaceError { requested: wgt::TextureFormat, available: Vec, }, + #[error("requested present mode {requested:?} is not in the list of supported present modes: {available:?}")] + UnsupportedPresentMode { + requested: wgt::PresentMode, + available: Vec, + }, #[error("requested usage is not supported")] UnsupportedUsage, } diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index 38c36293d4..9d42b2042f 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -2,7 +2,7 @@ name = "wgpu-hal" version = "0.12.0" authors = ["wgpu developers"] -edition = "2018" +edition = "2021" description = "WebGPU hardware abstraction layer" homepage = "https://github.com/gfx-rs/wgpu" repository = "https://github.com/gfx-rs/wgpu" diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index 7d4b993e68..0bf3423f9c 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -4,7 +4,7 @@ use crate::{ }; use std::{mem, sync::Arc, thread}; use winapi::{ - shared::{dxgi, dxgi1_2, dxgi1_5, minwindef, windef, winerror}, + shared::{dxgi, dxgi1_2, windef, winerror}, um::{d3d12, d3d12sdklayers, winuser}, }; @@ -426,20 +426,9 @@ impl crate::Adapter for super::Adapter { } }; - let mut present_modes = vec![wgt::PresentMode::Fifo]; - #[allow(trivial_casts)] - if let Some(factory5) = surface.factory.as_factory5() { - let mut allow_tearing: minwindef::BOOL = minwindef::FALSE; - let hr = factory5.CheckFeatureSupport( - dxgi1_5::DXGI_FEATURE_PRESENT_ALLOW_TEARING, - &mut allow_tearing as *mut _ as *mut _, - mem::size_of::() as _, - ); - - match hr.into_result() { - Err(err) => log::warn!("Unable to check for tearing support: {}", err), - Ok(()) => present_modes.push(wgt::PresentMode::Immediate), - } + let mut present_modes = vec![wgt::PresentMode::Mailbox, wgt::PresentMode::Fifo]; + if surface.supports_allow_tearing { + present_modes.push(wgt::PresentMode::Immediate); } Some(crate::SurfaceCapabilities { diff --git a/wgpu-hal/src/dx12/instance.rs b/wgpu-hal/src/dx12/instance.rs index 6129d53a71..2f115a6a62 100644 --- a/wgpu-hal/src/dx12/instance.rs +++ b/wgpu-hal/src/dx12/instance.rs @@ -1,6 +1,8 @@ +use winapi::shared::{dxgi1_5, minwindef}; + use super::SurfaceTarget; use crate::auxil::{self, dxgi::result::HResult as _}; -use std::sync::Arc; +use std::{mem, sync::Arc}; impl Drop for super::Instance { fn drop(&mut self) { @@ -37,11 +39,28 @@ impl crate::Instance for super::Instance { desc.flags, )?; + let mut supports_allow_tearing = false; + #[allow(trivial_casts)] + if let Some(factory5) = factory.as_factory5() { + let mut allow_tearing: minwindef::BOOL = minwindef::FALSE; + let hr = factory5.CheckFeatureSupport( + dxgi1_5::DXGI_FEATURE_PRESENT_ALLOW_TEARING, + &mut allow_tearing as *mut _ as *mut _, + mem::size_of::() as _, + ); + + match hr.into_result() { + Err(err) => log::warn!("Unable to check for tearing support: {}", err), + Ok(()) => supports_allow_tearing = true, + } + } + Ok(Self { // The call to create_factory will only succeed if we get a factory4, so this is safe. factory, library: Arc::new(lib_main), _lib_dxgi: lib_dxgi, + supports_allow_tearing, flags: desc.flags, }) } @@ -54,6 +73,7 @@ impl crate::Instance for super::Instance { raw_window_handle::RawWindowHandle::Win32(handle) => Ok(super::Surface { factory: self.factory, target: SurfaceTarget::WndHandle(handle.hwnd as *mut _), + supports_allow_tearing: self.supports_allow_tearing, swap_chain: None, }), _ => Err(crate::InstanceError), diff --git a/wgpu-hal/src/dx12/mod.rs b/wgpu-hal/src/dx12/mod.rs index 1d67d07a49..61d2ad9576 100644 --- a/wgpu-hal/src/dx12/mod.rs +++ b/wgpu-hal/src/dx12/mod.rs @@ -88,6 +88,7 @@ const ZERO_BUFFER_SIZE: wgt::BufferAddress = 256 << 10; pub struct Instance { factory: native::DxgiFactory, library: Arc, + supports_allow_tearing: bool, _lib_dxgi: native::DxgiLib, flags: crate::InstanceFlags, } @@ -100,6 +101,7 @@ impl Instance { Surface { factory: self.factory, target: SurfaceTarget::Visual(native::WeakPtr::from_raw(visual)), + supports_allow_tearing: self.supports_allow_tearing, swap_chain: None, } } @@ -128,6 +130,7 @@ enum SurfaceTarget { pub struct Surface { factory: native::DxgiFactory, target: SurfaceTarget, + supports_allow_tearing: bool, swap_chain: Option, } @@ -556,11 +559,11 @@ impl crate::Surface for Surface { config: &crate::SurfaceConfiguration, ) -> Result<(), crate::SurfaceError> { let mut flags = dxgi::DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; - match config.present_mode { - wgt::PresentMode::Immediate => { - flags |= dxgi::DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; - } - _ => {} + // We always set ALLOW_TEARING on the swapchain no matter + // what kind of swapchain we want because ResizeBuffers + // cannot change if ALLOW_TEARING is applied to the swapchain. + if self.supports_allow_tearing { + flags |= dxgi::DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; } let non_srgb_format = auxil::dxgi::conv::map_texture_format_nosrgb(config.format); @@ -771,9 +774,11 @@ impl crate::Queue for Queue { sc.acquired_count -= 1; let (interval, flags) = match sc.present_mode { + // We only allow immediate if ALLOW_TEARING is valid. wgt::PresentMode::Immediate => (0, dxgi::DXGI_PRESENT_ALLOW_TEARING), + wgt::PresentMode::Mailbox => (0, 0), wgt::PresentMode::Fifo => (1, 0), - wgt::PresentMode::Mailbox => (1, 0), + m => unreachable!("Cannot make surface with present mode {m:?}"), }; profiling::scope!("IDXGISwapchain3::Present"); diff --git a/wgpu-hal/src/metal/adapter.rs b/wgpu-hal/src/metal/adapter.rs index 1325b901f6..2b53069597 100644 --- a/wgpu-hal/src/metal/adapter.rs +++ b/wgpu-hal/src/metal/adapter.rs @@ -281,13 +281,18 @@ impl crate::Adapter for super::Adapter { None }; + let mut formats = vec![ + wgt::TextureFormat::Bgra8Unorm, + wgt::TextureFormat::Bgra8UnormSrgb, + wgt::TextureFormat::Rgba16Float, + ]; + if self.shared.private_caps.format_rgb10a2_unorm_surface { + formats.push(wgt::TextureFormat::Rgb10a2Unorm); + } + let pc = &self.shared.private_caps; Some(crate::SurfaceCapabilities { - formats: vec![ - wgt::TextureFormat::Bgra8Unorm, - wgt::TextureFormat::Bgra8UnormSrgb, - wgt::TextureFormat::Rgba16Float, - ], + formats, //Note: this is hardcoded in `CAMetalLayer` documentation swap_chain_sizes: if pc.can_set_maximum_drawables_count { 2..=3 @@ -575,6 +580,7 @@ impl super::PrivateCapabilities { format_rgba8_srgb_no_write: !Self::supports_any(device, RGBA8_SRGB), format_rgb10a2_unorm_all: Self::supports_any(device, RGB10A2UNORM_ALL), format_rgb10a2_unorm_no_write: !Self::supports_any(device, RGB10A2UNORM_ALL), + format_rgb10a2_unorm_surface: os_is_mac, format_rgb10a2_uint_color: !Self::supports_any(device, RGB10A2UINT_COLOR_WRITE), format_rgb10a2_uint_color_write: Self::supports_any(device, RGB10A2UINT_COLOR_WRITE), format_rg11b10_all: Self::supports_any(device, RG11B10FLOAT_ALL), diff --git a/wgpu-hal/src/metal/mod.rs b/wgpu-hal/src/metal/mod.rs index 540980a5a4..1ea8312b9f 100644 --- a/wgpu-hal/src/metal/mod.rs +++ b/wgpu-hal/src/metal/mod.rs @@ -180,6 +180,7 @@ struct PrivateCapabilities { format_rgba8_srgb_no_write: bool, format_rgb10a2_unorm_all: bool, format_rgb10a2_unorm_no_write: bool, + format_rgb10a2_unorm_surface: bool, format_rgb10a2_uint_color: bool, format_rgb10a2_uint_color_write: bool, format_rg11b10_all: bool, diff --git a/wgpu-hal/src/metal/surface.rs b/wgpu-hal/src/metal/surface.rs index 3031e4c67c..5975300442 100644 --- a/wgpu-hal/src/metal/surface.rs +++ b/wgpu-hal/src/metal/surface.rs @@ -170,7 +170,11 @@ impl crate::Surface for super::Surface { let render_layer = self.render_layer.lock(); let framebuffer_only = config.usage == crate::TextureUses::COLOR_TARGET; - let display_sync = config.present_mode != wgt::PresentMode::Immediate; + let display_sync = match config.present_mode { + wgt::PresentMode::Fifo => true, + wgt::PresentMode::Immediate => false, + m => unreachable!("Unsupported present mode: {m:?}"), + }; let drawable_size = CGSize::new(config.extent.width as f64, config.extent.height as f64); match config.composite_alpha_mode { diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index 878a5ed73c..4bfe3fd81f 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -1613,22 +1613,9 @@ impl crate::Adapter for super::Adapter { } }; - let supported_formats = [ - wgt::TextureFormat::Rgba8Unorm, - wgt::TextureFormat::Rgba8UnormSrgb, - wgt::TextureFormat::Bgra8Unorm, - wgt::TextureFormat::Bgra8UnormSrgb, - wgt::TextureFormat::Rgba16Float, - ]; - let formats = supported_formats - .iter() - .cloned() - .filter(|&format| { - let vk_format = self.private_caps.map_texture_format(format); - raw_surface_formats - .iter() - .any(|sf| sf.format == vk_format || sf.format == vk::Format::UNDEFINED) - }) + let formats = raw_surface_formats + .into_iter() + .filter_map(conv::map_vk_surface_formats) .collect(); Some(crate::SurfaceCapabilities { formats, diff --git a/wgpu-hal/src/vulkan/conv.rs b/wgpu-hal/src/vulkan/conv.rs index df24e8e6a7..dc5b915970 100644 --- a/wgpu-hal/src/vulkan/conv.rs +++ b/wgpu-hal/src/vulkan/conv.rs @@ -144,6 +144,24 @@ impl super::PrivateCapabilities { } } +pub fn map_vk_surface_formats(sf: vk::SurfaceFormatKHR) -> Option { + use ash::vk::Format as F; + use wgt::TextureFormat as Tf; + // List we care about pulled from https://vulkan.gpuinfo.org/listsurfaceformats.php + Some(match sf.format { + F::B8G8R8A8_UNORM => Tf::Bgra8Unorm, + F::B8G8R8A8_SRGB => Tf::Bgra8UnormSrgb, + F::R8G8B8A8_SNORM => Tf::Rgba8Snorm, + F::R8G8B8A8_UNORM => Tf::Rgba8Unorm, + F::R8G8B8A8_SRGB => Tf::Rgba8UnormSrgb, + F::R16G16B16A16_SFLOAT => Tf::Rgba16Float, + F::R16G16B16A16_SNORM => Tf::Rgba16Snorm, + F::R16G16B16A16_UNORM => Tf::Rgba16Unorm, + F::A2B10G10R10_UNORM_PACK32 => Tf::Rgb10a2Unorm, + _ => return None, + }) +} + impl crate::Attachment<'_, super::Api> { pub(super) fn make_attachment_key( &self, @@ -405,7 +423,10 @@ pub fn map_present_mode(mode: wgt::PresentMode) -> vk::PresentModeKHR { wgt::PresentMode::Immediate => vk::PresentModeKHR::IMMEDIATE, wgt::PresentMode::Mailbox => vk::PresentModeKHR::MAILBOX, wgt::PresentMode::Fifo => vk::PresentModeKHR::FIFO, - //wgt::PresentMode::Relaxed => vk::PresentModeKHR::FIFO_RELAXED, + wgt::PresentMode::FifoRelaxed => vk::PresentModeKHR::FIFO_RELAXED, + wgt::PresentMode::AutoNoVsync | wgt::PresentMode::AutoVsync => { + unreachable!("Cannot create swapchain with Auto PresentationMode") + } } } diff --git a/wgpu-info/Cargo.toml b/wgpu-info/Cargo.toml index 13767cc2c8..1b8ca530b4 100644 --- a/wgpu-info/Cargo.toml +++ b/wgpu-info/Cargo.toml @@ -2,7 +2,7 @@ name = "wgpu-info" version = "0.12.0" authors = ["wgpu developers"] -edition = "2018" +edition = "2021" description = "Adapter information and per-adapter test program" homepage = "https://github.com/gfx-rs/wgpu" repository = "https://github.com/gfx-rs/wgpu" diff --git a/wgpu-types/Cargo.toml b/wgpu-types/Cargo.toml index bdc69bc752..b34de382a9 100644 --- a/wgpu-types/Cargo.toml +++ b/wgpu-types/Cargo.toml @@ -2,7 +2,7 @@ name = "wgpu-types" version = "0.12.0" authors = ["wgpu developers"] -edition = "2018" +edition = "2021" description = "WebGPU types" homepage = "https://github.com/gfx-rs/wgpu" repository = "https://github.com/gfx-rs/wgpu" diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 9203d494a3..5e72546201 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -2949,20 +2949,75 @@ impl Default for CommandEncoderDescriptor> { #[cfg_attr(feature = "trace", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub enum PresentMode { - /// The presentation engine does **not** wait for a vertical blanking period and - /// the request is presented immediately. This is a low-latency presentation mode, - /// but visible tearing may be observed. Will fallback to `Fifo` if unavailable on the - /// selected platform and backend. Not optimal for mobile. - Immediate = 0, - /// The presentation engine waits for the next vertical blanking period to update - /// the current image, but frames may be submitted without delay. This is a low-latency - /// presentation mode and visible tearing will **not** be observed. Will fallback to `Fifo` - /// if unavailable on the selected platform and backend. Not optimal for mobile. - Mailbox = 1, - /// The presentation engine waits for the next vertical blanking period to update - /// the current image. The framerate will be capped at the display refresh rate, - /// corresponding to the `VSync`. Tearing cannot be observed. Optimal for mobile. + /// Chooses FifoRelaxed -> Fifo based on availability. + /// + /// Because of the fallback behavior, it is supported everywhere. + AutoVsync = 0, + /// Chooses Immediate -> Mailbox -> Fifo (on web) based on availability. + /// + /// Because of the fallback behavior, it is supported everywhere. + AutoNoVsync = 1, + /// Presentation frames are kept in a First-In-First-Out queue approximately 3 frames + /// long. Every vertical blanking period, the presentation engine will pop a frame + /// off the queue to display. If there is no frame to display, it will present the same + /// frame again until the next vblank. + /// + /// When a present command is executed on the gpu, the presented image is added on the queue. + /// + /// No tearing will be observed. + /// + /// Calls to get_current_texture will block until there is a spot in the queue. + /// + /// Supported on all platforms. + /// + /// If you don't know what mode to choose, choose this mode. This is traditionally called "Vsync On". Fifo = 2, + /// Presentation frames are kept in a First-In-First-Out queue approximately 3 frames + /// long. Every vertical blanking period, the presentation engine will pop a frame + /// off the queue to display. If there is no frame to display, it will present the + /// same frame until there is a frame in the queue. The moment there is a frame in the + /// queue, it will immediately pop the frame off the queue. + /// + /// When a present command is executed on the gpu, the presented image is added on the queue. + /// + /// Tearing will be observed if frames last more than one vblank as the front buffer. + /// + /// Calls to get_current_texture will block until there is a spot in the queue. + /// + /// Supported on AMD on Vulkan. + /// + /// This is traditionally called "Adaptive Vsync" + FifoRelaxed = 3, + /// Presentation frames are not queued at all. The moment a present command + /// is executed on the GPU, the presented image is swapped onto the front buffer + /// immediately. + /// + /// Tearing can be observed. + /// + /// Supported on most platforms except older DX12. + /// + /// This is traditionally called "Vsync Off". + Immediate = 4, + /// Presentation frames are kept in a single-frame queue. Every vertical blanking period, + /// the presentation engine will pop a frame from the queue. If there is no frame to display, + /// it will present the same frame again until the next vblank. + /// + /// When a present command is executed on the gpu, the frame will be put into the queue. + /// If there was already a frame in the queue, the new frame will _replace_ the old frame + /// on the queue. + /// + /// No tearing will be observed. + /// + /// Supported on DX11/12 on Windows 10, and NVidia on Vulkan. + /// + /// This is traditionally called "Fast Vsync" + Mailbox = 5, +} + +impl Default for PresentMode { + fn default() -> Self { + Self::Fifo + } } bitflags::bitflags! { diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index a1374c1fae..cf7108b91c 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -2,7 +2,7 @@ name = "wgpu" version = "0.12.0" authors = ["wgpu developers"] -edition = "2018" +edition = "2021" description = "Rusty WebGPU API wrapper" homepage = "https://wgpu.rs/" repository = "https://github.com/gfx-rs/wgpu/tree/v0.12" diff --git a/wgpu/examples/hello-triangle/main.rs b/wgpu/examples/hello-triangle/main.rs index 0de07f99ed..dd1012e2ba 100644 --- a/wgpu/examples/hello-triangle/main.rs +++ b/wgpu/examples/hello-triangle/main.rs @@ -72,7 +72,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) { format: swapchain_format, width: size.width, height: size.height, - present_mode: wgpu::PresentMode::Mailbox, + present_mode: wgpu::PresentMode::Fifo, }; surface.configure(&device, &config); diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index 5d35adc7a7..cb5ca21b92 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -20,6 +20,7 @@ use std::{ slice, sync::Arc, }; +use wgt::PresentMode; const LABEL: &str = "label"; @@ -972,7 +973,21 @@ impl crate::Context for Context { match wgc::gfx_select!(adapter => global.surface_get_supported_formats(surface.id, *adapter)) { Ok(formats) => formats, - Err(wgc::instance::GetSurfacePreferredFormatError::UnsupportedQueueFamily) => vec![], + Err(wgc::instance::GetSurfaceSupportError::UnsupportedQueueFamily) => vec![], + Err(err) => self.handle_error_fatal(err, "Surface::get_supported_formats"), + } + } + + fn surface_get_supported_modes( + &self, + surface: &Self::SurfaceId, + adapter: &Self::AdapterId, + ) -> Vec { + let global = &self.0; + match wgc::gfx_select!(adapter => global.surface_get_supported_modes(surface.id, *adapter)) + { + Ok(modes) => modes, + Err(wgc::instance::GetSurfaceSupportError::UnsupportedQueueFamily) => vec![], Err(err) => self.handle_error_fatal(err, "Surface::get_supported_formats"), } } diff --git a/wgpu/src/backend/web.rs b/wgpu/src/backend/web.rs index 7dae9adaa9..54ebeadb8a 100644 --- a/wgpu/src/backend/web.rs +++ b/wgpu/src/backend/web.rs @@ -1207,12 +1207,23 @@ impl crate::Context for Context { ] } + fn surface_get_supported_modes( + &self, + _surface: &Self::SurfaceId, + _adapter: &Self::AdapterId, + ) -> Vec { + vec![wgt::PresentMode::Fifo] + } + fn surface_configure( &self, surface: &Self::SurfaceId, device: &Self::DeviceId, config: &wgt::SurfaceConfiguration, ) { + if let wgt::PresentMode::Mailbox | wgt::PresentMode::Immediate = config.present_mode { + panic!("Only FIFO/Auto* is supported on web"); + } let mut mapped = web_sys::GpuCanvasConfiguration::new(&device.0, map_texture_format(config.format)); mapped.usage(config.usage.bits()); diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index a6bdb41215..090d67c1f1 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -230,6 +230,11 @@ trait Context: Debug + Send + Sized + Sync { surface: &Self::SurfaceId, adapter: &Self::AdapterId, ) -> Vec; + fn surface_get_supported_modes( + &self, + surface: &Self::SurfaceId, + adapter: &Self::AdapterId, + ) -> Vec; fn surface_configure( &self, surface: &Self::SurfaceId, @@ -3554,6 +3559,13 @@ impl Surface { Context::surface_get_supported_formats(&*self.context, &self.id, &adapter.id) } + /// Returns a vec of supported presentation modes to use for the [`Surface`] with this adapter. + /// + /// Returns an empty vector if the surface is incompatible with the adapter. + pub fn get_supported_modes(&self, adapter: &Adapter) -> Vec { + Context::surface_get_supported_modes(&*self.context, &self.id, &adapter.id) + } + /// Initializes [`Surface`] for presentation. /// /// # Panics