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::BusyStart => self.cursor.enabled = false,
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::DefaultColorsSet { colors } => self.default_colors = colors,
RedrawEvent::HighlightAttributesDefine { id, style } => { self.defined_styles.insert(id, style); },

@ -1,44 +1,81 @@
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 tokio::runtime::Runtime;
use tokio::time::{Instant as TokioInstant, delay_until};
lazy_static! {
pub static ref REDRAW_SCHEDULER: RedrawScheduler = RedrawScheduler::new();
}
pub struct RedrawScheduler {
runtime: Runtime,
window: Mutex<Option<Arc<Window>>> // Swap to some atomic type
window: Mutex<Option<Arc<Window>>>, // Would prefer not to have to lock this every time.
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 {
pub fn new() -> RedrawScheduler {
redraw_loop();
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) {
let window = {
self.window.lock().unwrap().clone()
};
if let Some(window) = window {
self.runtime.spawn(async move {
delay_until(TokioInstant::from_std(time)).await;
window.request_redraw();
});
pub fn schedule(&self, new_scheduled: Instant) {
let mut scheduled_frame = self.scheduled_frame.lock().unwrap();
if let Some(previous_scheduled) = scheduled_frame.clone() {
if new_scheduled < previous_scheduled {
*scheduled_frame = Some(new_scheduled);
}
} else {
*scheduled_frame = Some(new_scheduled);
}
}
pub fn request_redraw(&self) {
if let Some(window) = self.window.lock().unwrap().as_ref() {
window.request_redraw();
}
pub fn queue_next_frame(&self) {
self.frame_queued.store(true, Ordering::Relaxed);
}
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::editor::{EDITOR, Colors, Cursor, CursorShape};
use crate::redraw_scheduler::REDRAW_SCHEDULER;
const AVERAGE_MOTION_PERCENTAGE: f32 = 0.7;
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() {
self.previous_cursor = Some(new_cursor.clone());
self.last_transition = Instant::now();
@ -47,7 +48,7 @@ impl BlinkStatus {
if new_cursor.blinkwait == Some(0) ||
new_cursor.blinkoff == Some(0) ||
new_cursor.blinkon == Some(0) {
return (true, None);
return true;
}
let delay = match self.state {
@ -65,17 +66,20 @@ impl BlinkStatus {
self.last_transition = Instant::now();
}
(
match self.state {
BlinkState::Waiting | BlinkState::Off => false,
BlinkState::On => true
},
(match self.state {
BlinkState::Waiting => new_cursor.blinkwait,
BlinkState::Off => new_cursor.blinkoff,
BlinkState::On => new_cursor.blinkon
}).map(|delay| self.last_transition + Duration::from_millis(delay))
)
let scheduled_frame = (match self.state {
BlinkState::Waiting => new_cursor.blinkwait,
BlinkState::Off => new_cursor.blinkoff,
BlinkState::On => new_cursor.blinkon
}).map(|delay| self.last_transition + Duration::from_millis(delay));
if let Some(scheduled_frame) = scheduled_frame {
REDRAW_SCHEDULER.schedule(scheduled_frame);
}
match self.state {
BlinkState::Waiting | BlinkState::Off => false,
BlinkState::On => true
}
}
}
@ -171,8 +175,8 @@ impl CursorRenderer {
cursor: Cursor, default_colors: &Colors,
font_width: f32, font_height: f32,
paint: &mut Paint, shaper: &mut CachingShaper,
canvas: &mut Canvas) -> (bool, Option<Instant>) {
let (render, scheduled_update) = self.blink_status.update_status(&cursor);
canvas: &mut Canvas) {
let render = self.blink_status.update_status(&cursor);
self.previous_position = {
let editor = EDITOR.lock().unwrap();
@ -224,6 +228,10 @@ impl CursorRenderer {
}
}
if animating {
REDRAW_SCHEDULER.queue_next_frame();
}
if cursor.enabled && render {
// Draw Background
paint.set_color(cursor.background(&default_colors).to_color());
@ -240,7 +248,6 @@ impl CursorRenderer {
// Draw foreground
paint.set_color(cursor.foreground(&default_colors).to_color());
let editor = EDITOR.lock().unwrap();
canvas.save();
canvas.clip_path(&path, None, Some(false));
@ -250,7 +257,5 @@ impl CursorRenderer {
}
canvas.restore();
}
(animating, scheduled_update)
}
}

@ -1,5 +1,3 @@
use std::time::Instant;
use skulpin::CoordinateSystemHelper;
use skulpin::skia_safe::{Canvas, Paint, Surface, Budgeted, Rect, colors};
use skulpin::skia_safe::gpu::SurfaceOrigin;
@ -13,13 +11,6 @@ pub use caching_shaper::CachingShaper;
use cursor_renderer::CursorRenderer;
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 {
surface: Option<Surface>,
paint: Paint,
@ -105,7 +96,7 @@ impl Renderer {
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 mut editor = EDITOR.lock().unwrap();
(
@ -156,12 +147,12 @@ impl Renderer {
self.surface = Some(surface);
let (cursor_animating, scheduled_cursor_update) = self.cursor_renderer.draw(
self.cursor_renderer.draw(
cursor, &default_colors,
self.font_width, self.font_height,
&mut self.paint, &mut self.shaper,
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::time::{Duration, Instant};
use std::sync::Arc;
use std::time::Instant;
use image::{load_from_memory, GenericImageView, Pixel};
use skulpin::{CoordinateSystem, RendererBuilder, PresentMode};
@ -7,9 +7,9 @@ use skulpin::skia_safe::icu;
use skulpin::winit::dpi::LogicalSize;
use skulpin::winit::event::{ElementState, Event, MouseScrollDelta, StartCause, WindowEvent};
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::redraw_scheduler::REDRAW_SCHEDULER;
use crate::INITIAL_DIMENSIONS;
@ -18,8 +18,6 @@ use crate::INITIAL_DIMENSIONS;
#[folder = "assets/"]
struct Asset;
const EXTRA_LIVE_FRAMES: usize = 10;
fn handle_new_grid_size(new_size: LogicalSize, renderer: &Renderer) {
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;
@ -72,14 +70,9 @@ pub fn ui_loop() {
let mut mouse_down = false;
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| {
match event {
Event::NewEvents(StartCause::Init) |
Event::NewEvents(StartCause::ResumeTimeReached { .. }) => {
window.request_redraw()
},
Event::NewEvents(StartCause::Init) => window.request_redraw(),
Event::WindowEvent {
event: WindowEvent::CloseRequested,
@ -90,7 +83,7 @@ pub fn ui_loop() {
event: WindowEvent::Resized(new_size),
..
} => {
handle_new_grid_size(window.inner_size(), &renderer)
handle_new_grid_size(new_size, &renderer)
},
Event::WindowEvent {
@ -174,30 +167,12 @@ pub fn ui_loop() {
}
Event::RedrawRequested { .. } => {
frame_start = Instant::now();
if let Err(e) = skulpin_renderer.draw(&window, |canvas, coordinate_system_helper| {
let draw_result = 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 {
if renderer.draw(canvas, coordinate_system_helper) {
handle_new_grid_size(window.inner_size(), &renderer)
}
if live_frames > 0 {
*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);
}
*control_flow = ControlFlow::Wait;
}) {
println!("Error during draw: {:?}", e);
*control_flow = ControlFlow::Exit

Loading…
Cancel
Save