Desktop: Add an 'Enable V-Sync' preference on Mac (#3887)

* add vsync pref

* account for physical scale in pixel preview passthru check

* change allow to expect attr

* Update user-facing v-sync text

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Timon
2026-03-11 21:32:37 +00:00
committed by GitHub
co-authored by Keavon Chambers
parent 6d0357bbcf
commit a18b7ff79d
9 changed files with 111 additions and 31 deletions
+11 -2
View File
@@ -21,7 +21,7 @@ use crate::persist::PersistentData;
use crate::preferences;
use crate::render::{RenderError, RenderState};
use crate::window::Window;
use crate::wrapper::messages::{DesktopFrontendMessage, DesktopWrapperMessage, InputMessage, MouseKeys, MouseState};
use crate::wrapper::messages::{DesktopFrontendMessage, DesktopWrapperMessage, InputMessage, MouseKeys, MouseState, Preferences};
use crate::wrapper::{DesktopWrapper, NodeGraphExecutionResult, WgpuContext, serialize_frontend_messages};
pub(crate) struct App {
@@ -46,6 +46,8 @@ pub(crate) struct App {
web_communication_initialized: bool,
web_communication_startup_buffer: Vec<Vec<u8>>,
persistent_data: PersistentData,
#[cfg_attr(not(target_os = "macos"), expect(unused))]
preferences: Preferences,
cli: Cli,
startup_time: Option<Instant>,
exiting: Arc<AtomicBool>,
@@ -63,6 +65,7 @@ impl App {
wgpu_context: WgpuContext,
app_event_receiver: Receiver<AppEvent>,
app_event_scheduler: AppEventScheduler,
preferences: Preferences,
cli: Cli,
) -> Self {
let ctrlc_app_event_scheduler = app_event_scheduler.clone();
@@ -116,6 +119,7 @@ impl App {
web_communication_initialized: false,
web_communication_startup_buffer: Vec::new(),
persistent_data,
preferences,
cli,
startup_time: None,
exiting,
@@ -516,7 +520,12 @@ impl ApplicationHandler for App {
let window = Window::new(event_loop, self.app_event_scheduler.clone());
self.window = Some(window);
let render_state = RenderState::new(self.window.as_ref().unwrap(), self.wgpu_context.clone());
#[cfg(not(target_os = "macos"))]
let present_mode = None;
#[cfg(target_os = "macos")]
let present_mode = if !self.preferences.vsync { Some(wgpu::PresentMode::Immediate) } else { None };
let render_state = RenderState::new(self.window.as_ref().unwrap(), self.wgpu_context.clone(), present_mode);
self.render_state = Some(render_state);
if let Some(window) = &self.window.as_ref() {
+6 -4
View File
@@ -62,6 +62,8 @@ pub fn start() {
}
};
let prefs = preferences::read();
// Must be called before event loop initialization or native window integrations will break
App::init();
@@ -73,7 +75,7 @@ pub fn start() {
let (cef_view_info_sender, cef_view_info_receiver) = std::sync::mpsc::channel();
let disable_ui_acceleration = preferences::read().disable_ui_acceleration || cli.disable_ui_acceleration;
let disable_ui_acceleration = prefs.disable_ui_acceleration || cli.disable_ui_acceleration;
if disable_ui_acceleration {
println!("UI acceleration is disabled");
}
@@ -95,7 +97,7 @@ pub fn start() {
}
};
let app = App::new(Box::new(cef_context), cef_view_info_sender, wgpu_context, app_event_receiver, app_event_scheduler, cli);
let app = App::new(Box::new(cef_context), cef_view_info_sender, wgpu_context, app_event_receiver, app_event_scheduler, prefs, cli);
let exit_reason = app.run(event_loop);
@@ -111,15 +113,15 @@ pub fn start() {
drop(lock);
match exit_reason {
#[cfg(target_os = "linux")]
app::ExitReason::Restart | app::ExitReason::UiAccelerationFailure => {
tracing::error!("Restarting application");
let mut command = std::process::Command::new(std::env::current_exe().unwrap());
#[cfg(target_family = "unix")]
let _ = std::os::unix::process::CommandExt::exec(&mut command);
#[cfg(target_family = "unix")]
tracing::error!("Failed to restart application");
#[cfg(not(target_family = "unix"))]
let _ = command.spawn();
tracing::error!("Failed to restart application");
}
_ => {}
}
+3 -5
View File
@@ -1,4 +1,5 @@
use std::borrow::Cow;
use wgpu::PresentMode;
use crate::window::Window;
use crate::wrapper::{TargetTexture, WgpuContext, WgpuExecutor};
@@ -27,7 +28,7 @@ pub(crate) struct RenderState {
}
impl RenderState {
pub(crate) fn new(window: &Window, context: WgpuContext) -> Self {
pub(crate) fn new(window: &Window, context: WgpuContext, present_mode: Option<PresentMode>) -> Self {
let size = window.surface_size();
let surface = window.create_surface(context.instance.clone());
@@ -39,10 +40,7 @@ impl RenderState {
format: surface_format,
width: size.width,
height: size.height,
#[cfg(not(target_os = "macos"))]
present_mode: surface_caps.present_modes[0],
#[cfg(target_os = "macos")]
present_mode: wgpu::PresentMode::Immediate,
present_mode: present_mode.unwrap_or(surface_caps.present_modes[0]),
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 1,
+2 -2
View File
@@ -77,7 +77,7 @@ fn menu_items_from_wrapper(entries: Vec<WrapperMenuItem>) -> Vec<MenuItemKind> {
}
WrapperMenuItem::SubMenu { text: name, items, .. } => {
let items = menu_items_from_wrapper(items);
let items = items.iter().map(|item| menu_item_kind_to_dyn(item)).collect::<Vec<&dyn IsMenuItem>>();
let items = items.iter().map(menu_item_kind_to_dyn).collect::<Vec<&dyn IsMenuItem>>();
let submenu = Submenu::with_items(name, true, &items).unwrap();
menu_items.push(MenuItemKind::Submenu(submenu));
}
@@ -106,7 +106,7 @@ fn replace_children<'a, T: Into<MenuContainer<'a>>>(menu: T, new_items: Vec<Menu
for item in items.iter() {
menu.remove(menu_item_kind_to_dyn(item)).unwrap();
}
let items = new_items.iter().map(|item| menu_item_kind_to_dyn(item)).collect::<Vec<&dyn IsMenuItem>>();
let items = new_items.iter().map(menu_item_kind_to_dyn).collect::<Vec<&dyn IsMenuItem>>();
menu.append_items(items.as_ref()).unwrap();
}