fix animations, fix SlotMap dirty widget panic, set gui scale, set dash to 1080p

[skip ci]
This commit is contained in:
Aleksander
2025-12-26 15:02:08 +01:00
parent dead3f417c
commit 1364a5cb2e
12 changed files with 37 additions and 110 deletions

View File

@@ -125,8 +125,7 @@ impl Frontend {
let id_label_time = state.get_widget_id("label_time")?; let id_label_time = state.get_widget_id("label_time")?;
let id_rect_content = state.get_widget_id("rect_content")?; let id_rect_content = state.get_widget_id("rect_content")?;
let mut timestep = Timestep::new(); let timestep = Timestep::new(60.0);
timestep.set_tps(30.0); // 30 ticks per second
let mut frontend = Self { let mut frontend = Self {
layout, layout,

View File

@@ -47,7 +47,7 @@ impl Drop for MountedToast {
} }
} }
const TOAST_DURATION_TICKS: u32 = 90; const TOAST_DURATION_TICKS: u32 = 150;
impl ToastManager { impl ToastManager {
pub fn new() -> Self { pub fn new() -> Self {
@@ -130,7 +130,7 @@ impl ToastManager {
// show-up animation // show-up animation
layout.animations.add(Animation::new( layout.animations.add(Animation::new(
rect.id, rect.id,
(120.0 * globals.defaults.animation_mult) as u32, (TOAST_DURATION_TICKS as f32 * globals.defaults.animation_mult) as u32,
AnimationEasing::Linear, AnimationEasing::Linear,
Box::new(move |common, data| { Box::new(move |common, data| {
let pos_showup = AnimationEasing::OutQuint.interpolate((data.pos * 4.0).min(1.0)); let pos_showup = AnimationEasing::OutQuint.interpolate((data.pos * 4.0).min(1.0));

View File

@@ -1,7 +1,6 @@
use glam::{Vec2, vec2}; use glam::{Vec2, vec2};
use std::sync::Arc; use std::sync::Arc;
use testbed::{Testbed, testbed_any::TestbedAny}; use testbed::{Testbed, testbed_any::TestbedAny};
use timestep::Timestep;
use tracing_subscriber::EnvFilter; use tracing_subscriber::EnvFilter;
use tracing_subscriber::filter::LevelFilter; use tracing_subscriber::filter::LevelFilter;
use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::layer::SubscriberExt;
@@ -28,6 +27,7 @@ use winit::{
event_loop::ControlFlow, event_loop::ControlFlow,
keyboard::{KeyCode, PhysicalKey}, keyboard::{KeyCode, PhysicalKey},
}; };
use wlx_common::timestep::Timestep;
use crate::{ use crate::{
rate_limiter::RateLimiter, rate_limiter::RateLimiter,
@@ -40,7 +40,6 @@ mod assets;
mod profiler; mod profiler;
mod rate_limiter; mod rate_limiter;
mod testbed; mod testbed;
mod timestep;
mod vulkan; mod vulkan;
fn init_logging() { fn init_logging() {
@@ -114,8 +113,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut profiler = profiler::Profiler::new(1000); let mut profiler = profiler::Profiler::new(1000);
let mut frame_index: u64 = 0; let mut frame_index: u64 = 0;
let mut timestep = Timestep::new(); let mut timestep = Timestep::new(60.0);
timestep.set_tps(60.0);
let mut limiter = RateLimiter::new(); let mut limiter = RateLimiter::new();

View File

@@ -1,4 +1,4 @@
use crate::timestep::get_micros; use wlx_common::timestep::get_micros;
pub struct Profiler { pub struct Profiler {
interval_us: u64, interval_us: u64,

View File

@@ -1,4 +1,4 @@
use crate::timestep::get_micros; use wlx_common::timestep::get_micros;
#[derive(Default)] #[derive(Default)]
pub struct RateLimiter { pub struct RateLimiter {

View File

@@ -1,70 +0,0 @@
use std::{sync::LazyLock, time::Instant};
static TIME_START: LazyLock<Instant> = LazyLock::new(Instant::now);
pub fn get_micros() -> u64 {
TIME_START.elapsed().as_micros() as u64
}
#[derive(Default)]
pub struct Timestep {
current_time_us: u64,
accumulator: f32,
time_micros: u64,
ticks: u32,
speed: f32,
pub alpha: f32,
delta: f32,
loopnum: u8,
}
impl Timestep {
pub fn new() -> Timestep {
let mut timestep = Timestep {
speed: 1.0,
..Default::default()
};
timestep.reset();
timestep
}
fn calculate_alpha(&mut self) {
self.alpha = (self.accumulator / self.delta).clamp(0.0, 1.0);
}
pub fn set_tps(&mut self, tps: f32) {
self.delta = 1000.0 / tps;
}
pub fn reset(&mut self) {
self.current_time_us = get_micros();
self.accumulator = 0.0;
}
pub fn on_tick(&mut self) -> bool {
let newtime = get_micros();
let frametime = newtime - self.current_time_us;
self.time_micros += frametime;
self.current_time_us = newtime;
self.accumulator += frametime as f32 * self.speed / 1000.0;
self.calculate_alpha();
if self.accumulator >= self.delta {
self.accumulator -= self.delta;
self.loopnum += 1;
self.ticks += 1;
if self.loopnum > 5 {
// cannot keep up!
self.loopnum = 0;
self.accumulator = 0.0;
return false;
}
true
} else {
self.loopnum = 0;
false
}
}
}

View File

@@ -18,11 +18,11 @@ use crate::{
util, util,
}, },
widget::{ widget::{
ConstructEssentials, EventResult,
div::WidgetDiv, div::WidgetDiv,
label::{WidgetLabel, WidgetLabelParams}, label::{WidgetLabel, WidgetLabelParams},
rectangle::{WidgetRectangle, WidgetRectangleParams}, rectangle::{WidgetRectangle, WidgetRectangleParams},
util::WLength, util::WLength,
ConstructEssentials, EventResult,
}, },
}; };
@@ -224,7 +224,7 @@ impl State {
return; // nothing changed visually return; // nothing changed visually
} }
common.alterables.mark_dirty(data.slider_handle_node_id); common.alterables.mark_dirty(data.slider_handle_id);
common.alterables.mark_redraw(); common.alterables.mark_redraw();
if let Some(slider_text_id) = data.slider_text_id if let Some(slider_text_id) = data.slider_text_id

View File

@@ -104,7 +104,7 @@ pub enum StyleSetRequest {
// alterables which will be dispatched in the next loop iteration phase // alterables which will be dispatched in the next loop iteration phase
#[derive(Default)] #[derive(Default)]
pub struct EventAlterables { pub struct EventAlterables {
pub dirty_nodes: Vec<taffy::NodeId>, pub dirty_widgets: Vec<WidgetID>,
pub style_set_requests: Vec<(WidgetID, StyleSetRequest)>, pub style_set_requests: Vec<(WidgetID, StyleSetRequest)>,
pub animations: Vec<animation::Animation>, pub animations: Vec<animation::Animation>,
pub widgets_to_tick: HashSet<WidgetID>, // widgets which needs to be ticked in the next `Layout::update()` fn pub widgets_to_tick: HashSet<WidgetID>, // widgets which needs to be ticked in the next `Layout::update()` fn
@@ -125,8 +125,8 @@ impl EventAlterables {
self.style_set_requests.push((widget_id, request)); self.style_set_requests.push((widget_id, request));
} }
pub fn mark_dirty(&mut self, node_id: taffy::NodeId) { pub fn mark_dirty(&mut self, widget_id: WidgetID) {
self.dirty_nodes.push(node_id); self.dirty_widgets.push(widget_id);
} }
pub fn mark_tick(&mut self, widget_id: WidgetID) { pub fn mark_tick(&mut self, widget_id: WidgetID) {
@@ -154,9 +154,7 @@ impl CallbackDataCommon<'_> {
// helper function // helper function
pub fn mark_widget_dirty(&mut self, id: WidgetID) { pub fn mark_widget_dirty(&mut self, id: WidgetID) {
if let Some(node_id) = self.state.nodes.get(id) { self.alterables.mark_dirty(id);
self.alterables.mark_dirty(*node_id);
}
self.alterables.mark_redraw(); self.alterables.mark_redraw();
} }
} }

View File

@@ -683,8 +683,10 @@ impl Layout {
self.process_tasks()?; self.process_tasks()?;
for node in alterables.dirty_nodes { for dirty_widget_id in alterables.dirty_widgets {
self.state.tree.mark_dirty(node)?; if let Some(dirty_node_id) = self.state.nodes.get(dirty_widget_id) {
self.state.tree.mark_dirty(*dirty_node_id)?;
}
} }
if alterables.needs_redraw { if alterables.needs_redraw {

View File

@@ -18,13 +18,14 @@ pub struct Timestep {
} }
impl Timestep { impl Timestep {
pub fn new() -> Self { pub fn new(ticks_per_second: f32) -> Self {
let mut timestep = Self { let mut timestep = Self {
speed: 1.0, speed: 1.0,
..Default::default() ..Default::default()
}; };
timestep.reset(); timestep.reset();
timestep.set_tps(ticks_per_second);
timestep timestep
} }

View File

@@ -160,8 +160,7 @@ impl<S: 'static> GuiPanel<S> {
} }
let context = WguiContext::new(&mut app.wgui_shared, 1.0)?; let context = WguiContext::new(&mut app.wgui_shared, 1.0)?;
let mut timestep = Timestep::new(); let timestep = Timestep::new(60.0);
timestep.set_tps(60.0);
Ok(Self { Ok(Self {
layout, layout,
@@ -192,8 +191,7 @@ impl<S: 'static> GuiPanel<S> {
}, },
)?; )?;
let context = WguiContext::new(&mut app.wgui_shared, 1.0)?; let context = WguiContext::new(&mut app.wgui_shared, 1.0)?;
let mut timestep = Timestep::new(); let timestep = Timestep::new(60.0);
timestep.set_tps(60.0);
Ok(Self { Ok(Self {
layout, layout,

View File

@@ -37,7 +37,7 @@ use crate::{
pub const DASH_NAME: &str = "Dashboard"; pub const DASH_NAME: &str = "Dashboard";
const DASH_RES_U32A: [u32; 2] = [1280, 720]; const DASH_RES_U32A: [u32; 2] = [1920, 1080];
const DASH_RES_VEC2: Vec2 = vec2(DASH_RES_U32A[0] as _, DASH_RES_U32A[1] as _); const DASH_RES_VEC2: Vec2 = vec2(DASH_RES_U32A[0] as _, DASH_RES_U32A[1] as _);
//FIXME: replace with proper impl //FIXME: replace with proper impl
@@ -86,6 +86,8 @@ pub struct DashFrontend {
context: WguiContext, context: WguiContext,
} }
const GUI_SCALE: f32 = 2.0;
impl DashFrontend { impl DashFrontend {
fn new(app: &mut AppState) -> anyhow::Result<Self> { fn new(app: &mut AppState) -> anyhow::Result<Self> {
let settings = SimpleSettingsIO::new(); let settings = SimpleSettingsIO::new();
@@ -100,15 +102,18 @@ impl DashFrontend {
inner: frontend, inner: frontend,
initialized: false, initialized: false,
interaction_transform: None, interaction_transform: None,
timestep: Timestep::new(), timestep: Timestep::new(60.0),
has_focus: [false, false], has_focus: [false, false],
context, context,
}) })
} }
fn update(&mut self) -> anyhow::Result<()> { fn update(&mut self, timestep_alpha: f32) -> anyhow::Result<()> {
log::info!("update layout"); self.inner.update(
self.inner.update(DASH_RES_VEC2.x, DASH_RES_VEC2.y, 0.0) DASH_RES_VEC2.x / GUI_SCALE,
DASH_RES_VEC2.y / GUI_SCALE,
timestep_alpha,
)
} }
fn push_event(&mut self, event: &WguiEvent) -> EventResult { fn push_event(&mut self, event: &WguiEvent) -> EventResult {
@@ -125,11 +130,11 @@ impl DashFrontend {
impl OverlayBackend for DashFrontend { impl OverlayBackend for DashFrontend {
fn init(&mut self, app: &mut AppState) -> anyhow::Result<()> { fn init(&mut self, app: &mut AppState) -> anyhow::Result<()> {
self.context self.context
.update_viewport(&mut app.wgui_shared, DASH_RES_U32A, 1.0)?; .update_viewport(&mut app.wgui_shared, DASH_RES_U32A, GUI_SCALE)?;
self.interaction_transform = Some(ui_transform(DASH_RES_U32A)); self.interaction_transform = Some(ui_transform(DASH_RES_U32A));
if self.inner.layout.content_size.x * self.inner.layout.content_size.y != 0.0 { if self.inner.layout.content_size.x * self.inner.layout.content_size.y != 0.0 {
self.update()?; self.update(0.0)?;
self.initialized = true; self.initialized = true;
} }
Ok(()) Ok(())
@@ -150,6 +155,10 @@ impl OverlayBackend for DashFrontend {
self.inner.layout.tick()?; self.inner.layout.tick()?;
} }
if let Err(e) = self.update(self.timestep.alpha) {
log::error!("uncaught exception: {e:?}");
}
Ok(if self.inner.layout.check_toggle_needs_redraw() { Ok(if self.inner.layout.check_toggle_needs_redraw() {
ShouldRender::Should ShouldRender::Should
} else { } else {
@@ -158,14 +167,6 @@ impl OverlayBackend for DashFrontend {
} }
fn render(&mut self, app: &mut AppState, rdr: &mut RenderResources) -> anyhow::Result<()> { fn render(&mut self, app: &mut AppState, rdr: &mut RenderResources) -> anyhow::Result<()> {
self.inner
.layout
.update(DASH_RES_VEC2, self.timestep.alpha)?;
if let Err(e) = self.update() {
log::error!("uncaught exception: {e:?}");
}
let globals = self.inner.layout.state.globals.clone(); // sorry let globals = self.inner.layout.state.globals.clone(); // sorry
let mut globals = globals.get(); let mut globals = globals.get();