manually setup redraw system

macos-click-through
keith 5 years ago
parent 49d9dbb15d
commit 1ed9fc9eb9

@ -72,7 +72,7 @@ impl Editor {
RedrawEvent::ModeChange { mode_index } => self.cursor.change_mode(mode_index, &self.defined_styles), RedrawEvent::ModeChange { mode_index } => self.cursor.change_mode(mode_index, &self.defined_styles),
RedrawEvent::BusyStart => self.cursor.enabled = false, RedrawEvent::BusyStart => self.cursor.enabled = false,
RedrawEvent::BusyStop => self.cursor.enabled = true, RedrawEvent::BusyStop => self.cursor.enabled = true,
RedrawEvent::Flush => REDRAW_SCHEDULER.request_redraw(), RedrawEvent::Flush => REDRAW_SCHEDULER.queue_next_frame(),
RedrawEvent::Resize { width, height, .. } => self.resize((width, height)), RedrawEvent::Resize { width, height, .. } => self.resize((width, height)),
RedrawEvent::DefaultColorsSet { colors } => self.default_colors = colors, RedrawEvent::DefaultColorsSet { colors } => self.default_colors = colors,
RedrawEvent::HighlightAttributesDefine { id, style } => { self.defined_styles.insert(id, style); }, RedrawEvent::HighlightAttributesDefine { id, style } => { self.defined_styles.insert(id, style); },

@ -1,44 +1,81 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::Instant; use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Duration, Instant};
use std::thread;
use skulpin::winit::window::Window; use skulpin::winit::window::Window;
use tokio::runtime::Runtime;
use tokio::time::{Instant as TokioInstant, delay_until};
lazy_static! { lazy_static! {
pub static ref REDRAW_SCHEDULER: RedrawScheduler = RedrawScheduler::new(); pub static ref REDRAW_SCHEDULER: RedrawScheduler = RedrawScheduler::new();
} }
pub struct RedrawScheduler { pub struct RedrawScheduler {
runtime: Runtime, window: Mutex<Option<Arc<Window>>>, // Would prefer not to have to lock this every time.
window: Mutex<Option<Arc<Window>>> // Swap to some atomic type frame_queued: AtomicBool,
scheduled_frame: Mutex<Option<Instant>>
}
pub fn redraw_loop() {
thread::spawn(|| {
loop {
let frame_start = Instant::now();
let request_redraw = {
if REDRAW_SCHEDULER.frame_queued.load(Ordering::Relaxed) {
REDRAW_SCHEDULER.frame_queued.store(false, Ordering::Relaxed);
true
} else {
let mut next_scheduled_frame = REDRAW_SCHEDULER.scheduled_frame.lock().unwrap();
if let Some(scheduled_frame) = next_scheduled_frame.clone() {
if scheduled_frame < frame_start {
*next_scheduled_frame = None;
true
} else {
false
}
} else {
false
}
}
};
if request_redraw {
let window = REDRAW_SCHEDULER.window.lock().unwrap();
if let Some(window) = &*window {
window.request_redraw();
}
}
if let Some(time_to_sleep) = Duration::from_secs_f32(1.0 / 60.0).checked_sub(Instant::now() - frame_start) {
thread::sleep(time_to_sleep)
}
}
});
} }
impl RedrawScheduler { impl RedrawScheduler {
pub fn new() -> RedrawScheduler { pub fn new() -> RedrawScheduler {
redraw_loop();
RedrawScheduler { RedrawScheduler {
runtime: Runtime::new().unwrap(), window: Mutex::new(None),
window: Mutex::new(None) frame_queued: AtomicBool::new(false),
scheduled_frame: Mutex::new(None)
} }
} }
pub fn schedule(&self, time: Instant) { pub fn schedule(&self, new_scheduled: Instant) {
let window = { let mut scheduled_frame = self.scheduled_frame.lock().unwrap();
self.window.lock().unwrap().clone() if let Some(previous_scheduled) = scheduled_frame.clone() {
}; if new_scheduled < previous_scheduled {
*scheduled_frame = Some(new_scheduled);
if let Some(window) = window { }
self.runtime.spawn(async move { } else {
delay_until(TokioInstant::from_std(time)).await; *scheduled_frame = Some(new_scheduled);
window.request_redraw();
});
} }
} }
pub fn request_redraw(&self) { pub fn queue_next_frame(&self) {
if let Some(window) = self.window.lock().unwrap().as_ref() { self.frame_queued.store(true, Ordering::Relaxed);
window.request_redraw();
}
} }
pub fn set_window(&self, window: &Arc<Window>){ pub fn set_window(&self, window: &Arc<Window>){

@ -4,6 +4,7 @@ use skulpin::skia_safe::{Canvas, Paint, Path, Point};
use crate::renderer::CachingShaper; use crate::renderer::CachingShaper;
use crate::editor::{EDITOR, Colors, Cursor, CursorShape}; use crate::editor::{EDITOR, Colors, Cursor, CursorShape};
use crate::redraw_scheduler::REDRAW_SCHEDULER;
const AVERAGE_MOTION_PERCENTAGE: f32 = 0.7; const AVERAGE_MOTION_PERCENTAGE: f32 = 0.7;
const MOTION_PERCENTAGE_SPREAD: f32 = 0.5; const MOTION_PERCENTAGE_SPREAD: f32 = 0.5;
@ -33,7 +34,7 @@ impl BlinkStatus {
} }
} }
pub fn update_status(&mut self, new_cursor: &Cursor) -> (bool, Option<Instant>) { pub fn update_status(&mut self, new_cursor: &Cursor) -> bool {
if self.previous_cursor.is_none() || new_cursor != self.previous_cursor.as_ref().unwrap() { if self.previous_cursor.is_none() || new_cursor != self.previous_cursor.as_ref().unwrap() {
self.previous_cursor = Some(new_cursor.clone()); self.previous_cursor = Some(new_cursor.clone());
self.last_transition = Instant::now(); self.last_transition = Instant::now();
@ -47,7 +48,7 @@ impl BlinkStatus {
if new_cursor.blinkwait == Some(0) || if new_cursor.blinkwait == Some(0) ||
new_cursor.blinkoff == Some(0) || new_cursor.blinkoff == Some(0) ||
new_cursor.blinkon == Some(0) { new_cursor.blinkon == Some(0) {
return (true, None); return true;
} }
let delay = match self.state { let delay = match self.state {
@ -65,17 +66,20 @@ impl BlinkStatus {
self.last_transition = Instant::now(); self.last_transition = Instant::now();
} }
( let scheduled_frame = (match self.state {
match self.state { BlinkState::Waiting => new_cursor.blinkwait,
BlinkState::Waiting | BlinkState::Off => false, BlinkState::Off => new_cursor.blinkoff,
BlinkState::On => true BlinkState::On => new_cursor.blinkon
}, }).map(|delay| self.last_transition + Duration::from_millis(delay));
(match self.state {
BlinkState::Waiting => new_cursor.blinkwait, if let Some(scheduled_frame) = scheduled_frame {
BlinkState::Off => new_cursor.blinkoff, REDRAW_SCHEDULER.schedule(scheduled_frame);
BlinkState::On => new_cursor.blinkon }
}).map(|delay| self.last_transition + Duration::from_millis(delay))
) match self.state {
BlinkState::Waiting | BlinkState::Off => false,
BlinkState::On => true
}
} }
} }
@ -171,8 +175,8 @@ impl CursorRenderer {
cursor: Cursor, default_colors: &Colors, cursor: Cursor, default_colors: &Colors,
font_width: f32, font_height: f32, font_width: f32, font_height: f32,
paint: &mut Paint, shaper: &mut CachingShaper, paint: &mut Paint, shaper: &mut CachingShaper,
canvas: &mut Canvas) -> (bool, Option<Instant>) { canvas: &mut Canvas) {
let (render, scheduled_update) = self.blink_status.update_status(&cursor); let render = self.blink_status.update_status(&cursor);
self.previous_position = { self.previous_position = {
let editor = EDITOR.lock().unwrap(); let editor = EDITOR.lock().unwrap();
@ -224,6 +228,10 @@ impl CursorRenderer {
} }
} }
if animating {
REDRAW_SCHEDULER.queue_next_frame();
}
if cursor.enabled && render { if cursor.enabled && render {
// Draw Background // Draw Background
paint.set_color(cursor.background(&default_colors).to_color()); paint.set_color(cursor.background(&default_colors).to_color());
@ -240,7 +248,6 @@ impl CursorRenderer {
// Draw foreground // Draw foreground
paint.set_color(cursor.foreground(&default_colors).to_color()); paint.set_color(cursor.foreground(&default_colors).to_color());
let editor = EDITOR.lock().unwrap();
canvas.save(); canvas.save();
canvas.clip_path(&path, None, Some(false)); canvas.clip_path(&path, None, Some(false));
@ -250,7 +257,5 @@ impl CursorRenderer {
} }
canvas.restore(); canvas.restore();
} }
(animating, scheduled_update)
} }
} }

