diff --git a/crates/bevy_render/src/view/window.rs b/crates/bevy_render/src/view/window.rs index 94bf8a21c0fdb..a120f44c8f822 100644 --- a/crates/bevy_render/src/view/window.rs +++ b/crates/bevy_render/src/view/window.rs @@ -7,7 +7,7 @@ use crate::{ use bevy_app::{App, Plugin}; use bevy_ecs::prelude::*; use bevy_utils::{tracing::debug, HashMap, HashSet}; -use bevy_window::{RawWindowHandleWrapper, WindowId, Windows}; +use bevy_window::{PresentMode, RawWindowHandleWrapper, WindowId, Windows}; use std::ops::{Deref, DerefMut}; use wgpu::TextureFormat; @@ -43,7 +43,7 @@ pub struct ExtractedWindow { pub handle: RawWindowHandleWrapper, pub physical_width: u32, pub physical_height: u32, - pub vsync: bool, + pub present_mode: PresentMode, pub swap_chain_texture: Option, pub size_changed: bool, } @@ -83,7 +83,7 @@ fn extract_windows(mut render_world: ResMut, windows: Res) handle: window.raw_window_handle(), physical_width: new_width, physical_height: new_height, - vsync: window.vsync(), + present_mode: window.present_mode(), swap_chain_texture: None, size_changed: false, }); @@ -138,10 +138,10 @@ pub fn prepare_windows( width: window.physical_width, height: window.physical_height, usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - present_mode: if window.vsync { - wgpu::PresentMode::Fifo - } else { - wgpu::PresentMode::Immediate + present_mode: match window.present_mode { + PresentMode::Fifo => wgpu::PresentMode::Fifo, + PresentMode::Mailbox => wgpu::PresentMode::Mailbox, + PresentMode::Immediate => wgpu::PresentMode::Immediate, }, }; diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 7381a32134ef7..94442c5e06e06 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -5,6 +5,39 @@ use raw_window_handle::RawWindowHandle; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct WindowId(Uuid); +/// Presentation mode for a window. +/// +/// The presentation mode specifies when a frame is presented to the window. The `Fifo` +/// option corresponds to a traditional `VSync`, where the framerate is capped by the +/// display refresh rate. Both `Immediate` and `Mailbox` are low-latency and are not +/// capped by the refresh rate, but may not be available on all platforms. Tearing +/// may be observed with `Immediate` mode, but will not be observed with `Mailbox` or +/// `Fifo`. +/// +/// `Immediate` or `Mailbox` will gracefully fallback to `Fifo` when unavailable. +/// +/// The presentation mode may be declared in the [`WindowDescriptor`](WindowDescriptor::present_mode) +/// or updated on a [`Window`](Window::set_present_mode). +#[repr(C)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[doc(alias = "vsync")] +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. + Fifo = 2, // NOTE: The explicit ordinal values mirror wgpu and the vulkan spec. +} + impl WindowId { pub fn new() -> Self { WindowId(Uuid::new_v4()) @@ -121,7 +154,7 @@ pub struct Window { scale_factor_override: Option, backend_scale_factor: f64, title: String, - vsync: bool, + present_mode: PresentMode, resizable: bool, decorations: bool, cursor_icon: CursorIcon, @@ -152,8 +185,8 @@ pub enum WindowCommand { logical_resolution: (f32, f32), scale_factor: f64, }, - SetVsync { - vsync: bool, + SetPresentMode { + present_mode: PresentMode, }, SetResizable { resizable: bool, @@ -222,7 +255,7 @@ impl Window { scale_factor_override: window_descriptor.scale_factor_override, backend_scale_factor: scale_factor, title: window_descriptor.title.clone(), - vsync: window_descriptor.vsync, + present_mode: window_descriptor.present_mode, resizable: window_descriptor.resizable, decorations: window_descriptor.decorations, cursor_visible: window_descriptor.cursor_visible, @@ -425,14 +458,17 @@ impl Window { } #[inline] - pub fn vsync(&self) -> bool { - self.vsync + #[doc(alias = "vsync")] + pub fn present_mode(&self) -> PresentMode { + self.present_mode } #[inline] - pub fn set_vsync(&mut self, vsync: bool) { - self.vsync = vsync; - self.command_queue.push(WindowCommand::SetVsync { vsync }); + #[doc(alias = "set_vsync")] + pub fn set_present_mode(&mut self, present_mode: PresentMode) { + self.present_mode = present_mode; + self.command_queue + .push(WindowCommand::SetPresentMode { present_mode }); } #[inline] @@ -557,7 +593,8 @@ pub struct WindowDescriptor { pub resize_constraints: WindowResizeConstraints, pub scale_factor_override: Option, pub title: String, - pub vsync: bool, + #[doc(alias = "vsync")] + pub present_mode: PresentMode, pub resizable: bool, pub decorations: bool, pub cursor_visible: bool, @@ -584,7 +621,7 @@ impl Default for WindowDescriptor { position: None, resize_constraints: WindowResizeConstraints::default(), scale_factor_override: None, - vsync: true, + present_mode: PresentMode::Fifo, resizable: true, decorations: true, cursor_locked: false, diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index a833ecf44b57f..147f633f2ede1 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -94,7 +94,7 @@ fn change_window(world: &mut World) { .to_physical::(scale_factor), ); } - bevy_window::WindowCommand::SetVsync { .. } => (), + bevy_window::WindowCommand::SetPresentMode { .. } => (), bevy_window::WindowCommand::SetResizable { resizable } => { let window = winit_windows.get_window(id).unwrap(); window.set_resizable(resizable); diff --git a/examples/ios/src/lib.rs b/examples/ios/src/lib.rs index f79caa85d9625..c4f687f57c941 100644 --- a/examples/ios/src/lib.rs +++ b/examples/ios/src/lib.rs @@ -5,7 +5,6 @@ use bevy::{input::touch::TouchPhase, prelude::*, window::WindowMode}; fn main() { App::new() .insert_resource(WindowDescriptor { - vsync: true, resizable: false, mode: WindowMode::BorderlessFullscreen, ..Default::default() diff --git a/examples/tools/bevymark.rs b/examples/tools/bevymark.rs index 290fdf42594aa..39701f4924e61 100644 --- a/examples/tools/bevymark.rs +++ b/examples/tools/bevymark.rs @@ -2,6 +2,7 @@ use bevy::{ core::FixedTimestep, diagnostic::{Diagnostics, FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, prelude::*, + window::PresentMode, }; use rand::random; @@ -28,7 +29,7 @@ fn main() { title: "BevyMark".to_string(), width: 800., height: 600., - vsync: false, + present_mode: PresentMode::Mailbox, resizable: true, ..Default::default() }) diff --git a/examples/ui/text_debug.rs b/examples/ui/text_debug.rs index 867a7aae382ea..b5c062b47d002 100644 --- a/examples/ui/text_debug.rs +++ b/examples/ui/text_debug.rs @@ -1,13 +1,14 @@ use bevy::{ diagnostic::{Diagnostics, FrameTimeDiagnosticsPlugin}, prelude::*, + window::PresentMode, }; /// This example is for debugging text layout fn main() { App::new() .insert_resource(WindowDescriptor { - vsync: false, + present_mode: PresentMode::Immediate, ..Default::default() }) .add_plugins(DefaultPlugins) diff --git a/examples/window/multiple_windows.rs b/examples/window/multiple_windows.rs index 6932460a20947..b84d3eaec7f37 100644 --- a/examples/window/multiple_windows.rs +++ b/examples/window/multiple_windows.rs @@ -8,7 +8,7 @@ use bevy::{ renderer::RenderContext, RenderApp, RenderStage, }, - window::{CreateWindow, WindowId}, + window::{CreateWindow, PresentMode, WindowId}, }; /// This example creates a second window and draws a mesh from two different cameras, one in each window @@ -57,7 +57,7 @@ fn create_new_window( descriptor: WindowDescriptor { width: 800., height: 600., - vsync: false, + present_mode: PresentMode::Immediate, title: "Second window".to_string(), ..Default::default() }, diff --git a/examples/window/window_settings.rs b/examples/window/window_settings.rs index e00ba07416809..9bb4cfff860a8 100644 --- a/examples/window/window_settings.rs +++ b/examples/window/window_settings.rs @@ -1,4 +1,4 @@ -use bevy::prelude::*; +use bevy::{prelude::*, window::PresentMode}; /// This example illustrates how to customize the default window settings fn main() { @@ -7,7 +7,7 @@ fn main() { title: "I am a window!".to_string(), width: 500., height: 300., - vsync: true, + present_mode: PresentMode::Fifo, ..Default::default() }) .add_plugins(DefaultPlugins)