From eac787be3fbcaf2096f697bb3f783665adc925a0 Mon Sep 17 00:00:00 2001 From: popertots Date: Fri, 20 Mar 2026 17:10:54 +0000 Subject: [PATCH] feat: runtime-configurable VSync via config.toml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [display] vsync = "mailbox" # "vsync" | "mailbox" | "uncapped" VsyncMode enum with custom Deserialize — accepts string values in toml. apply_vsync_setting system reads GameConfig on every frame, updates Window.present_mode immediately on change. No restart needed. --- config.toml | 3 +++ src/config.rs | 31 +++++++++++++++++++++++++++++++ src/world/mod.rs | 18 +++++++++++++++++- 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/config.toml b/config.toml index fbb1c42..419b4cd 100644 --- a/config.toml +++ b/config.toml @@ -1,5 +1,8 @@ initial_chunk_radius = 15 +[display] +vsync = "mailbox" + [spawn_counts] dorfs = 5 pigs = 5 diff --git a/src/config.rs b/src/config.rs index 9c5199d..64fdec9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,6 +8,37 @@ use std::sync::OnceLock; pub struct GameConfig { pub initial_chunk_radius: i32, pub spawn_counts: SpawnCounts, + #[serde(default)] + pub display: DisplaySettings, +} + +#[derive(Debug, Deserialize, Clone, Default)] +pub struct DisplaySettings { + #[serde(default)] + pub vsync: VsyncMode, +} + +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +pub enum VsyncMode { + #[default] + Vsync, + Mailbox, + Uncapped, +} + +impl<'de> Deserialize<'de> for VsyncMode { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + match s.as_str() { + "vsync" => Ok(VsyncMode::Vsync), + "mailbox" => Ok(VsyncMode::Mailbox), + "uncapped" => Ok(VsyncMode::Uncapped), + _ => Ok(VsyncMode::Vsync), + } + } } #[derive(Debug, Deserialize, Clone)] diff --git a/src/world/mod.rs b/src/world/mod.rs index 2238158..4be608b 100644 --- a/src/world/mod.rs +++ b/src/world/mod.rs @@ -1,6 +1,6 @@ use crate::{ camera, - config::GameConfig, + config::{GameConfig, VsyncMode}, world::generation::{ generate_chunk_fauna, generate_chunk_foliage, generate_chunk_forrestry, generate_chunk_weathering_and_precipitation, apply_terrain_blobs, spawn_terrain_tasks, @@ -18,6 +18,21 @@ pub use chunks::management::*; pub use textures::management::*; pub use tiles::{prefabs::*, rendering::*, visibility::*}; +pub fn apply_vsync_setting( + config: Res, + mut windows: Query<&mut Window>, +) { + if config.is_changed() { + if let Ok(mut window) = windows.single_mut() { + window.present_mode = match config.display.vsync { + VsyncMode::Vsync => bevy::window::PresentMode::AutoVsync, + VsyncMode::Mailbox => bevy::window::PresentMode::Mailbox, + VsyncMode::Uncapped => bevy::window::PresentMode::AutoNoVsync, + }; + } + } +} + pub struct WorldPlugin; impl Plugin for WorldPlugin { @@ -58,6 +73,7 @@ impl Plugin for WorldPlugin { .after(crate::entities::item::item_tile_management_system), tiles::track_benchmark, tiles::render_bench_report_system, + apply_vsync_setting, handle_tile_occlusion_updates, tiles::spawn_tilemap_chunks, tiles::on_camera_z_changed,