Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
22 changes: 13 additions & 9 deletions backends/vulkan/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ pub struct VulkanBackend {
slot: usize,
image_index: Option<u32>,
want_size: (u32, u32),
vsync: bool,
vsync: VsyncMode,
dirty_swapchain: bool,
buffers: Slab<Buffer>,
textures: Slab<Texture>,
Expand Down Expand Up @@ -653,14 +653,18 @@ impl VulkanBackend {
.surface_fn
.get_physical_device_surface_present_modes(self.pdev, self.surface)
.map_err(err("present modes"))?;
let mode = if self.vsync {
vk::PresentModeKHR::FIFO
} else if modes.contains(&vk::PresentModeKHR::MAILBOX) {
vk::PresentModeKHR::MAILBOX
} else if modes.contains(&vk::PresentModeKHR::IMMEDIATE) {
vk::PresentModeKHR::IMMEDIATE
use vk::PresentModeKHR as P;
let [first, second] = match self.vsync {
VsyncMode::On => [P::FIFO; 2],
VsyncMode::Off => [P::MAILBOX, P::IMMEDIATE],
VsyncMode::Uncapped => [P::IMMEDIATE, P::MAILBOX],
};
let mode = if modes.contains(&first) {
first
} else if modes.contains(&second) {
second
} else {
vk::PresentModeKHR::FIFO
P::FIFO
};
let mut count = caps.min_image_count + 1;
if caps.max_image_count > 0 {
Expand Down Expand Up @@ -1171,7 +1175,7 @@ impl Backend for VulkanBackend {
}
}

fn set_vsync(&mut self, vsync: bool) {
fn set_vsync(&mut self, vsync: VsyncMode) {
if self.vsync != vsync {
self.vsync = vsync;
self.dirty_swapchain = true;
Expand Down
21 changes: 12 additions & 9 deletions backends/wgpu/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -779,16 +779,19 @@ impl Backend for WgpuBackend {
}
}

fn set_vsync(&mut self, vsync: bool) {
let modes = &self.present_modes;
let mode = if vsync {
wgpu::PresentMode::Fifo
} else if modes.contains(&wgpu::PresentMode::Mailbox) {
wgpu::PresentMode::Mailbox
} else if modes.contains(&wgpu::PresentMode::Immediate) {
wgpu::PresentMode::Immediate
fn set_vsync(&mut self, vsync: VsyncMode) {
use wgpu::PresentMode as P;
let [first, second] = match vsync {
VsyncMode::On => [P::Fifo; 2],
VsyncMode::Off => [P::Mailbox, P::Immediate],
VsyncMode::Uncapped => [P::Immediate, P::Mailbox],
};
let mode = if self.present_modes.contains(&first) {
first
} else if self.present_modes.contains(&second) {
second
} else {
wgpu::PresentMode::Fifo
P::Fifo
};
if self.config.present_mode != mode {
self.config.present_mode = mode;
Expand Down
3 changes: 3 additions & 0 deletions crates/engine/assets/lang/en_us.lang
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ settings.fov_effects = FOV effects: {0}%
settings.gui_scale = GUI scale: {0}
settings.gui_scale.auto = Auto
settings.vsync = VSync: {0}
settings.vsync.off = Off
settings.vsync.on = On
settings.vsync.uncapped = Off (uncapped)
settings.renderer = Renderer: {0}
settings.renderer.restart = Renderer: {0} (after restart)
settings.renderer.vulkan = Vulkan
Expand Down
3 changes: 3 additions & 0 deletions crates/engine/assets/lang/fr_fr.lang
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ settings.fov_effects = Effets de champ de vision: {0}%
settings.gui_scale = Taille de l'interface: {0}
settings.gui_scale.auto = Auto
settings.vsync = VSync: {0}
settings.vsync.off = Non
settings.vsync.on = Oui
settings.vsync.uncapped = Non (sans limite d'IPS)
settings.renderer = Rendu : {0}
settings.renderer.restart = Rendu : {0} (au redémarrage)
settings.renderer.vulkan = Vulkan
Expand Down
3 changes: 3 additions & 0 deletions crates/engine/assets/lang/ru_ru.lang
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ settings.fov_effects = Эффекты поля зрения: {0}%
settings.gui_scale = Масштаб интерфейса: {0}
settings.gui_scale.auto = Авто
settings.vsync = Вертикальная синхронизация: {0}
settings.vsync.off = Выкл
settings.vsync.on = Вкл
settings.vsync.uncapped = Выкл (без лимита FPS)
settings.renderer = Рендер: {0}
settings.renderer.restart = Рендер: {0} (после перезапуска)
settings.renderer.vulkan = Vulkan
Expand Down
6 changes: 3 additions & 3 deletions crates/engine/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::time::Instant;

use glam::Vec2;
use weft_abi as abi;
use weft_render::{Backend, BackendDesc, Cmd, CommandList, Format, Target, TextureDesc, TextureId};
use weft_render::{Backend, BackendDesc, Cmd, CommandList, Format, Target, TextureDesc, TextureId, VsyncMode};
use winit::application::ApplicationHandler;
use winit::event::{DeviceEvent, DeviceId, ElementState, MouseButton, MouseScrollDelta, WindowEvent};
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
Expand Down Expand Up @@ -293,7 +293,7 @@ fn engine_assets(dev: bool) -> PathBuf {
/// The settings `Running::apply_settings` last put into effect.
#[derive(Default, PartialEq)]
struct Applied {
vsync: bool,
vsync: VsyncMode,
view: i32,
language: String,
gui_scale: u32,
Expand Down Expand Up @@ -328,7 +328,7 @@ impl App {
crate::settings::migrate(&self.opts.run_dir);
crate::config::load(&self.opts.run_dir);
if self.opts.no_vsync {
crate::config::override_for_run("core:vsync", "false");
crate::config::override_for_run("core:vsync", "off");
}
if let Some(v) = self.opts.view {
crate::config::override_for_run("core:view_distance", &v.to_string());
Expand Down
4 changes: 3 additions & 1 deletion crates/engine/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Mutex, OnceLock, RwLock};

use weft_abi::{config_kind as ck, config_scope as scope};
use weft_render::VsyncMode;

use crate::content::content;
use crate::entries::{Entry, RawEntry};
Expand Down Expand Up @@ -384,7 +385,8 @@ pub fn engine_configs(keys: &[(String, String)]) -> Vec<RawEntry> {
number("core:chat_opacity", ck::INT, 50.0, 0.0, 100.0, 1.0),
number("core:chat_visibility", ck::CHOICE, 0.0, 0.0, 2.0, 0.0)
.set(c::CHOICES, ["all", "system", "hidden"].map(String::from).to_vec()),
number("core:vsync", ck::BOOL, 1.0, 0.0, 1.0, 0.0),
number("core:vsync", ck::CHOICE, VsyncMode::On as u8 as f64, 0.0, 2.0, 0.0)
.set(c::CHOICES, VsyncMode::ALL.map(|m| m.name().to_string()).to_vec()),
// Taken at the next start; the others are tried if it can't run.
number("core:renderer", ck::CHOICE, 0.0, 0.0, 1.0, 0.0).set(c::CHOICES, ["vulkan", "wgpu"].map(String::from).to_vec()),
number("core:fullscreen", ck::BOOL, 0.0, 0.0, 1.0, 0.0),
Expand Down
5 changes: 3 additions & 2 deletions crates/engine/src/screens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -633,8 +633,9 @@ impl Screen for SettingsScreen {
let next = if r.settings.gui_scale() >= max { 0 } else { r.settings.gui_scale() + 1 };
r.settings.set("gui_scale", next as f64);
});
d.button("settings.vsync", &[on_off(s.vsync())], 300.0, false, |r| {
r.settings.set("vsync", !r.settings.vsync() as u8 as f64);
d.button("settings.vsync", &[Arg::Key(format!("settings.vsync.{}", s.vsync().name()))], 300.0, false, |r| {
r.settings
.set("vsync", r.settings.vsync().next() as u8 as f64);
r.handle(vec![Request::Rebuild]);
});
let chosen = s.renderer();
Expand Down
29 changes: 27 additions & 2 deletions crates/engine/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

use std::path::{Path, PathBuf};

use weft_render::VsyncMode;

use crate::config;
use crate::input::{Binding, Input, Slots};

Expand Down Expand Up @@ -87,8 +89,8 @@ impl Settings {
pub fn view_distance(&self) -> i32 {
number("core:view_distance") as i32
}
pub fn vsync(&self) -> bool {
number("core:vsync") != 0.0
pub fn vsync(&self) -> VsyncMode {
VsyncMode::from_index(number("core:vsync") as u32)
}
/// The chosen renderer's name (`core:renderer`).
pub fn renderer(&self) -> String {
Expand Down Expand Up @@ -189,6 +191,29 @@ pub fn migrate(run_dir: &Path) {
merge_settings(&config);
for dir in [config, run_dir.join("test-config")] {
migrate_bindings(&dir.join("core.txt"));
migrate_vsync(&dir.join("core.txt"));
}
}

/// `vsync` was true or false; it names a mode now. Off always preferred mailbox over immediate,
/// which is what `off` does, so both keep working the same.
/// TODO remove together with `migrate_bindings`
fn migrate_vsync(path: &Path) {
let Ok(src) = std::fs::read_to_string(path) else { return };
let mut changed = false;
let out: String = crate::kv::pairs(&src)
.map(|(_, k, v)| {
let v = match (k, v.trim()) {
("vsync", "true" | "1") => "on",
("vsync", "false" | "0") => "off",
_ => return format!("{k} = {v}\n"),
};
changed = true;
format!("{k} = {v}\n")
})
.collect();
if changed {
let _ = std::fs::write(path, out);
}
}

Expand Down
35 changes: 33 additions & 2 deletions crates/render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,11 +346,42 @@ impl std::error::Error for Error {}
pub trait Window: HasWindowHandle + HasDisplayHandle {}
impl<T: HasWindowHandle + HasDisplayHandle> Window for T {}

#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub enum VsyncMode {
/// May torn but sometimes resolves issues with `Off`.
Uncapped = 0,
/// Never torn, fps capped to the refresh rate.
#[default]
On = 1,
/// Never torn, but some players experience an fps cap.
Off = 2,
}

impl VsyncMode {
pub const ALL: [VsyncMode; 3] = [VsyncMode::Off, VsyncMode::On, VsyncMode::Uncapped];

pub fn name(self) -> &'static str {
match self {
VsyncMode::Off => "off",
VsyncMode::On => "on",
VsyncMode::Uncapped => "uncapped",
}
}

pub fn from_index(i: u32) -> VsyncMode {
Self::ALL.get(i as usize).copied().unwrap_or_default()
}

pub fn next(self) -> VsyncMode {
Self::from_index((self as u32 + 1) % Self::ALL.len() as u32)
}
}

pub struct BackendDesc<'a> {
pub window: &'a dyn Window,
pub width: u32,
pub height: u32,
pub vsync: bool,
pub vsync: VsyncMode,
pub debug: bool,
}

Expand All @@ -363,7 +394,7 @@ pub trait Backend: Send {
fn frames_in_flight(&self) -> usize;
fn surface_size(&self) -> (u32, u32);
fn resize(&mut self, width: u32, height: u32);
fn set_vsync(&mut self, vsync: bool);
fn set_vsync(&mut self, vsync: VsyncMode);

fn create_buffer(&mut self, size: u64, usage: u32, label: &str) -> BufferId;
/// Only valid for `usage::HOST` buffers.
Expand Down
Loading