@ -1,5 +1,3 @@
use std::time::Instant;
use skulpin::CoordinateSystemHelper; use skulpin::CoordinateSystemHelper;
use skulpin::skia_safe::{Canvas, Paint, Surface, Budgeted, Rect, colors}; use skulpin::skia_safe::{Canvas, Paint, Surface, Budgeted, Rect, colors};
use skulpin::skia_safe::gpu::SurfaceOrigin; use skulpin::skia_safe::gpu::SurfaceOrigin;
@ -13,13 +11,6 @@ pub use caching_shaper::CachingShaper;
use cursor_renderer::CursorRenderer; use cursor_renderer::CursorRenderer;
use crate::editor::{EDITOR, Style, Colors}; use crate::editor::{EDITOR, Style, Colors};
#[derive(new)]
pub struct DrawResult {
pub is_animating: bool,
pub font_changed: bool,
pub scheduled_update: Option<Instant>
}
pub struct Renderer { pub struct Renderer {
surface: Option<Surface>, surface: Option<Surface>,
paint: Paint, paint: Paint,
@ -105,7 +96,7 @@ impl Renderer {
canvas.restore(); canvas.restore();
} }
pub fn draw(&mut self, gpu_canvas: &mut Canvas, coordinate_system_helper: &CoordinateSystemHelper) -> DrawResult { pub fn draw(&mut self, gpu_canvas: &mut Canvas, coordinate_system_helper: &CoordinateSystemHelper) -> bool {
let ((draw_commands, should_clear), default_colors, cursor, font_name, font_size) = { let ((draw_commands, should_clear), default_colors, cursor, font_name, font_size) = {
let mut editor = EDITOR.lock().unwrap(); let mut editor = EDITOR.lock().unwrap();
( (
@ -156,12 +147,12 @@ impl Renderer {
self.surface = Some(surface); self.surface = Some(surface);
let (cursor_animating, scheduled_cursor_update) = self.cursor_renderer.draw( self.cursor_renderer.draw(
cursor, &default_colors, cursor, &default_colors,
self.font_width, self.font_height, self.font_width, self.font_height,
&mut self.paint, &mut self.shaper, &mut self.paint, &mut self.shaper,
gpu_canvas); gpu_canvas);
DrawResult::new(!draw_commands.is_empty() || cursor_animating, font_changed, scheduled_cursor_update) font_changed
} }
} }

@ -1,5 +1,5 @@
use std::sync::{Arc, Mutex}; use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::Instant;
use image::{load_from_memory, GenericImageView, Pixel}; use image::{load_from_memory, GenericImageView, Pixel};
use skulpin::{CoordinateSystem, RendererBuilder, PresentMode}; use skulpin::{CoordinateSystem, RendererBuilder, PresentMode};
@ -7,9 +7,9 @@ use skulpin::skia_safe::icu;
use skulpin::winit::dpi::LogicalSize; use skulpin::winit::dpi::LogicalSize;
use skulpin::winit::event::{ElementState, Event, MouseScrollDelta, StartCause, WindowEvent}; use skulpin::winit::event::{ElementState, Event, MouseScrollDelta, StartCause, WindowEvent};
use skulpin::winit::event_loop::{ControlFlow, EventLoop}; use skulpin::winit::event_loop::{ControlFlow, EventLoop};
use skulpin::winit::window::{Icon, Window, WindowBuilder}; use skulpin::winit::window::{Icon, WindowBuilder};
use crate::bridge::{construct_keybinding_string, BRIDGE, Bridge, UiCommand}; use crate::bridge::{construct_keybinding_string, BRIDGE, UiCommand};
use crate::renderer::Renderer; use crate::renderer::Renderer;
use crate::redraw_scheduler::REDRAW_SCHEDULER; use crate::redraw_scheduler::REDRAW_SCHEDULER;
use crate::INITIAL_DIMENSIONS; use crate::INITIAL_DIMENSIONS;
@ -18,8 +18,6 @@ use crate::INITIAL_DIMENSIONS;
#[folder = "assets/"] #[folder = "assets/"]
struct Asset; struct Asset;
const EXTRA_LIVE_FRAMES: usize = 10;
fn handle_new_grid_size(new_size: LogicalSize, renderer: &Renderer) { fn handle_new_grid_size(new_size: LogicalSize, renderer: &Renderer) {
if new_size.width > 0.0 && new_size.height > 0.0 { if new_size.width > 0.0 && new_size.height > 0.0 {
let new_width = ((new_size.width + 1.0) as f32 / renderer.font_width) as u64; let new_width = ((new_size.width + 1.0) as f32 / renderer.font_width) as u64;
@ -72,14 +70,9 @@ pub fn ui_loop() {
let mut mouse_down = false; let mut mouse_down = false;
let mut mouse_pos = (0, 0); let mut mouse_pos = (0, 0);
let mut live_frames = 0;
let mut frame_start = Instant::now();
event_loop.run(move |event, _window_target, control_flow| { event_loop.run(move |event, _window_target, control_flow| {
match event { match event {
Event::NewEvents(StartCause::Init) | Event::NewEvents(StartCause::Init) => window.request_redraw(),
Event::NewEvents(StartCause::ResumeTimeReached { .. }) => {
window.request_redraw()
},
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
@ -90,7 +83,7 @@ pub fn ui_loop() {
event: WindowEvent::Resized(new_size), event: WindowEvent::Resized(new_size),
.. ..
} => { } => {
handle_new_grid_size(window.inner_size(), &renderer) handle_new_grid_size(new_size, &renderer)
}, },
Event::WindowEvent { Event::WindowEvent {
@ -174,30 +167,12 @@ pub fn ui_loop() {
} }
Event::RedrawRequested { .. } => { Event::RedrawRequested { .. } => {
frame_start = Instant::now();
if let Err(e) = skulpin_renderer.draw(&window, |canvas, coordinate_system_helper| { if let Err(e) = skulpin_renderer.draw(&window, |canvas, coordinate_system_helper| {
let draw_result = renderer.draw(canvas, coordinate_system_helper); if renderer.draw(canvas, coordinate_system_helper) {
if draw_result.is_animating {
live_frames = EXTRA_LIVE_FRAMES;
} else {
if live_frames > 0 {
live_frames = live_frames - 1;
}
}
if draw_result.font_changed {
handle_new_grid_size(window.inner_size(), &renderer) handle_new_grid_size(window.inner_size(), &renderer)
} }
if live_frames > 0 { *control_flow = ControlFlow::Wait;
*control_flow = ControlFlow::WaitUntil(frame_start + Duration::from_secs_f32(1.0 / 60.0));
} else {
*control_flow = ControlFlow::Wait;
}
if let Some(scheduled_update) = draw_result.scheduled_update {
REDRAW_SCHEDULER.schedule(scheduled_update);
}
}) { }) {
println!("Error during draw: {:?}", e); println!("Error during draw: {:?}", e);
*control_flow = ControlFlow::Exit *control_flow = ControlFlow::Exit

Loading…
Cancel
Save