Replace VSync with PresentMode (#3812)

# Objective

Enable the user to specify any presentation modes (including `Mailbox`).

Fixes #3807

## Solution

I've added a new `PresentMode` enum in `bevy_window` that mirrors the `wgpu` enum 1:1. Alternatively, I could add a new dependency on `wgpu-types` if that would be preferred.
This commit is contained in:
aloucks 2022-02-04 03:37:44 +00:00
parent fe0e5580db
commit 1477765f62
8 changed files with 64 additions and 26 deletions

View file

@ -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<TextureView>,
pub size_changed: bool,
}
@ -83,7 +83,7 @@ fn extract_windows(mut render_world: ResMut<RenderWorld>, windows: Res<Windows>)
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,
},
};

View file

@ -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<f64>,
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<f64>,
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,

View file

@ -94,7 +94,7 @@ fn change_window(world: &mut World) {
.to_physical::<f64>(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);

View file

@ -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()

View file

@ -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()
})

View file

@ -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)

View file

@ -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()
},

View file

@ -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)