update dependencies, add sort of busy loop

This commit is contained in:
Daniel Bulant 2023-10-26 11:30:49 +02:00
parent 7609ab3de3
commit 8d6cecdfb1
4 changed files with 679 additions and 250 deletions

814
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -7,9 +7,9 @@ edition = "2021"
[dependencies] [dependencies]
femtovg = "0.7.1" femtovg = "0.7.1"
glutin = "0.30.10" glutin = "0.31.0"
raw-window-handle = "0.5.0" raw-window-handle = "0.5.0"
winit = { version = "0.28.7", default-features = false } winit = { version = "0.29.2" }
glutin-winit = "0.3.0" glutin-winit = "0.4.2"
taffy = "0.3.16" taffy = "0.3.16"
weak-table = "0.3.2" weak-table = "0.3.2"

View file

@ -1,8 +1,8 @@
use std::ops::{AddAssign, Add, SubAssign, Sub}; use std::ops::{AddAssign, Add, SubAssign, Sub};
use taffy::{prelude::Size, style::Dimension, geometry::Point}; use taffy::{prelude::Size, style::Dimension, geometry::Point};
use winit::event::ElementState; use winit::{event::ElementState, keyboard::{Key, KeyCode, ModifiersState}};
pub use winit::event::{TouchPhase, MouseScrollDelta, DeviceId, ModifiersState, VirtualKeyCode, ScanCode, MouseButton}; pub use winit::event::{TouchPhase, MouseScrollDelta, DeviceId, Modifiers, MouseButton};
use crate::SharedNode; use crate::SharedNode;
pub mod handler; pub mod handler;
@ -62,16 +62,16 @@ pub enum InnerEvent {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct KeyboardEvent { pub struct KeyboardEvent {
/// Logical location ("it's effect") of the key /// Logical location ("it's effect") of the key
pub key: Option<VirtualKeyCode>, pub key: Option<Key>,
/// Physical location of the key /// Physical location of the key
pub code: ScanCode, pub code: KeyCode,
// altKey: bool, // altKey: bool,
// ctrlKey: bool, // ctrlKey: bool,
// metaKey: bool, // metaKey: bool,
// shiftKey: bool, // shiftKey: bool,
/// modifier keys pressed (alt, ctrl, shift or meta/logo/windows) /// modifier keys pressed (alt, ctrl, shift or meta/logo/windows)
pub modifiers: ModifiersState, pub modifiers: Modifiers,
// repeat: bool, // repeat: bool,
// char_code: u32, // char_code: u32,
@ -94,7 +94,7 @@ pub struct MouseEvent {
// metaKey: bool, // metaKey: bool,
// shiftKey: bool, // shiftKey: bool,
/// modifier keys pressed (alt, ctrl, shift or meta/logo/windows) /// modifier keys pressed (alt, ctrl, shift or meta/logo/windows)
pub modifiers: ModifiersState, pub modifiers: Modifiers,
/// The location of the mouse relative to window /// The location of the mouse relative to window
pub client: Location, pub client: Location,
@ -194,19 +194,19 @@ impl Into<Size<Dimension>> for Location {
impl MouseEvent { impl MouseEvent {
/// Returns `true` if the shift key is pressed. /// Returns `true` if the shift key is pressed.
pub fn shift(&self) -> bool { pub fn shift(&self) -> bool {
self.modifiers.intersects(ModifiersState::SHIFT) self.modifiers.state().intersects(ModifiersState::SHIFT)
} }
/// Returns `true` if the control key is pressed. /// Returns `true` if the control key is pressed.
pub fn ctrl(&self) -> bool { pub fn ctrl(&self) -> bool {
self.modifiers.intersects(ModifiersState::CTRL) self.modifiers.state().intersects(ModifiersState::CONTROL)
} }
/// Returns `true` if the alt key is pressed. /// Returns `true` if the alt key is pressed.
pub fn alt(&self) -> bool { pub fn alt(&self) -> bool {
self.modifiers.intersects(ModifiersState::ALT) self.modifiers.state().intersects(ModifiersState::ALT)
} }
/// Returns `true` if the logo key is pressed. /// Returns `true` if the logo key is pressed.
pub fn logo(&self) -> bool { pub fn logo(&self) -> bool {
self.modifiers.intersects(ModifiersState::LOGO) self.modifiers.state().intersects(ModifiersState::SUPER)
} }
pub fn button_to_buttons(button: MouseButton) -> u8 { pub fn button_to_buttons(button: MouseButton) -> u8 {
@ -214,7 +214,9 @@ impl MouseEvent {
MouseButton::Left => 1, MouseButton::Left => 1,
MouseButton::Right => 2, MouseButton::Right => 2,
MouseButton::Middle => 4, MouseButton::Middle => 4,
MouseButton::Other(n) => 1 << n MouseButton::Back => 8,
MouseButton::Forward => 16,
MouseButton::Other(n) => 1 << (n + 4)
} }
} }
} }

View file

@ -2,6 +2,7 @@ use std::collections::HashMap;
use std::num::NonZeroU32; use std::num::NonZeroU32;
use std::ops::Deref; use std::ops::Deref;
use std::sync::{Arc, RwLock, Weak}; use std::sync::{Arc, RwLock, Weak};
use std::time::Duration;
use events::{Location, MouseValue, NodeEvent, MouseEvent}; use events::{Location, MouseValue, NodeEvent, MouseEvent};
use femtovg::renderer::OpenGl; use femtovg::renderer::OpenGl;
@ -11,7 +12,7 @@ use glutin::{context::PossiblyCurrentContext, display::Display};
use glutin_winit::DisplayBuilder; use glutin_winit::DisplayBuilder;
use nodes::{get_element_at, run_event_handlers, run_single_event_handlers}; use nodes::{get_element_at, run_event_handlers, run_single_event_handlers};
use raw_window_handle::HasRawWindowHandle; use raw_window_handle::HasRawWindowHandle;
use winit::event::{Event, WindowEvent, ModifiersState, DeviceId}; use winit::event::{Event, WindowEvent, Modifiers, DeviceId};
use winit::event_loop::{ControlFlow, EventLoop}; use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder; use winit::window::WindowBuilder;
use winit::{dpi::PhysicalSize, window::Window}; use winit::{dpi::PhysicalSize, window::Window};
@ -40,10 +41,14 @@ type WeakNode = Weak<RwLock<dyn Node>>;
type NodePtr = Option<Vec<WeakNode>>; type NodePtr = Option<Vec<WeakNode>>;
type NodeLayoutMap = PtrWeakKeyHashMap<Weak<RwLock<dyn Node>>, taffy::node::Node>; type NodeLayoutMap = PtrWeakKeyHashMap<Weak<RwLock<dyn Node>>, taffy::node::Node>;
pub fn run_event_loop(root_node: SharedNode) -> ! { pub fn run_event_loop(root_node: SharedNode) -> () {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new().unwrap();
let (buffer_context, gl_display, window, surface) = create_window(&event_loop); let (buffer_context, gl_display, window, surface) = create_window(&event_loop);
if let Err(res) = surface.set_swap_interval(&buffer_context, glutin::surface::SwapInterval::Wait(NonZeroU32::new(1).unwrap())) {
dbg!("Could not set swap interval (vsync)", res);
}
let renderer = unsafe { OpenGl::new_from_function_cstr(|s| gl_display.get_proc_address(s) as *const _) } let renderer = unsafe { OpenGl::new_from_function_cstr(|s| gl_display.get_proc_address(s) as *const _) }
.expect("Cannot create renderer"); .expect("Cannot create renderer");
@ -72,11 +77,11 @@ pub fn run_event_loop(root_node: SharedNode) -> ! {
let mut should_recompute = true; let mut should_recompute = true;
let mut modifiers = ModifiersState::default(); let mut modifiers = Modifiers::default();
let focus_path: Option<Vec<WeakNode>> = None; let focus_path: Option<Vec<WeakNode>> = None;
let mut mouse_values: HashMap<DeviceId, MouseValue> = HashMap::new(); let mut mouse_values: HashMap<DeviceId, MouseValue> = HashMap::new();
event_loop.run(move |event, _target, control_flow| match event { event_loop.run(move |event, target| match event {
Event::WindowEvent { event, .. } => match event { Event::WindowEvent { event, .. } => match event {
WindowEvent::MouseWheel { device_id, delta, phase, .. } => {}, WindowEvent::MouseWheel { device_id, delta, phase, .. } => {},
WindowEvent::CursorMoved { device_id, position, .. } => { WindowEvent::CursorMoved { device_id, position, .. } => {
@ -162,7 +167,7 @@ pub fn run_event_loop(root_node: SharedNode) -> ! {
}; };
}, },
WindowEvent::ModifiersChanged(new_modifiers) => { modifiers = new_modifiers; }, WindowEvent::ModifiersChanged(new_modifiers) => { modifiers = new_modifiers; },
WindowEvent::KeyboardInput { device_id, input, is_synthetic } => {}, WindowEvent::KeyboardInput { device_id, event, is_synthetic } => {},
WindowEvent::MouseInput { device_id, state, button, .. } => { WindowEvent::MouseInput { device_id, state, button, .. } => {
let mouse_value = mouse_values.get(&device_id); let mouse_value = mouse_values.get(&device_id);
let mut mouse_value = match mouse_value { let mut mouse_value = match mouse_value {
@ -206,7 +211,7 @@ pub fn run_event_loop(root_node: SharedNode) -> ! {
None => {} None => {}
} }
}, },
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => target.exit(),
WindowEvent::Resized(size) => { WindowEvent::Resized(size) => {
let width: NonZeroU32 = NonZeroU32::new(size.width).unwrap(); let width: NonZeroU32 = NonZeroU32::new(size.width).unwrap();
let height: NonZeroU32 = NonZeroU32::new(size.height).unwrap(); let height: NonZeroU32 = NonZeroU32::new(size.height).unwrap();
@ -218,41 +223,51 @@ pub fn run_event_loop(root_node: SharedNode) -> ! {
window.request_redraw(); window.request_redraw();
should_recompute = true; should_recompute = true;
}, },
WindowEvent::RedrawRequested => {
if should_recompute {
layout_recursively(&root, &mut context);
let src_nodes = context.node_layout.values().map(|v| v.to_owned()).collect::<Vec<_>>();
context.node_layout.remove_expired();
let dst_nodes = context.node_layout.values().map(|v| v.to_owned()).collect::<Vec<_>>();
for src_node in src_nodes {
if !dst_nodes.contains(&src_node) {
context.taffy.remove(src_node).unwrap();
dbg!("Removed node", src_node);
}
}
for (node, taffy_node) in context.node_layout.iter() {
let node = node.read().unwrap();
let node_style = node.style();
context.taffy.set_style(*taffy_node, node_style.layout.to_owned()).unwrap();
}
context.taffy.compute_layout(*context.node_layout.get(&root).unwrap(), Size::MAX_CONTENT).unwrap();
should_recompute = false;
// Additional optimizations could be done here
// - When setting styles, check that the styles aren't the same (taffy doesn't do that and instead always mark it as dirty)
// - taffy seems to always recompute (maybe internally checks dirtyness, I didn't look into it that much)
// - the weakmap dance (src_nodes, dst_nodes) could be avoided by changing the weakmap used
// (weakmap removes keys when you attempt to read them, we could change it so that we could iterate on them and remove them in one go)
// could perhaps be a significant boost regarding memory usage (and performance) during large layout changes
// dbg!("recomputed");
}
// dbg!(&root);
render(&buffer_context, &surface, &window, &mut context, &root);
}
_ => {} _ => {}
}, },
Event::RedrawRequested(_) => { Event::NewEvents(_) => {
if should_recompute { if let Some(monitor) = window.current_monitor() {
layout_recursively(&root, &mut context); if let Some(refresh_rate) = monitor.refresh_rate_millihertz() {
let src_nodes = context.node_layout.values().map(|v| v.to_owned()).collect::<Vec<_>>(); // dbg!(refresh_rate);
context.node_layout.remove_expired(); // some leeway before vsync
let dst_nodes = context.node_layout.values().map(|v| v.to_owned()).collect::<Vec<_>>(); target.set_control_flow(ControlFlow::wait_duration(Duration::from_millis(1000 / refresh_rate as u64 - 100/refresh_rate as u64)));
for src_node in src_nodes { window.request_redraw();
if !dst_nodes.contains(&src_node) {
context.taffy.remove(src_node).unwrap();
dbg!("Removed node", src_node);
}
} }
for (node, taffy_node) in context.node_layout.iter() {
let node = node.read().unwrap();
let node_style = node.style();
context.taffy.set_style(*taffy_node, node_style.layout.to_owned()).unwrap();
}
context.taffy.compute_layout(*context.node_layout.get(&root).unwrap(), Size::MAX_CONTENT).unwrap();
should_recompute = false;
// Additional optimizations could be done here
// - When setting styles, check that the styles aren't the same (taffy doesn't do that and instead always mark it as dirty)
// - taffy seems to always recompute (maybe internally checks dirtyness, I didn't look into it that much)
// - the weakmap dance (src_nodes, dst_nodes) could be avoided by changing the weakmap used
// (weakmap removes keys when you attempt to read them, we could change it so that we could iterate on them and remove them in one go)
// could perhaps be a significant boost regarding memory usage (and performance) during large layout changes
// dbg!("recomputed");
} }
// dbg!(&root);
render(&buffer_context, &surface, &window, &mut context, &root);
}, },
// In the future, window should be created after resuming from suspend (for android support) // In the future, window should be created after resuming from suspend (for android support)
_ => {} _ => {}
}) }).unwrap();
} }
/// I have no idea if there's a better way to do this in rust... /// I have no idea if there's a better way to do this in rust...