From 33fffed8bedc3a148aa8409a29441ca5b0208048 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Fri, 20 Jan 2023 23:43:27 +0200 Subject: [PATCH 01/53] Move the glutin opengl functionality out to its own module --- src/renderer/mod.rs | 3 +++ src/renderer/opengl.rs | 40 +++++++++++++++++++++++++++++++ src/window/mod.rs | 54 +++++++++++------------------------------- 3 files changed, 57 insertions(+), 40 deletions(-) create mode 100644 src/renderer/opengl.rs diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index fe4b3fd53..7d2927c16 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -2,6 +2,7 @@ pub mod animation_utils; pub mod cursor_renderer; pub mod fonts; pub mod grid_renderer; +mod opengl; pub mod profiler; mod rendered_window; @@ -31,6 +32,8 @@ pub use rendered_window::{ LineFragment, RenderedWindow, WindowDrawCommand, WindowDrawDetails, WindowPadding, }; +pub use opengl::{build_context, Context}; + #[derive(SettingGroup, Clone)] pub struct RendererSettings { position_animation_length: f32, diff --git a/src/renderer/opengl.rs b/src/renderer/opengl.rs new file mode 100644 index 000000000..5542f35a4 --- /dev/null +++ b/src/renderer/opengl.rs @@ -0,0 +1,40 @@ +use crate::cmd_line::CmdLineSettings; + +use glutin::{ContextBuilder, GlProfile, PossiblyCurrent, WindowedContext}; + +use winit::{event_loop::EventLoop, window::WindowBuilder}; + +pub type Context = WindowedContext; + +pub fn build_context( + cmd_line_settings: &CmdLineSettings, + winit_window_builder: WindowBuilder, + event_loop: &EventLoop, +) -> WindowedContext { + let builder = ContextBuilder::new() + .with_pixel_format(24, 8) + .with_stencil_buffer(8) + .with_gl_profile(GlProfile::Core) + .with_srgb(cmd_line_settings.srgb) + .with_vsync(cmd_line_settings.vsync); + + let ctx = match builder + .clone() + .build_windowed(winit_window_builder.clone(), event_loop) + { + Ok(ctx) => ctx, + Err(err) => { + // haven't found any sane way to actually match on the pattern rabbithole CreationError + // provides, so here goes nothing + if err.to_string().contains("vsync") { + builder + .with_vsync(false) + .build_windowed(winit_window_builder, event_loop) + .unwrap() + } else { + panic!("{}", err); + } + } + }; + unsafe { ctx.make_current().unwrap() } +} diff --git a/src/window/mod.rs b/src/window/mod.rs index 82aa28113..697f4a53b 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -8,25 +8,23 @@ mod draw_background; use std::time::{Duration, Instant}; -use glutin::{ - self, +use log::trace; +use tokio::sync::mpsc::UnboundedReceiver; +use winit::{ dpi::PhysicalSize, event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::{self, Fullscreen, Icon}, - ContextBuilder, GlProfile, WindowedContext, }; -use log::trace; -use tokio::sync::mpsc::UnboundedReceiver; #[cfg(target_os = "macos")] -use glutin::platform::macos::WindowBuilderExtMacOS; +use winit::platform::macos::WindowBuilderExtMacOS; #[cfg(target_os = "macos")] use draw_background::draw_background; #[cfg(target_os = "linux")] -use glutin::platform::unix::WindowBuilderExtUnix; +use winit::platform::unix::WindowBuilderExtUnix; use image::{load_from_memory, GenericImageView, Pixel}; use keyboard_manager::KeyboardManager; @@ -43,6 +41,7 @@ use crate::{ redraw_scheduler::REDRAW_SCHEDULER, renderer::Renderer, renderer::WindowPadding, + renderer::{build_context, Context}, running_tracker::*, settings::{ load_last_window_settings, save_window_geometry, PersistentWindowSettings, SETTINGS, @@ -62,8 +61,8 @@ pub enum WindowCommand { ListAvailableFonts, } -pub struct GlutinWindowWrapper { - windowed_context: WindowedContext, +pub struct WinitWindowWrapper { + windowed_context: Context, skia_renderer: SkiaRenderer, renderer: Renderer, keyboard_manager: KeyboardManager, @@ -78,7 +77,7 @@ pub struct GlutinWindowWrapper { window_command_receiver: UnboundedReceiver, } -impl GlutinWindowWrapper { +impl WinitWindowWrapper { pub fn toggle_fullscreen(&mut self) { let window = self.windowed_context.window(); if self.fullscreen { @@ -361,41 +360,16 @@ pub fn create_window() { #[cfg(target_os = "linux")] let winit_window_builder = winit_window_builder - .with_app_id(cmd_line_settings.wayland_app_id) + .with_app_id(cmd_line_settings.wayland_app_id.clone()) .with_class( - cmd_line_settings.x11_wm_class_instance, - cmd_line_settings.x11_wm_class, + cmd_line_settings.x11_wm_class_instance.clone(), + cmd_line_settings.x11_wm_class.clone(), ); #[cfg(target_os = "macos")] let winit_window_builder = winit_window_builder.with_accepts_first_mouse(false); - let builder = ContextBuilder::new() - .with_pixel_format(24, 8) - .with_stencil_buffer(8) - .with_gl_profile(GlProfile::Core) - .with_srgb(cmd_line_settings.srgb) - .with_vsync(cmd_line_settings.vsync); - - let windowed_context = match builder - .clone() - .build_windowed(winit_window_builder.clone(), &event_loop) - { - Ok(ctx) => ctx, - Err(err) => { - // haven't found any sane way to actually match on the pattern rabbithole CreationError - // provides, so here goes nothing - if err.to_string().contains("vsync") { - builder - .with_vsync(false) - .build_windowed(winit_window_builder, &event_loop) - .unwrap() - } else { - panic!("{}", err); - } - } - }; - let windowed_context = unsafe { windowed_context.make_current().unwrap() }; + let windowed_context = build_context(&cmd_line_settings, winit_window_builder, &event_loop); let window = windowed_context.window(); let initial_size = window.inner_size(); @@ -442,7 +416,7 @@ pub fn create_window() { renderer.grid_renderer.font_dimensions, ); - let mut window_wrapper = GlutinWindowWrapper { + let mut window_wrapper = WinitWindowWrapper { windowed_context, skia_renderer, renderer, From e4cb5464c4ce2f8107f0f51c92169c885293eda4 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 21 Jan 2023 00:08:24 +0200 Subject: [PATCH 02/53] Remove references to glutin Most of it replaced by winit --- src/dimensions.rs | 2 +- src/renderer/cursor_renderer/mod.rs | 2 +- src/renderer/grid_renderer.rs | 2 +- src/renderer/mod.rs | 4 ++-- src/settings/window_geometry.rs | 2 +- src/window/draw_background.rs | 7 +++---- src/window/keyboard_manager.rs | 2 +- src/window/mod.rs | 4 ++-- src/window/mouse_manager.rs | 14 ++++++-------- src/window/renderer.rs | 3 +-- 10 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/dimensions.rs b/src/dimensions.rs index 6b36e0401..3c161cb80 100644 --- a/src/dimensions.rs +++ b/src/dimensions.rs @@ -4,8 +4,8 @@ use std::{ str::FromStr, }; -use glutin::dpi::PhysicalSize; use serde::{Deserialize, Serialize}; +use winit::dpi::PhysicalSize; use crate::settings; diff --git a/src/renderer/cursor_renderer/mod.rs b/src/renderer/cursor_renderer/mod.rs index c471f2817..b68c04487 100644 --- a/src/renderer/cursor_renderer/mod.rs +++ b/src/renderer/cursor_renderer/mod.rs @@ -3,8 +3,8 @@ mod cursor_vfx; use std::collections::HashMap; -use glutin::event::{Event, WindowEvent}; use skia_safe::{op, Canvas, Paint, Path, Point}; +use winit::event::{Event, WindowEvent}; use crate::{ bridge::EditorMode, diff --git a/src/renderer/grid_renderer.rs b/src/renderer/grid_renderer.rs index 40f2f8702..eba5d9022 100644 --- a/src/renderer/grid_renderer.rs +++ b/src/renderer/grid_renderer.rs @@ -1,10 +1,10 @@ use std::sync::Arc; -use glutin::dpi::PhysicalSize; use log::trace; use skia_safe::{ colors, dash_path_effect, BlendMode, Canvas, Color, Paint, Path, Point, Rect, HSV, }; +use winit::dpi::PhysicalSize; use crate::{ dimensions::Dimensions, diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 7d2927c16..80cde6415 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -12,10 +12,10 @@ use std::{ sync::Arc, }; -use glutin::event::Event; use log::error; use skia_safe::Canvas; use tokio::sync::mpsc::UnboundedReceiver; +use winit::event::Event; use crate::{ bridge::EditorMode, @@ -32,7 +32,7 @@ pub use rendered_window::{ LineFragment, RenderedWindow, WindowDrawCommand, WindowDrawDetails, WindowPadding, }; -pub use opengl::{build_context, Context}; +pub use opengl::{build_context, Context as WindowedContext}; #[derive(SettingGroup, Clone)] pub struct RendererSettings { diff --git a/src/settings/window_geometry.rs b/src/settings/window_geometry.rs index d6dfc2a78..8e30130b2 100644 --- a/src/settings/window_geometry.rs +++ b/src/settings/window_geometry.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; -use glutin::dpi::PhysicalPosition; use serde::{Deserialize, Serialize}; +use winit::dpi::PhysicalPosition; use crate::{dimensions::Dimensions, settings::SETTINGS, window::WindowSettings}; diff --git a/src/window/draw_background.rs b/src/window/draw_background.rs index 3c80cc2fd..2c24a1da8 100644 --- a/src/window/draw_background.rs +++ b/src/window/draw_background.rs @@ -1,13 +1,12 @@ -use crate::{settings::SETTINGS, window::WindowSettings}; -use glutin::{PossiblyCurrent, WindowedContext}; +use crate::{renderer::WindowedContext, settings::SETTINGS, window::WindowSettings}; use cocoa::appkit::{NSColor, NSWindow}; use cocoa::base::{id, nil}; use csscolorparser::Color; -use glutin::platform::macos::WindowExtMacOS; use objc::{rc::autoreleasepool, runtime::YES}; +use winit::platform::macos::WindowExtMacOS; -pub fn draw_background(window: &WindowedContext) { +pub fn draw_background(window: &WindowedContext) { if let Ok(color) = &SETTINGS .get::() .background_color diff --git a/src/window/keyboard_manager.rs b/src/window/keyboard_manager.rs index 72972fbf3..37dd1144d 100644 --- a/src/window/keyboard_manager.rs +++ b/src/window/keyboard_manager.rs @@ -4,7 +4,7 @@ use crate::{ settings::SETTINGS, window::KeyboardSettings, }; -use glutin::{ +use winit::{ event::{ElementState, Event, KeyEvent, WindowEvent}, keyboard::{Key, Key::Dead}, platform::modifier_supplement::KeyEventExtModifierSupplement, diff --git a/src/window/mod.rs b/src/window/mod.rs index 697f4a53b..4f73bb43b 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -41,7 +41,7 @@ use crate::{ redraw_scheduler::REDRAW_SCHEDULER, renderer::Renderer, renderer::WindowPadding, - renderer::{build_context, Context}, + renderer::{build_context, WindowedContext}, running_tracker::*, settings::{ load_last_window_settings, save_window_geometry, PersistentWindowSettings, SETTINGS, @@ -62,7 +62,7 @@ pub enum WindowCommand { } pub struct WinitWindowWrapper { - windowed_context: Context, + windowed_context: WindowedContext, skia_renderer: SkiaRenderer, renderer: Renderer, keyboard_manager: KeyboardManager, diff --git a/src/window/mouse_manager.rs b/src/window/mouse_manager.rs index 77f2c5c03..6d9ee23b0 100644 --- a/src/window/mouse_manager.rs +++ b/src/window/mouse_manager.rs @@ -4,21 +4,19 @@ use std::{ time::{Duration, Instant}, }; -use glutin::{ - self, +use skia_safe::Rect; +use winit::{ dpi::PhysicalPosition, event::{ DeviceId, ElementState, Event, MouseButton, MouseScrollDelta, Touch, TouchPhase, WindowEvent, }, - PossiblyCurrent, WindowedContext, }; -use skia_safe::Rect; use crate::{ bridge::{SerialCommand, UiCommand}, event_aggregator::EVENT_AGGREGATOR, - renderer::{Renderer, WindowDrawDetails}, + renderer::{Renderer, WindowDrawDetails, WindowedContext}, settings::SETTINGS, window::keyboard_manager::KeyboardManager, window::WindowSettings, @@ -109,7 +107,7 @@ impl MouseManager { y: i32, keyboard_manager: &KeyboardManager, renderer: &Renderer, - windowed_context: &WindowedContext, + windowed_context: &WindowedContext, ) { let size = windowed_context.window().inner_size(); if x < 0 || x as u32 >= size.width || y < 0 || y as u32 >= size.height { @@ -313,7 +311,7 @@ impl MouseManager { &mut self, keyboard_manager: &KeyboardManager, renderer: &Renderer, - windowed_context: &WindowedContext, + windowed_context: &WindowedContext, finger_id: (DeviceId, u64), location: PhysicalPosition, phase: &TouchPhase, @@ -416,7 +414,7 @@ impl MouseManager { event: &Event<()>, keyboard_manager: &KeyboardManager, renderer: &Renderer, - windowed_context: &WindowedContext, + windowed_context: &WindowedContext, ) { match event { Event::WindowEvent { diff --git a/src/window/renderer.rs b/src/window/renderer.rs index d642f088e..707f040b3 100644 --- a/src/window/renderer.rs +++ b/src/window/renderer.rs @@ -1,14 +1,13 @@ use std::convert::TryInto; use crate::redraw_scheduler::REDRAW_SCHEDULER; +use crate::renderer::WindowedContext; use gl::types::*; use skia_safe::{ gpu::{gl::FramebufferInfo, BackendRenderTarget, DirectContext, SurfaceOrigin}, Canvas, ColorType, Surface, }; -type WindowedContext = glutin::ContextWrapper; - fn create_surface( windowed_context: &WindowedContext, gr_context: &mut DirectContext, From 706e6bdb914a1930359d174ef4650ecd1be0593d Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 21 Jan 2023 00:22:23 +0200 Subject: [PATCH 03/53] Pass the winit window around, not the glutin context --- src/window/draw_background.rs | 8 ++++---- src/window/mod.rs | 4 ++-- src/window/mouse_manager.rs | 25 +++++++++++++------------ 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/window/draw_background.rs b/src/window/draw_background.rs index 2c24a1da8..a7900bc0f 100644 --- a/src/window/draw_background.rs +++ b/src/window/draw_background.rs @@ -1,12 +1,12 @@ -use crate::{renderer::WindowedContext, settings::SETTINGS, window::WindowSettings}; +use crate::{settings::SETTINGS, window::WindowSettings}; use cocoa::appkit::{NSColor, NSWindow}; use cocoa::base::{id, nil}; use csscolorparser::Color; use objc::{rc::autoreleasepool, runtime::YES}; -use winit::platform::macos::WindowExtMacOS; +use winit::{platform::macos::WindowExtMacOS, window::Window}; -pub fn draw_background(window: &WindowedContext) { +pub fn draw_background(window: &Window) { if let Ok(color) = &SETTINGS .get::() .background_color @@ -14,7 +14,7 @@ pub fn draw_background(window: &WindowedContext) { { autoreleasepool(|| unsafe { let [red, green, blue, alpha] = color.to_array(); - let ns_window: id = window.window().ns_window() as id; + let ns_window: id = window.ns_window() as id; let ns_background = NSColor::colorWithSRGBRed_green_blue_alpha_( nil, red.into(), diff --git a/src/window/mod.rs b/src/window/mod.rs index 4f73bb43b..81010a402 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -146,7 +146,7 @@ impl WinitWindowWrapper { &event, &self.keyboard_manager, &self.renderer, - &self.windowed_context, + self.windowed_context.window(), ); self.renderer.handle_event(&event); match event { @@ -491,7 +491,7 @@ pub fn create_window() { } previous_frame_start = frame_start; #[cfg(target_os = "macos")] - draw_background(&window_wrapper.windowed_context); + draw_background(window_wrapper.windowed_context.window()); } *control_flow = ControlFlow::WaitUntil(previous_frame_start + frame_duration) diff --git a/src/window/mouse_manager.rs b/src/window/mouse_manager.rs index 6d9ee23b0..7e8f5778c 100644 --- a/src/window/mouse_manager.rs +++ b/src/window/mouse_manager.rs @@ -11,12 +11,13 @@ use winit::{ DeviceId, ElementState, Event, MouseButton, MouseScrollDelta, Touch, TouchPhase, WindowEvent, }, + window::Window, }; use crate::{ bridge::{SerialCommand, UiCommand}, event_aggregator::EVENT_AGGREGATOR, - renderer::{Renderer, WindowDrawDetails, WindowedContext}, + renderer::{Renderer, WindowDrawDetails}, settings::SETTINGS, window::keyboard_manager::KeyboardManager, window::WindowSettings, @@ -107,9 +108,9 @@ impl MouseManager { y: i32, keyboard_manager: &KeyboardManager, renderer: &Renderer, - windowed_context: &WindowedContext, + window: &Window, ) { - let size = windowed_context.window().inner_size(); + let size = window.inner_size(); if x < 0 || x as u32 >= size.width || y < 0 || y as u32 >= size.height { return; } @@ -311,7 +312,7 @@ impl MouseManager { &mut self, keyboard_manager: &KeyboardManager, renderer: &Renderer, - windowed_context: &WindowedContext, + window: &Window, finger_id: (DeviceId, u64), location: PhysicalPosition, phase: &TouchPhase, @@ -360,7 +361,7 @@ impl MouseManager { location.y.round() as i32, keyboard_manager, renderer, - windowed_context, + window, ); } // the double check might seem useless, but the if branch above might set @@ -383,7 +384,7 @@ impl MouseManager { location.y.round() as i32, keyboard_manager, renderer, - windowed_context, + window, ); self.handle_pointer_transition(&MouseButton::Left, true, keyboard_manager); } @@ -399,7 +400,7 @@ impl MouseManager { trace.start.y.round() as i32, keyboard_manager, renderer, - windowed_context, + window, ); self.handle_pointer_transition(&MouseButton::Left, true, keyboard_manager); self.handle_pointer_transition(&MouseButton::Left, false, keyboard_manager); @@ -414,7 +415,7 @@ impl MouseManager { event: &Event<()>, keyboard_manager: &KeyboardManager, renderer: &Renderer, - windowed_context: &WindowedContext, + window: &Window, ) { match event { Event::WindowEvent { @@ -426,10 +427,10 @@ impl MouseManager { position.y as i32, keyboard_manager, renderer, - windowed_context, + window, ); if self.mouse_hidden { - windowed_context.window().set_cursor_visible(true); + window.set_cursor_visible(true); self.mouse_hidden = false; } } @@ -466,7 +467,7 @@ impl MouseManager { } => self.handle_touch( keyboard_manager, renderer, - windowed_context, + window, (*device_id, *id), location.cast(), phase, @@ -489,7 +490,7 @@ impl MouseManager { if key_event.state == ElementState::Pressed { let window_settings = SETTINGS.get::(); if window_settings.hide_mouse_when_typing && !self.mouse_hidden { - windowed_context.window().set_cursor_visible(false); + window.set_cursor_visible(false); self.mouse_hidden = true; } } From c1020701c1a74f2488ce1a8a5fc5bdc3ce718a8e Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Mon, 14 Nov 2022 11:44:42 +0200 Subject: [PATCH 04/53] Integrate tracy profiler --- Cargo.lock | 10 + Cargo.toml | 3 + src/main.rs | 5 + src/profiling/mod.rs | 9 + src/profiling/profiling_disabled.rs | 23 +++ src/profiling/profiling_enabled.rs | 309 ++++++++++++++++++++++++++++ src/window/mod.rs | 12 +- 7 files changed, 369 insertions(+), 2 deletions(-) create mode 100644 src/profiling/mod.rs create mode 100644 src/profiling/profiling_disabled.rs create mode 100644 src/profiling/profiling_enabled.rs diff --git a/Cargo.lock b/Cargo.lock index 46a2bf6fb..a1cd790a7 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -1406,6 +1406,7 @@ dependencies = [ "time", "tokio", "tokio-util", + "tracy-client-sys", "unicode-segmentation", "which", "winapi", @@ -2508,6 +2509,15 @@ dependencies = [ "serde", ] +[[package]] +name = "tracy-client-sys" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcbdba03a3cfc5f757469fd5b6d795fc461484c97e47e94b0fc7db93261d9c5" +dependencies = [ + "cc", +] + [[package]] name = "ttf-parser" version = "0.15.2" diff --git a/Cargo.toml b/Cargo.toml index c31809e9d..cdfe81dc2 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,8 @@ members = ["neovide-derive"] [features] default = [] embed-fonts = [] +profiling = ["dep:tracy-client-sys"] +gpu_profiling = ["profiling"] [dependencies] copypasta = "0.8.1" @@ -49,6 +51,7 @@ unicode-segmentation = "1.9.0" which = "4.2.5" winit = { git = "https://github.com/neovide/winit", branch = "new-keyboard-all" } xdg = "2.4.1" +tracy-client-sys = { version = "0.19.0", optional = true } [dev-dependencies] mockall = "0.11.0" diff --git a/src/main.rs b/src/main.rs index 93e349295..8434d27b6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,7 @@ mod editor; mod error_handling; mod event_aggregator; mod frame; +mod profiling; mod redraw_scheduler; mod renderer; mod running_tracker; @@ -57,6 +58,8 @@ pub use running_tracker::*; #[cfg(target_os = "windows")] pub use windows_utils::*; +pub use profiling::startup_profiler; + const BACKTRACES_FILE: &str = "neovide_backtraces.log"; const REQUEST_MESSAGE: &str = "This is a bug and we would love for it to be reported to https://github.com/neovide/neovide/issues"; @@ -139,6 +142,8 @@ fn protected_main() { // Multiple other parts of the app "queue_next_frame" function to ensure animations continue // properly or updates to the graphics are pushed to the screen. + startup_profiler(); + #[cfg(target_os = "windows")] windows_attach_to_console(); diff --git a/src/profiling/mod.rs b/src/profiling/mod.rs new file mode 100644 index 000000000..3dd376d76 --- /dev/null +++ b/src/profiling/mod.rs @@ -0,0 +1,9 @@ +#[cfg(not(feature = "profiling"))] +mod profiling_disabled; +#[cfg(feature = "profiling")] +mod profiling_enabled; + +#[cfg(not(feature = "profiling"))] +pub use profiling_disabled::*; +#[cfg(feature = "profiling")] +pub use profiling_enabled::*; diff --git a/src/profiling/profiling_disabled.rs b/src/profiling/profiling_disabled.rs new file mode 100644 index 000000000..6d59e72c8 --- /dev/null +++ b/src/profiling/profiling_disabled.rs @@ -0,0 +1,23 @@ +#[inline(always)] +pub fn startup_profiler() {} + +#[inline(always)] +pub fn emit_frame_mark() {} + +#[inline(always)] +pub fn tracy_create_gpu_context(_name: &str) {} + +#[inline(always)] +pub fn tracy_gpu_collect() {} + +macro_rules! tracy_zone { + ($name: expr, $color: expr) => {}; + ($name: expr) => {}; +} +macro_rules! tracy_gpu_zone { + ($name: expr, $color: expr) => {}; + ($name: expr) => {}; +} + +pub(crate) use tracy_gpu_zone; +pub(crate) use tracy_zone; diff --git a/src/profiling/profiling_enabled.rs b/src/profiling/profiling_enabled.rs new file mode 100644 index 000000000..467702798 --- /dev/null +++ b/src/profiling/profiling_enabled.rs @@ -0,0 +1,309 @@ +use std::{ + cell::RefCell, + ffi::CString, + mem, + ptr::null, + sync::atomic::{AtomicU8, Ordering}, +}; + +use tracy_client_sys::{ + ___tracy_c_zone_context, ___tracy_connected, ___tracy_emit_frame_mark, + ___tracy_emit_gpu_context_name, ___tracy_emit_gpu_new_context, ___tracy_emit_gpu_time_serial, + ___tracy_emit_gpu_zone_begin_serial, ___tracy_emit_gpu_zone_end_serial, + ___tracy_emit_zone_begin, ___tracy_emit_zone_end, ___tracy_gpu_context_name_data, + ___tracy_gpu_new_context_data, ___tracy_gpu_time_data, ___tracy_gpu_zone_begin_data, + ___tracy_gpu_zone_end_data, ___tracy_source_location_data, ___tracy_startup_profiler, +}; + +use gl::{ + GenQueries, GetInteger64v, GetQueryObjectiv, GetQueryObjectui64v, QueryCounter, QUERY_RESULT, + QUERY_RESULT_AVAILABLE, TIMESTAMP, +}; + +pub struct _LocationData { + pub data: ___tracy_source_location_data, +} + +unsafe impl Send for _LocationData {} +unsafe impl Sync for _LocationData {} + +#[allow(unconditional_panic)] +const fn illegal_null_in_string() { + [][0] +} + +#[doc(hidden)] +pub const fn validate_cstr_contents(bytes: &[u8]) { + let mut i = 0; + while i < bytes.len() { + if bytes[i] == b'\0' { + illegal_null_in_string(); + } + i += 1; + } +} + +macro_rules! cstr { + ( $s:literal ) => {{ + $crate::profiling::validate_cstr_contents($s.as_bytes()); + unsafe { std::mem::transmute::<_, &std::ffi::CStr>(concat!($s, "\0")) } + }}; +} + +macro_rules! file_cstr { + ( ) => {{ + unsafe { std::mem::transmute::<_, &std::ffi::CStr>(concat!(std::file!(), "\0")) } + }}; +} + +pub const fn _create_location_data( + name: &std::ffi::CStr, + function: &std::ffi::CStr, + file: &std::ffi::CStr, + line: u32, + color: u32, +) -> _LocationData { + _LocationData { + data: ___tracy_source_location_data { + name: name.as_ptr(), + function: function.as_ptr(), + file: file.as_ptr(), + line, + color, + }, + } +} + +#[allow(dead_code)] +fn is_connected() -> bool { + unsafe { ___tracy_connected() > 0 } +} + +#[cfg(feature = "gpu_profiling")] +fn gpu_enabled() -> bool { + is_connected() +} + +#[cfg(not(feature = "gpu_profiling"))] +fn gpu_enabled() -> bool { + false +} + +pub struct _Zone { + context: ___tracy_c_zone_context, + gpu: bool, +} + +impl _Zone { + pub fn new(loc_data: &___tracy_source_location_data, gpu: bool) -> Self { + let context = unsafe { ___tracy_emit_zone_begin(loc_data, 1) }; + let gpu = gpu && gpu_enabled(); + if gpu { + let (context, query, glquery) = GPUCTX.with(|ctx| { + let mut ctx = ctx.borrow_mut(); + let query = ctx.next_query_id(); + (ctx.id, query, ctx.query[query]) + }); + + let gpu_data = ___tracy_gpu_zone_begin_data { + srcloc: (loc_data as *const ___tracy_source_location_data) as u64, + queryId: query as u16, + context, + }; + unsafe { + QueryCounter(glquery, TIMESTAMP); + ___tracy_emit_gpu_zone_begin_serial(gpu_data); + } + } + _Zone { context, gpu } + } +} + +impl Drop for _Zone { + fn drop(&mut self) { + if self.gpu && gpu_enabled() { + let (context, query, glquery) = GPUCTX.with(|ctx| { + let mut ctx = ctx.borrow_mut(); + let query = ctx.next_query_id(); + (ctx.id, query, ctx.query[query]) + }); + let gpu_data = ___tracy_gpu_zone_end_data { + queryId: query as u16, + context, + }; + unsafe { + QueryCounter(glquery, TIMESTAMP); + ___tracy_emit_gpu_zone_end_serial(gpu_data); + } + } + unsafe { + ___tracy_emit_zone_end(self.context); + } + } +} + +static CONTEXT_ID: AtomicU8 = AtomicU8::new(0); + +struct GpuCtx { + id: u8, + query: [u32; 64 * 1024], + head: usize, + tail: usize, +} + +impl GpuCtx { + fn next_query_id(&mut self) -> usize { + let query = self.head; + self.head = (self.head + 1) % self.query.len(); + assert!(self.head != self.tail); + query + } +} + +thread_local! { + static GPUCTX: RefCell = RefCell::new(GpuCtx { + id: CONTEXT_ID.fetch_add(1, Ordering::Relaxed), + query: unsafe {mem::MaybeUninit::zeroed().assume_init()}, + head: 0, + tail: 0, + }); +} + +pub fn startup_profiler() { + unsafe { + ___tracy_startup_profiler(); + } +} + +#[inline(always)] +pub fn emit_frame_mark() { + unsafe { + ___tracy_emit_frame_mark(null()); + } +} + +pub fn tracy_create_gpu_context(name: &str) { + // Don't change order, only add new entries at the end, this is also used on trace dumps! + #[allow(dead_code)] + enum GpuContextType { + Invalid, + OpenGl, + Vulkan, + OpenCL, + Direct3D12, + Direct3D11, + } + + let id = GPUCTX.with(|ctx| { + let mut ctx = ctx.borrow_mut(); + unsafe { + GenQueries(ctx.query.len() as i32, ctx.query.as_mut_ptr()); + } + + ctx.id + }); + + let mut timestamp: i64 = 0; + unsafe { + GetInteger64v(TIMESTAMP, &mut timestamp); + } + + let ctxt_data = ___tracy_gpu_new_context_data { + gpuTime: timestamp, + period: 1.0, + context: id, + flags: 0, + type_: GpuContextType::OpenGl as u8, + }; + let namestring = CString::new(name).unwrap(); + let name_data = ___tracy_gpu_context_name_data { + context: id, + name: namestring.as_ptr(), + len: name.len() as u16, + }; + unsafe { + ___tracy_emit_gpu_new_context(ctxt_data); + ___tracy_emit_gpu_context_name(name_data); + } +} + +pub fn tracy_gpu_collect() { + tracy_zone!("collect gpu info"); + if !gpu_enabled() { + return; + } + + GPUCTX.with(|ctx| { + let mut ctx = ctx.borrow_mut(); + + while ctx.tail != ctx.head { + let mut available: i32 = 0; + unsafe { + GetQueryObjectiv(ctx.query[ctx.tail], QUERY_RESULT_AVAILABLE, &mut available); + } + if available <= 0 { + break; + } + + let mut time: u64 = 0; + unsafe { + GetQueryObjectui64v(ctx.query[ctx.tail], QUERY_RESULT, &mut time); + } + let time_data = ___tracy_gpu_time_data { + gpuTime: time as i64, + queryId: ctx.tail as u16, + context: ctx.id, + }; + unsafe { + ___tracy_emit_gpu_time_serial(time_data); + } + ctx.tail = (ctx.tail + 1) % ctx.query.len(); + } + }); +} + +#[macro_export] +macro_rules! location_data { + ($name: expr, $color: expr) => {{ + static LOC: $crate::profiling::_LocationData = $crate::profiling::_create_location_data( + $crate::profiling::cstr!($name), + // There does not seem to be any way of getting a c string to the current + // function until this is implemented + // https://github.com/rust-lang/rust/issues/63084 + // So use Unknown for now + $crate::profiling::cstr!("Unknown"), + $crate::profiling::file_cstr!(), + std::line!(), + $color, + ); + &LOC.data + }}; +} + +#[macro_export] +macro_rules! tracy_zone { + ($name: expr, $color: expr) => { + let _tracy_zone = + $crate::profiling::_Zone::new($crate::profiling::location_data!($name, $color), false); + }; + ($name: expr) => { + $crate::profiling::tracy_zone!($name, 0) + }; +} + +#[macro_export] +macro_rules! tracy_gpu_zone { + ($name: expr, $color: expr) => { + let _tracy_zone = + $crate::profiling::_Zone::new($crate::profiling::location_data!($name, $color), true); + }; + ($name: expr) => { + $crate::profiling::tracy_gpu_zone!($name, 0) + }; +} + +pub(crate) use cstr; +pub(crate) use file_cstr; +pub(crate) use location_data; +pub(crate) use tracy_gpu_zone; +pub(crate) use tracy_zone; diff --git a/src/window/mod.rs b/src/window/mod.rs index 82aa28113..15362a0b1 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -221,8 +221,16 @@ impl GlutinWindowWrapper { if REDRAW_SCHEDULER.should_draw() || SETTINGS.get::().no_idle { self.font_changed_last_frame = self.renderer.draw_frame(self.skia_renderer.canvas(), dt); - self.skia_renderer.gr_context.flush(None); - self.windowed_context.swap_buffers().unwrap(); + { + tracy_gpu_zone!("skia flush"); + self.skia_renderer.gr_context.flush(None); + } + { + tracy_gpu_zone!("swap buffers"); + self.windowed_context.swap_buffers().unwrap(); + } + emit_frame_mark(); + tracy_gpu_collect(); } // Wait until fonts are loaded, so we can set proper window size. From 415ecffd37c488413ef64034e2ee2f49bca7a819 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Wed, 16 Nov 2022 10:22:56 +0200 Subject: [PATCH 05/53] Add some profiling zones --- src/editor/mod.rs | 82 ++++++++++++++++++++++------ src/renderer/cursor_renderer/mod.rs | 2 + src/renderer/fonts/caching_shaper.rs | 2 + src/renderer/grid_renderer.rs | 3 + src/renderer/mod.rs | 2 + src/renderer/profiler.rs | 6 +- src/renderer/rendered_window.rs | 12 +++- src/window/mod.rs | 9 +++ 8 files changed, 99 insertions(+), 19 deletions(-) diff --git a/src/editor/mod.rs b/src/editor/mod.rs index 50f12660b..8a5272e17 100644 --- a/src/editor/mod.rs +++ b/src/editor/mod.rs @@ -11,6 +11,7 @@ use log::{error, trace}; use crate::{ bridge::{GuiOption, RedrawEvent, WindowAnchor}, event_aggregator::EVENT_AGGREGATOR, + profiling::tracy_zone, redraw_scheduler::REDRAW_SCHEDULER, renderer::DrawCommand, window::WindowCommand, @@ -81,9 +82,11 @@ impl Editor { match command { EditorCommand::NeovimRedrawEvent(event) => match event { RedrawEvent::SetTitle { title } => { + tracy_zone!("EditorSetTitle"); EVENT_AGGREGATOR.send(WindowCommand::TitleChanged(title)); } RedrawEvent::ModeInfoSet { cursor_modes } => { + tracy_zone!("EditorModeInfoSet"); self.mode_list = cursor_modes; if let Some(current_mode_i) = self.current_mode_index { if let Some(current_mode) = self.mode_list.get(current_mode_i as usize) { @@ -91,8 +94,12 @@ impl Editor { } } } - RedrawEvent::OptionSet { gui_option } => self.set_option(gui_option), + RedrawEvent::OptionSet { gui_option } => { + tracy_zone!("EditorOptionSet"); + self.set_option(gui_option); + } RedrawEvent::ModeChange { mode, mode_index } => { + tracy_zone!("ModeChange"); if let Some(cursor_mode) = self.mode_list.get(mode_index as usize) { self.cursor.change_mode(cursor_mode, &self.defined_styles); self.current_mode_index = Some(mode_index) @@ -104,26 +111,38 @@ impl Editor { .ok(); } RedrawEvent::MouseOn => { + tracy_zone!("EditorMouseOn"); EVENT_AGGREGATOR.send(WindowCommand::SetMouseEnabled(true)); } RedrawEvent::MouseOff => { + tracy_zone!("EditorMouseOff"); EVENT_AGGREGATOR.send(WindowCommand::SetMouseEnabled(false)); } RedrawEvent::BusyStart => { + tracy_zone!("EditorBusyStart"); trace!("Cursor off"); self.cursor.enabled = false; } RedrawEvent::BusyStop => { + tracy_zone!("EditorBusyStop"); trace!("Cursor on"); self.cursor.enabled = true; } RedrawEvent::Flush => { + tracy_zone!("EditorFlush"); trace!("Image flushed"); self.send_cursor_info(); - self.draw_command_batcher.send_batch(); - REDRAW_SCHEDULER.queue_next_frame(); + { + trace!("send_batch"); + self.draw_command_batcher.send_batch(); + } + { + trace!("queue_next_frame"); + REDRAW_SCHEDULER.queue_next_frame(); + } } RedrawEvent::DefaultColorsSet { colors } => { + tracy_zone!("EditorDefaultColorsSet"); self.draw_command_batcher .queue(DrawCommand::DefaultStyleChanged(Style::new(colors))) .ok(); @@ -132,18 +151,23 @@ impl Editor { REDRAW_SCHEDULER.queue_next_frame(); } RedrawEvent::HighlightAttributesDefine { id, style } => { + tracy_zone!("EditorHighlightAttributesDefine"); self.defined_styles.insert(id, Arc::new(style)); } RedrawEvent::CursorGoto { grid, column: left, row: top, - } => self.set_cursor_position(grid, left, top), + } => { + tracy_zone!("EditorCursorGoto"); + self.set_cursor_position(grid, left, top); + } RedrawEvent::Resize { grid, width, height, } => { + tracy_zone!("EditorResize"); self.resize_window(grid, width, height); } RedrawEvent::GridLine { @@ -152,6 +176,7 @@ impl Editor { column_start, cells, } => { + tracy_zone!("EditorGridLine"); let defined_styles = &self.defined_styles; let window = self.windows.get_mut(&grid); if let Some(window) = window { @@ -159,12 +184,16 @@ impl Editor { } } RedrawEvent::Clear { grid } => { + tracy_zone!("EditorClear"); let window = self.windows.get_mut(&grid); if let Some(window) = window { window.clear(); } } - RedrawEvent::Destroy { grid } => self.close_window(grid), + RedrawEvent::Destroy { grid } => { + tracy_zone!("EditorDestroy"); + self.close_window(grid) + } RedrawEvent::Scroll { grid, top, @@ -174,6 +203,7 @@ impl Editor { rows, columns, } => { + tracy_zone!("EditorScroll"); let window = self.windows.get_mut(&grid); if let Some(window) = window { window.scroll_region(top, bottom, left, right, rows, columns); @@ -185,7 +215,10 @@ impl Editor { start_column, width, height, - } => self.set_window_position(grid, start_column, start_row, width, height), + } => { + tracy_zone!("EditorWindowPosition"); + self.set_window_position(grid, start_column, start_row, width, height) + } RedrawEvent::WindowFloatPosition { grid, anchor, @@ -194,22 +227,30 @@ impl Editor { anchor_row: anchor_top, sort_order, .. - } => self.set_window_float_position( - grid, - anchor_grid, - anchor, - anchor_left, - anchor_top, - sort_order, - ), + } => { + tracy_zone!("EditorWindowFloatPosition"); + self.set_window_float_position( + grid, + anchor_grid, + anchor, + anchor_left, + anchor_top, + sort_order, + ) + } RedrawEvent::WindowHide { grid } => { + tracy_zone!("EditorWindowHide"); let window = self.windows.get(&grid); if let Some(window) = window { window.hide(); } } - RedrawEvent::WindowClose { grid } => self.close_window(grid), + RedrawEvent::WindowClose { grid } => { + tracy_zone!("EditorWindowClose"); + self.close_window(grid) + } RedrawEvent::MessageSetPosition { grid, row, .. } => { + tracy_zone!("EditorMessageSetPosition"); self.set_message_position(grid, row) } RedrawEvent::WindowViewport { @@ -217,10 +258,16 @@ impl Editor { top_line, bottom_line, .. - } => self.send_updated_viewport(grid, top_line, bottom_line), + } => { + tracy_zone!("EditorWindowViewport"); + self.send_updated_viewport(grid, top_line, bottom_line); + } _ => {} }, - EditorCommand::RedrawScreen => self.redraw_screen(), + EditorCommand::RedrawScreen => { + tracy_zone!("EditorRedrawScreen"); + self.redraw_screen(); + } }; } @@ -423,6 +470,7 @@ impl Editor { } fn send_cursor_info(&mut self) { + tracy_zone!("send_cursor_info"); let (grid_left, grid_top) = self.cursor.grid_position; if let Some(window) = self.windows.get(&self.cursor.parent_window_id) { let (character, style, double_width) = window.get_cursor_grid_cell(grid_left, grid_top); diff --git a/src/renderer/cursor_renderer/mod.rs b/src/renderer/cursor_renderer/mod.rs index c471f2817..56d56adbd 100644 --- a/src/renderer/cursor_renderer/mod.rs +++ b/src/renderer/cursor_renderer/mod.rs @@ -9,6 +9,7 @@ use skia_safe::{op, Canvas, Paint, Path, Point}; use crate::{ bridge::EditorMode, editor::{Cursor, CursorShape}, + profiling::tracy_zone, redraw_scheduler::REDRAW_SCHEDULER, renderer::animation_utils::*, renderer::{GridRenderer, RenderedWindow}, @@ -278,6 +279,7 @@ impl CursorRenderer { canvas: &mut Canvas, dt: f32, ) { + tracy_zone!("cursor_draw"); let render = self.blink_status.update_status(&self.cursor); let settings = SETTINGS.get::(); diff --git a/src/renderer/fonts/caching_shaper.rs b/src/renderer/fonts/caching_shaper.rs index ac77f77a0..a328220c1 100644 --- a/src/renderer/fonts/caching_shaper.rs +++ b/src/renderer/fonts/caching_shaper.rs @@ -16,6 +16,7 @@ use swash::{ }; use unicode_segmentation::UnicodeSegmentation; +use crate::profiling::tracy_zone; use crate::renderer::fonts::{font_loader::*, font_options::*}; #[derive(new, Clone, Hash, PartialEq, Eq, Debug)] @@ -392,6 +393,7 @@ impl CachingShaper { } pub fn shape_cached(&mut self, text: String, bold: bool, italic: bool) -> &Vec { + tracy_zone!("shape_cached"); let key = ShapeKey::new(text.clone(), bold, italic); if !self.blob_cache.contains(&key) { diff --git a/src/renderer/grid_renderer.rs b/src/renderer/grid_renderer.rs index 40f2f8702..e93c85297 100644 --- a/src/renderer/grid_renderer.rs +++ b/src/renderer/grid_renderer.rs @@ -9,6 +9,7 @@ use skia_safe::{ use crate::{ dimensions::Dimensions, editor::{Colors, Style, UnderlineStyle}, + profiling::tracy_zone, renderer::{CachingShaper, RendererSettings}, settings::*, window::WindowSettings, @@ -103,6 +104,7 @@ impl GridRenderer { style: &Option>, is_floating: bool, ) { + tracy_zone!("draw_background"); self.paint.set_blend_mode(BlendMode::Src); let region = self.compute_text_region(grid_position, cell_width); @@ -137,6 +139,7 @@ impl GridRenderer { cell_width: u64, style: &Option>, ) { + tracy_zone!("draw_foreground"); let (x, y) = grid_position * self.font_dimensions; let width = cell_width * self.font_dimensions.width; diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index fe4b3fd53..f6306c19d 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -20,6 +20,7 @@ use crate::{ bridge::EditorMode, editor::{Cursor, Style}, event_aggregator::EVENT_AGGREGATOR, + profiling::tracy_zone, settings::*, WindowSettings, }; @@ -140,6 +141,7 @@ impl Renderer { /// `bool` indicating whether or not font was changed during this frame. #[allow(clippy::needless_collect)] pub fn draw_frame(&mut self, root_canvas: &mut Canvas, dt: f32) -> bool { + tracy_zone!("renderer_draw_frame"); let mut draw_commands = Vec::new(); while let Ok(draw_command) = self.batched_draw_command_receiver.try_recv() { draw_commands.extend(draw_command); diff --git a/src/renderer/profiler.rs b/src/renderer/profiler.rs index d2d70bba1..e4f542ecd 100644 --- a/src/renderer/profiler.rs +++ b/src/renderer/profiler.rs @@ -4,7 +4,10 @@ use std::collections::VecDeque; use std::sync::Arc; use std::time::Instant; -use crate::renderer::{fonts::font_loader::*, RendererSettings}; +use crate::{ + profiling::tracy_zone, + renderer::{fonts::font_loader::*, RendererSettings}, +}; use skia_safe::{Canvas, Color, Paint, Point, Rect, Size}; const FRAMETIMES_COUNT: usize = 48; @@ -32,6 +35,7 @@ impl Profiler { } pub fn draw(&mut self, root_canvas: &mut Canvas, dt: f32) { + tracy_zone!("profiler_draw"); if !SETTINGS.get::().profiler { return; } diff --git a/src/renderer/rendered_window.rs b/src/renderer/rendered_window.rs index 637893b41..ad906d2f8 100644 --- a/src/renderer/rendered_window.rs +++ b/src/renderer/rendered_window.rs @@ -11,6 +11,7 @@ use skia_safe::{ use crate::{ dimensions::Dimensions, editor::Style, + profiling::tracy_zone, redraw_scheduler::REDRAW_SCHEDULER, renderer::{animation_utils::*, GridRenderer, RendererSettings}, }; @@ -354,6 +355,7 @@ impl RenderedWindow { grid_size, floating_order, } => { + tracy_zone!("position_cmd", 0); let Dimensions { width: font_width, height: font_height, @@ -411,6 +413,7 @@ impl RenderedWindow { } } WindowDrawCommand::DrawLine(line_fragments) => { + tracy_zone!("draw_line_cmd", 0); let canvas = self.current_surface.surface.canvas(); canvas.save(); @@ -453,6 +456,7 @@ impl RenderedWindow { rows, cols, } => { + tracy_zone!("scroll_cmd", 0); let Dimensions { width: font_width, height: font_height, @@ -486,6 +490,7 @@ impl RenderedWindow { canvas.restore(); } WindowDrawCommand::Clear => { + tracy_zone!("clear_cmd", 0); self.current_surface.surface = build_window_surface_with_grid_size( self.current_surface.surface.canvas(), grid_renderer, @@ -495,6 +500,7 @@ impl RenderedWindow { self.snapshots.clear(); } WindowDrawCommand::Show => { + tracy_zone!("show_cmd", 0); if self.hidden { self.hidden = false; self.position_t = 2.0; // We don't want to animate since the window is becoming visible, @@ -502,8 +508,12 @@ impl RenderedWindow { self.grid_start_position = self.grid_destination; } } - WindowDrawCommand::Hide => self.hidden = true, + WindowDrawCommand::Hide => { + tracy_zone!("hide_cmd", 0); + self.hidden = true; + } WindowDrawCommand::Viewport { top_line, .. } => { + tracy_zone!("viewport_cmd", 0); if self.current_surface.top_line != top_line as u64 { let new_snapshot = self.current_surface.snapshot(); self.snapshots.push_back(new_snapshot); diff --git a/src/window/mod.rs b/src/window/mod.rs index 15362a0b1..dcca40e20 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -28,6 +28,10 @@ use draw_background::draw_background; #[cfg(target_os = "linux")] use glutin::platform::unix::WindowBuilderExtUnix; +use crate::profiling::{ + emit_frame_mark, tracy_create_gpu_context, tracy_gpu_collect, tracy_gpu_zone, tracy_zone, +}; + use image::{load_from_memory, GenericImageView, Pixel}; use keyboard_manager::KeyboardManager; use mouse_manager::MouseManager; @@ -101,6 +105,7 @@ impl GlutinWindowWrapper { #[allow(clippy::needless_collect)] pub fn handle_window_commands(&mut self) { + tracy_zone!("handle_window_commands", 0); while let Ok(window_command) = self.window_command_receiver.try_recv() { match window_command { WindowCommand::TitleChanged(new_title) => self.handle_title_changed(new_title), @@ -142,6 +147,7 @@ impl GlutinWindowWrapper { } pub fn handle_event(&mut self, event: Event<()>) { + tracy_zone!("handle_event", 0); self.keyboard_manager.handle_event(&event); self.mouse_manager.handle_event( &event, @@ -194,6 +200,7 @@ impl GlutinWindowWrapper { } pub fn draw_frame(&mut self, dt: f32) { + tracy_zone!("draw_frame"); let window = self.windowed_context.window(); let new_size = window.inner_size(); @@ -466,6 +473,8 @@ pub fn create_window() { window_command_receiver, }; + tracy_create_gpu_context("main_render_context"); + let mut previous_frame_start = Instant::now(); enum FocusedState { From d347cad8fdbbbf6c9625c0fb05243ba67bf81b6d Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Wed, 16 Nov 2022 10:23:17 +0200 Subject: [PATCH 06/53] Add a profiling build profile Basically release with debug symbols --- Cargo.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index cdfe81dc2..2b92d7fa2 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,6 +80,11 @@ lto = true incremental = true strip = true +[profile.profiling] +inherits = "release" +strip = false +debug = true + [package.metadata.bundle] name = "Neovide" identifier = "com.neovide.neovide" From 5a8113ece37a0d9e8f78bc65e5d7c5a0790ae282 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 7 Jan 2023 23:08:38 +0200 Subject: [PATCH 07/53] Split the grid into separate lines --- src/editor/grid.rs | 130 ++++++++++++++++++++++--------------------- src/editor/window.rs | 49 +++++++++------- 2 files changed, 96 insertions(+), 83 deletions(-) diff --git a/src/editor/grid.rs b/src/editor/grid.rs index 0e3ff23b9..d0198bb34 100644 --- a/src/editor/grid.rs +++ b/src/editor/grid.rs @@ -11,74 +11,82 @@ macro_rules! default_cell { }; } +#[derive(Clone)] +struct GridLine { + characters: Vec, +} + +impl GridLine { + pub fn new(length: usize) -> GridLine { + GridLine { + characters: vec![default_cell!(); length], + } + } +} + pub struct CharacterGrid { - pub width: u64, - pub height: u64, + pub width: usize, + pub height: usize, - characters: Vec, + lines: Vec, } impl CharacterGrid { - pub fn new(size: (u64, u64)) -> CharacterGrid { - let (width, height) = size; - let cell_count = (width * height) as usize; + pub fn new((width, height): (usize, usize)) -> CharacterGrid { CharacterGrid { - characters: vec![default_cell!(); cell_count], width, height, + lines: vec![GridLine::new(width); height], } } - pub fn resize(&mut self, (width, height): (u64, u64)) { - let new_cell_count = (width * height) as usize; - let mut new_characters = vec![default_cell!(); new_cell_count]; + pub fn resize(&mut self, (width, height): (usize, usize)) { + let mut new_lines = vec![GridLine::new(width); height]; for x in 0..self.width.min(width) { - for y in 0..self.height.min(height) { + for (y, new_line) in new_lines + .iter_mut() + .enumerate() + .take(self.height.min(height)) + { if let Some(existing_cell) = self.get_cell(x, y) { - new_characters[(x + y * width) as usize] = existing_cell.clone(); + new_line.characters[x] = existing_cell.clone(); } } } self.width = width; self.height = height; - self.characters = new_characters; + self.lines = new_lines; } pub fn clear(&mut self) { self.set_all_characters(default_cell!()); } - fn cell_index(&self, x: u64, y: u64) -> Option { - if x >= self.width || y >= self.height { - None - } else { - Some((x + y * self.width) as usize) - } + pub fn get_cell(&self, x: usize, y: usize) -> Option<&GridCell> { + self.lines + .get(y) + .and_then(|line| line.characters.get(x)) } - pub fn get_cell(&self, x: u64, y: u64) -> Option<&GridCell> { - self.cell_index(x, y).map(|idx| &self.characters[idx]) - } - - pub fn get_cell_mut(&mut self, x: u64, y: u64) -> Option<&mut GridCell> { - self.cell_index(x, y) - .map(move |idx| &mut self.characters[idx]) + pub fn get_cell_mut(&mut self, x: usize, y: usize) -> Option<&mut GridCell> { + self.lines + .get_mut(y) + .and_then(|line| line.characters.get_mut(x)) } pub fn set_all_characters(&mut self, value: GridCell) { - self.characters.clear(); - self.characters - .resize_with((self.width * self.height) as usize, || value.clone()); + for line in &mut self.lines { + for grid in &mut line.characters { + *grid = value.clone() + } + } } - pub fn row(&self, row_index: u64) -> Option<&[GridCell]> { + pub fn row(&self, row_index: usize) -> Option<&[GridCell]> { if row_index < self.height { - Some( - &self.characters - [(row_index * self.width) as usize..((row_index + 1) * self.width) as usize], - ) + Some(&self.lines[row_index].characters[..]) } else { None } @@ -94,22 +102,20 @@ mod tests { #[derive(Debug)] struct Context { none_colors: Colors, - size: (u64, u64), - x: u64, - y: u64, - area: usize, - index: usize, + size: (usize, usize), + x: usize, + y: usize, } impl Context { fn new() -> Self { let size = ( - (thread_rng().gen::() % 500) + 1, - (thread_rng().gen::() % 500) + 1, + (thread_rng().gen::() % 500) + 1, + (thread_rng().gen::() % 500) + 1, ); let (x, y) = ( - thread_rng().gen::() % size.0, - thread_rng().gen::() % size.1, + thread_rng().gen::() % size.0, + thread_rng().gen::() % size.1, ); Self { none_colors: Colors { @@ -120,8 +126,14 @@ mod tests { size, x, y, - area: (size.0 * size.1) as usize, - index: (x + y * size.0) as usize, + } + } + } + + fn assert_all_cells_equal_to(context: &Context, grid: &CharacterGrid, cell: &GridCell) { + for x in 0..context.size.0 { + for y in 0..context.size.1 { + assert_eq!(grid.get_cell(x, y), Some(cell)); } } } @@ -134,10 +146,7 @@ mod tests { let character_grid = CharacterGrid::new(context.size); assert_eq!(character_grid.width, context.size.0); assert_eq!(character_grid.height, context.size.1); - assert_eq!( - character_grid.characters, - vec![default_cell!(); context.area] - ); + assert_all_cells_equal_to(&context, &character_grid, &default_cell!()); } #[test] @@ -145,7 +154,7 @@ mod tests { let context = Context::new(); let mut character_grid = CharacterGrid::new(context.size); - character_grid.characters[context.index] = ( + character_grid.lines[context.y].characters[context.x] = ( "foo".to_string(), Some(Arc::new(Style::new(context.none_colors.clone()))), ); @@ -166,7 +175,7 @@ mod tests { let context = Context::new(); let mut character_grid = CharacterGrid::new(context.size); - character_grid.characters[context.index] = ( + character_grid.lines[context.y].characters[context.x] = ( "foo".to_string(), Some(Arc::new(Style::new(context.none_colors.clone()))), ); @@ -193,13 +202,13 @@ mod tests { let context = Context::new(); let grid_cell = ( "foo".to_string(), - Some(Arc::new(Style::new(context.none_colors))), + Some(Arc::new(Style::new(context.none_colors.clone()))), ); let mut character_grid = CharacterGrid::new(context.size); // RUN FUNCTION character_grid.set_all_characters(grid_cell.clone()); - assert_eq!(character_grid.characters, vec![grid_cell; context.area]); + assert_all_cells_equal_to(&context, &character_grid, &grid_cell); } #[test] @@ -209,19 +218,16 @@ mod tests { let grid_cell = ( "foo".to_string(), - Some(Arc::new(Style::new(context.none_colors))), + Some(Arc::new(Style::new(context.none_colors.clone()))), ); - character_grid.characters = vec![grid_cell; context.area]; + character_grid.set_all_characters(grid_cell.clone()); // RUN FUNCTION character_grid.clear(); assert_eq!(character_grid.width, context.size.0); assert_eq!(character_grid.height, context.size.1); - assert_eq!( - character_grid.characters, - vec![default_cell!(); context.area] - ); + assert_all_cells_equal_to(&context, &character_grid, &default_cell!()); } #[test] @@ -229,15 +235,15 @@ mod tests { let context = Context::new(); let mut character_grid = CharacterGrid::new(context.size); let (width, height) = ( - (thread_rng().gen::() % 500) + 1, - (thread_rng().gen::() % 500) + 1, + (thread_rng().gen::() % 500) + 1, + (thread_rng().gen::() % 500) + 1, ); let grid_cell = ( "foo".to_string(), Some(Arc::new(Style::new(context.none_colors))), ); - character_grid.characters = vec![grid_cell.clone(); context.area]; + character_grid.set_all_characters(grid_cell.clone()); // RUN FUNCTION character_grid.resize((width, height)); diff --git a/src/editor/window.rs b/src/editor/window.rs index 250c43d9e..5a50428c5 100644 --- a/src/editor/window.rs +++ b/src/editor/window.rs @@ -36,7 +36,7 @@ impl Window { ) -> Window { let window = Window { grid_id, - grid: CharacterGrid::new(grid_size), + grid: CharacterGrid::new((grid_size.0 as usize, grid_size.1 as usize)), window_type, anchor_info, grid_position, @@ -58,7 +58,7 @@ impl Window { fn send_updated_position(&self) { self.send_command(WindowDrawCommand::Position { grid_position: self.grid_position, - grid_size: (self.grid.width, self.grid.height), + grid_size: (self.grid.width as u64, self.grid.height as u64), floating_order: self.anchor_info.clone().map(|anchor| anchor.sort_order), }); } @@ -68,12 +68,18 @@ impl Window { window_left: u64, window_top: u64, ) -> (String, Option>, bool) { - let grid_cell = match self.grid.get_cell(window_left, window_top) { + let grid_cell = match self + .grid + .get_cell(window_left as usize, window_top as usize) + { Some((character, style)) => (character.clone(), style.clone()), _ => (' '.to_string(), None), }; - let double_width = match self.grid.get_cell(window_left + 1, window_top) { + let double_width = match self + .grid + .get_cell(window_left as usize + 1, window_top as usize) + { Some((character, _)) => character.is_empty(), _ => false, }; @@ -82,11 +88,11 @@ impl Window { } pub fn get_width(&self) -> u64 { - self.grid.width + self.grid.width as u64 } pub fn get_height(&self) -> u64 { - self.grid.height + self.grid.height as u64 } pub fn get_grid_position(&self) -> (f64, f64) { @@ -99,7 +105,8 @@ impl Window { grid_size: (u64, u64), grid_position: (f64, f64), ) { - self.grid.resize(grid_size); + self.grid + .resize((grid_size.0 as usize, grid_size.1 as usize)); self.anchor_info = anchor_info; self.grid_position = grid_position; self.send_updated_position(); @@ -107,15 +114,15 @@ impl Window { } pub fn resize(&mut self, new_size: (u64, u64)) { - self.grid.resize(new_size); + self.grid.resize((new_size.0 as usize, new_size.1 as usize)); self.send_updated_position(); self.redraw(); } fn modify_grid( &mut self, - row_index: u64, - column_pos: &mut u64, + row_index: usize, + column_pos: &mut usize, cell: GridLineCell, defined_styles: &HashMap>, previous_style: &mut Option>, @@ -153,16 +160,15 @@ impl Window { // Build a line fragment for the given row starting from current_start up until the next style // change or double width character. - fn build_line_fragment(&self, row_index: u64, start: u64) -> (u64, LineFragment) { + fn build_line_fragment(&self, row_index: usize, start: usize) -> (usize, LineFragment) { let row = self.grid.row(row_index).unwrap(); - let (_, style) = &row[start as usize]; + let (_, style) = &row[start]; let mut text = String::new(); let mut width = 0; - for possible_end_index in start..self.grid.width { - let (character, possible_end_style) = &row[possible_end_index as usize]; + for (character, possible_end_style) in row.iter().take(self.grid.width).skip(start) { // Style doesn't match. Draw what we've got. if style != possible_end_style { break; @@ -180,9 +186,9 @@ impl Window { let line_fragment = LineFragment { text, - window_left: start, - window_top: row_index, - width, + window_left: start as u64, + window_top: row_index as u64, + width: width as u64, style: style.clone(), }; @@ -192,7 +198,7 @@ impl Window { // Redraw line by calling build_line_fragment starting at 0 // until current_start is greater than the grid width and sending the resulting // fragments as a batch. - fn redraw_line(&self, row: u64) { + fn redraw_line(&self, row: usize) { let mut current_start = 0; let mut line_fragments = Vec::new(); while current_start < self.grid.width { @@ -211,8 +217,9 @@ impl Window { defined_styles: &HashMap>, ) { let mut previous_style = None; + let row = row as usize; if row < self.grid.height { - let mut column_pos = column_start; + let mut column_pos = column_start as usize; for cell in cells { self.modify_grid( row, @@ -284,11 +291,11 @@ impl Window { for x in x_iter { let dest_x = x - cols; - let cell_data = self.grid.get_cell(x as u64, y as u64).cloned(); + let cell_data = self.grid.get_cell(x as usize, y as usize).cloned(); if let Some(cell_data) = cell_data { if let Some(dest_cell) = - self.grid.get_cell_mut(dest_x as u64, dest_y as u64) + self.grid.get_cell_mut(dest_x as usize, dest_y as usize) { *dest_cell = cell_data; } From d4b3ce3b3a3928a20e65118e26cf0b0543b8964f Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 8 Jan 2023 13:52:25 +0200 Subject: [PATCH 08/53] Move scroll_region to the grid --- src/editor/grid.rs | 46 +++++++++++++++++++++++++++++++++++++++++ src/editor/window.rs | 49 +++++++++----------------------------------- 2 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/editor/grid.rs b/src/editor/grid.rs index d0198bb34..43a7ebe10 100644 --- a/src/editor/grid.rs +++ b/src/editor/grid.rs @@ -91,6 +91,52 @@ impl CharacterGrid { None } } + + pub fn scroll_region( + &mut self, + top: usize, + bottom: usize, + left: usize, + right: usize, + rows: isize, + cols: isize, + ) { + let mut top_to_bottom; + let mut bottom_to_top; + let y_iter: &mut dyn Iterator = if rows > 0 { + top_to_bottom = (top as isize + rows) as usize..bottom; + &mut top_to_bottom + } else { + bottom_to_top = (top..(bottom as isize + rows) as usize).rev(); + &mut bottom_to_top + }; + + for y in y_iter { + let dest_y = y as isize - rows; + let mut cols_left; + let mut cols_right; + if dest_y >= 0 && dest_y < self.height as isize { + let x_iter: &mut dyn Iterator = if cols > 0 { + cols_left = (left as isize + cols) as usize..right; + &mut cols_left + } else { + cols_right = (left..(right as isize + cols) as usize).rev(); + &mut cols_right + }; + + for x in x_iter { + let dest_x = ((x as isize) - cols) as usize; + let cell_data = self.get_cell(x, y).cloned(); + + if let Some(cell_data) = cell_data { + if let Some(dest_cell) = self.get_cell_mut(dest_x, dest_y as usize) { + *dest_cell = cell_data; + } + } + } + } + } + } } #[cfg(test)] diff --git a/src/editor/window.rs b/src/editor/window.rs index 5a50428c5..f79204aac 100644 --- a/src/editor/window.rs +++ b/src/editor/window.rs @@ -255,16 +255,17 @@ impl Window { rows: i64, cols: i64, ) { - let mut top_to_bottom; - let mut bottom_to_top; - let y_iter: &mut dyn Iterator = if rows > 0 { - top_to_bottom = (top as i64 + rows)..bottom as i64; - &mut top_to_bottom - } else { - bottom_to_top = (top as i64..(bottom as i64 + rows)).rev(); - &mut bottom_to_top - }; + self.grid.scroll_region( + top as usize, + bottom as usize, + left as usize, + right as usize, + rows as isize, + cols as isize, + ); + // Scrolls must not only translate the rendered texture, but also must move the grid data + // accordingly so that future renders work correctly. self.send_command(WindowDrawCommand::Scroll { top, bottom, @@ -273,36 +274,6 @@ impl Window { rows, cols, }); - - // Scrolls must not only translate the rendered texture, but also must move the grid data - // accordingly so that future renders work correctly. - for y in y_iter { - let dest_y = y - rows; - let mut cols_left; - let mut cols_right; - if dest_y >= 0 && dest_y < self.grid.height as i64 { - let x_iter: &mut dyn Iterator = if cols > 0 { - cols_left = (left as i64 + cols)..right as i64; - &mut cols_left - } else { - cols_right = (left as i64..(right as i64 + cols)).rev(); - &mut cols_right - }; - - for x in x_iter { - let dest_x = x - cols; - let cell_data = self.grid.get_cell(x as usize, y as usize).cloned(); - - if let Some(cell_data) = cell_data { - if let Some(dest_cell) = - self.grid.get_cell_mut(dest_x as usize, dest_y as usize) - { - *dest_cell = cell_data; - } - } - } - } - } } pub fn clear(&mut self) { From eeb61077be052690043e3151729050f00418b9e8 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 8 Jan 2023 17:49:02 +0200 Subject: [PATCH 09/53] Add some unit tests for grid scrolling --- src/editor/grid.rs | 163 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/src/editor/grid.rs b/src/editor/grid.rs index 43a7ebe10..ee1145aed 100644 --- a/src/editor/grid.rs +++ b/src/editor/grid.rs @@ -176,6 +176,13 @@ mod tests { } } + fn set_grid_line_to_chars(grid: &mut CharacterGrid, row: usize, value: &str) { + assert_eq!(value.len(), grid.width); + for (col_nr, chr) in value.chars().enumerate() { + *grid.get_cell_mut(col_nr, row).unwrap() = (chr.to_string(), None); + } + } + fn assert_all_cells_equal_to(context: &Context, grid: &CharacterGrid, cell: &GridCell) { for x in 0..context.size.0 { for y in 0..context.size.1 { @@ -184,6 +191,28 @@ mod tests { } } + fn assert_grid_cell_equal_to_char(grid: &CharacterGrid, x: usize, y: usize, chr: &str) { + let chr = chr.to_string(); + let value = (chr, None); + let cell = Some(&value); + assert_eq!(grid.get_cell(x, y), cell); + } + + fn create_initialized_grid(lines: &Vec<&str>) -> CharacterGrid { + let num_lines = lines.len(); + assert_ne!(num_lines, 0); + let line_lengths: Vec = lines.iter().map(|s| s.len()).collect(); + let num_columns = line_lengths[0]; + assert_eq!(line_lengths, vec![num_columns; num_lines]); + let mut grid = CharacterGrid::new((num_columns, num_lines)); + for (row_nr, line) in lines.iter().enumerate() { + for (col_nr, chr) in line.chars().enumerate() { + *grid.get_cell_mut(col_nr, row_nr).unwrap() = (chr.to_string(), None); + } + } + grid + } + #[test] fn new_constructs_grid() { let context = Context::new(); @@ -310,4 +339,138 @@ mod tests { } } } + + #[test] + fn scroll_down_moves_the_grid_correctly() { + let mut grid = create_initialized_grid(&["abcd", "efgh", "ijkl", "mnop"].to_vec()); + + grid.scroll_region(0, 4, 0, 4, 2, 0); + assert_grid_cell_equal_to_char(&grid, 0, 0, "i"); + assert_grid_cell_equal_to_char(&grid, 3, 0, "l"); + assert_grid_cell_equal_to_char(&grid, 0, 1, "m"); + } + + #[test] + fn scroll_up_moves_the_grid_correctly() { + let mut grid = create_initialized_grid(&["abcd", "efgh", "ijkl", "mnop"].to_vec()); + + grid.scroll_region(0, 4, 0, 4, -2, 0); + assert_grid_cell_equal_to_char(&grid, 0, 2, "a"); + assert_grid_cell_equal_to_char(&grid, 0, 3, "e"); + assert_grid_cell_equal_to_char(&grid, 3, 3, "h"); + } + + #[test] + fn partial_scroll_lines_down_moves_the_grid_correctly() { + let mut grid = create_initialized_grid(&["abcd", "efgh", "ijkl", "mnop"].to_vec()); + + grid.scroll_region(1, 3, 0, 4, 1, 0); + // The initial line is not touched + assert_grid_cell_equal_to_char(&grid, 0, 0, "a"); + + assert_grid_cell_equal_to_char(&grid, 0, 1, "i"); + assert_grid_cell_equal_to_char(&grid, 3, 1, "l"); + + // The last line is not touched either + assert_grid_cell_equal_to_char(&grid, 0, 3, "m"); + } + + #[test] + fn partial_scroll_lines_up_moves_the_grid_correctly() { + let mut grid = create_initialized_grid(&["abcd", "efgh", "ijkl", "mnop"].to_vec()); + + grid.scroll_region(1, 3, 0, 4, -1, 0); + // The initial line is not touched + assert_grid_cell_equal_to_char(&grid, 0, 0, "a"); + + assert_grid_cell_equal_to_char(&grid, 0, 2, "e"); + assert_grid_cell_equal_to_char(&grid, 3, 2, "h"); + + // The last line is not touched either + assert_grid_cell_equal_to_char(&grid, 0, 3, "m"); + } + + #[test] + fn scroll_left_moves_the_grid_correctly() { + let mut grid = create_initialized_grid(&["abcd", "efgh", "ijkl", "mnop"].to_vec()); + + grid.scroll_region(0, 4, 0, 4, 0, 1); + assert_grid_cell_equal_to_char(&grid, 0, 0, "b"); + assert_grid_cell_equal_to_char(&grid, 2, 2, "l"); + } + + #[test] + fn scroll_right_moves_the_grid_correctly() { + let mut grid = create_initialized_grid(&["abcd", "efgh", "ijkl", "mnop"].to_vec()); + + grid.scroll_region(0, 4, 0, 4, 0, -3); + assert_grid_cell_equal_to_char(&grid, 3, 0, "a"); + assert_grid_cell_equal_to_char(&grid, 3, 3, "m"); + } + + #[test] + fn scroll_inner_box_diagonally_moves_the_grid_correctly() { + let mut grid = create_initialized_grid(&["abcd", "efgh", "ijkl", "mnop"].to_vec()); + + grid.scroll_region(1, 3, 1, 3, 1, 1); + // The first row is preserved + assert_grid_cell_equal_to_char(&grid, 0, 0, "a"); + assert_grid_cell_equal_to_char(&grid, 1, 0, "b"); + + // The first character is not touched + assert_grid_cell_equal_to_char(&grid, 0, 1, "e"); + + // Only k is part of the box now + assert_grid_cell_equal_to_char(&grid, 1, 1, "k"); + + // The last character is not touched + assert_grid_cell_equal_to_char(&grid, 3, 1, "h"); + + // The last row is preserved + assert_grid_cell_equal_to_char(&grid, 0, 3, "m"); + } + + #[test] + fn scrolling_one_screen_down_works() { + let mut grid = create_initialized_grid(&["1", "2", "3", "4"].to_vec()); + // Scroll down one screen + grid.scroll_region(0, 4, 0, 1, 4, 0); + set_grid_line_to_chars(&mut grid, 0, "5"); + set_grid_line_to_chars(&mut grid, 1, "6"); + set_grid_line_to_chars(&mut grid, 2, "7"); + set_grid_line_to_chars(&mut grid, 3, "8"); + } + + #[test] + fn scrolling_more_than_one_screen_down_works_makes_a_small_jump() { + let mut grid = create_initialized_grid(&["1", "2", "3", "4"].to_vec()); + // Scroll down one screen + grid.scroll_region(0, 4, 0, 1, 4, 0); + set_grid_line_to_chars(&mut grid, 0, "5"); + set_grid_line_to_chars(&mut grid, 1, "6"); + set_grid_line_to_chars(&mut grid, 2, "7"); + set_grid_line_to_chars(&mut grid, 3, "8"); + } + + #[test] + fn scrolling_one_screen_up_works() { + let mut grid = create_initialized_grid(&["5", "6", "7", "8"].to_vec()); + // Scroll up one screen + grid.scroll_region(0, 4, 0, 1, -4, 0); + set_grid_line_to_chars(&mut grid, 0, "1"); + set_grid_line_to_chars(&mut grid, 1, "2"); + set_grid_line_to_chars(&mut grid, 2, "3"); + set_grid_line_to_chars(&mut grid, 3, "4"); + } + + #[test] + fn scrolling_more_than_one_screen_up_works_makes_a_small_jump() { + let mut grid = create_initialized_grid(&["5", "6", "7", "8"].to_vec()); + // Scroll up one screen + grid.scroll_region(0, 4, 0, 1, -4, 0); + set_grid_line_to_chars(&mut grid, 0, "1"); + set_grid_line_to_chars(&mut grid, 1, "2"); + set_grid_line_to_chars(&mut grid, 2, "3"); + set_grid_line_to_chars(&mut grid, 3, "4"); + } } From 5b35ede6e5bb2c62adf8912c44e8d0a89f3a92b9 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 8 Jan 2023 22:18:45 +0200 Subject: [PATCH 10/53] Use a ringbuffer to represent the grid This optimizes the scrolling a bit, especially pure up/down. --- src/editor/grid.rs | 81 ++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 32 deletions(-) diff --git a/src/editor/grid.rs b/src/editor/grid.rs index ee1145aed..7aac754b3 100644 --- a/src/editor/grid.rs +++ b/src/editor/grid.rs @@ -29,6 +29,7 @@ pub struct CharacterGrid { pub height: usize, lines: Vec, + top_index: isize, } impl CharacterGrid { @@ -36,6 +37,7 @@ impl CharacterGrid { CharacterGrid { width, height, + top_index: 0, lines: vec![GridLine::new(width); height], } } @@ -58,21 +60,25 @@ impl CharacterGrid { self.width = width; self.height = height; self.lines = new_lines; + self.top_index = 0; } pub fn clear(&mut self) { self.set_all_characters(default_cell!()); + self.top_index = 0; } pub fn get_cell(&self, x: usize, y: usize) -> Option<&GridCell> { + let index = self.get_row_array_index(y as isize); self.lines - .get(y) + .get(index) .and_then(|line| line.characters.get(x)) } pub fn get_cell_mut(&mut self, x: usize, y: usize) -> Option<&mut GridCell> { + let index = self.get_row_array_index(y as isize); self.lines - .get_mut(y) + .get_mut(index) .and_then(|line| line.characters.get_mut(x)) } @@ -86,7 +92,7 @@ impl CharacterGrid { pub fn row(&self, row_index: usize) -> Option<&[GridCell]> { if row_index < self.height { - Some(&self.lines[row_index].characters[..]) + Some(&self.lines[self.get_row_array_index(row_index as isize)].characters[..]) } else { None } @@ -101,42 +107,53 @@ impl CharacterGrid { rows: isize, cols: isize, ) { - let mut top_to_bottom; - let mut bottom_to_top; - let y_iter: &mut dyn Iterator = if rows > 0 { - top_to_bottom = (top as isize + rows) as usize..bottom; - &mut top_to_bottom + if top == 0 && bottom == self.height && left == 0 && right == self.width && cols == 0 { + // Pure up/down scrolling is optimized, and furthermore does not destroy the region + // that has been scrolled out + self.top_index += rows; } else { - bottom_to_top = (top..(bottom as isize + rows) as usize).rev(); - &mut bottom_to_top - }; - - for y in y_iter { - let dest_y = y as isize - rows; - let mut cols_left; - let mut cols_right; - if dest_y >= 0 && dest_y < self.height as isize { - let x_iter: &mut dyn Iterator = if cols > 0 { - cols_left = (left as isize + cols) as usize..right; - &mut cols_left - } else { - cols_right = (left..(right as isize + cols) as usize).rev(); - &mut cols_right - }; - - for x in x_iter { - let dest_x = ((x as isize) - cols) as usize; - let cell_data = self.get_cell(x, y).cloned(); - - if let Some(cell_data) = cell_data { - if let Some(dest_cell) = self.get_cell_mut(dest_x, dest_y as usize) { - *dest_cell = cell_data; + let mut top_to_bottom; + let mut bottom_to_top; + let y_iter: &mut dyn Iterator = if rows > 0 { + top_to_bottom = (top as isize + rows) as usize..bottom; + &mut top_to_bottom + } else { + bottom_to_top = (top..(bottom as isize + rows) as usize).rev(); + &mut bottom_to_top + }; + + for y in y_iter { + let dest_y = y as isize - rows; + let mut cols_left; + let mut cols_right; + if dest_y >= 0 && dest_y < self.height as isize { + let x_iter: &mut dyn Iterator = if cols > 0 { + cols_left = (left as isize + cols) as usize..right; + &mut cols_left + } else { + cols_right = (left..(right as isize + cols) as usize).rev(); + &mut cols_right + }; + + for x in x_iter { + let dest_x = ((x as isize) - cols) as usize; + let cell_data = self.get_cell(x, y).cloned(); + + if let Some(cell_data) = cell_data { + if let Some(dest_cell) = self.get_cell_mut(dest_x, dest_y as usize) { + *dest_cell = cell_data; + } } } } } } } + + fn get_row_array_index(&self, index: isize) -> usize { + let rows = self.lines.len() as isize; + (self.top_index + index).rem_euclid(rows) as usize + } } #[cfg(test)] From 852de6328737162721fc84526606f53696bff4f9 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 14 Jan 2023 18:46:38 +0200 Subject: [PATCH 11/53] Draw each line as a skia picture This currently disables smooth scrolling. Which will be re-implemented using the scroll events and the skia pictures instead of the old scrolling snapshots. --- src/editor/grid.rs | 7 +- src/editor/window.rs | 28 ++++- src/renderer/rendered_window.rs | 181 ++++++++++---------------------- 3 files changed, 88 insertions(+), 128 deletions(-) diff --git a/src/editor/grid.rs b/src/editor/grid.rs index 7aac754b3..7e22d6e21 100644 --- a/src/editor/grid.rs +++ b/src/editor/grid.rs @@ -98,6 +98,7 @@ impl CharacterGrid { } } + // Returns true if it's a pure up/down scroll pub fn scroll_region( &mut self, top: usize, @@ -106,11 +107,12 @@ impl CharacterGrid { right: usize, rows: isize, cols: isize, - ) { + ) -> bool { if top == 0 && bottom == self.height && left == 0 && right == self.width && cols == 0 { // Pure up/down scrolling is optimized, and furthermore does not destroy the region // that has been scrolled out self.top_index += rows; + true } else { let mut top_to_bottom; let mut bottom_to_top; @@ -147,10 +149,11 @@ impl CharacterGrid { } } } + false } } - fn get_row_array_index(&self, index: isize) -> usize { + pub fn get_row_array_index(&self, index: isize) -> usize { let rows = self.lines.len() as isize; (self.top_index + index).rem_euclid(rows) as usize } diff --git a/src/editor/window.rs b/src/editor/window.rs index f79204aac..900038a95 100644 --- a/src/editor/window.rs +++ b/src/editor/window.rs @@ -105,6 +105,8 @@ impl Window { grid_size: (u64, u64), grid_position: (f64, f64), ) { + // This could perhaps be optimized, setting the position does not necessarily need + // to rezize, and reset everything self.grid .resize((grid_size.0 as usize, grid_size.1 as usize)); self.anchor_info = anchor_info; @@ -187,7 +189,6 @@ impl Window { let line_fragment = LineFragment { text, window_left: start as u64, - window_top: row_index as u64, width: width as u64, style: style.clone(), }; @@ -206,7 +207,10 @@ impl Window { current_start = next_start; line_fragments.push(line_fragment); } - self.send_command(WindowDrawCommand::DrawLine(line_fragments)); + self.send_command(WindowDrawCommand::DrawLine { + row, + line_fragments, + }); } pub fn draw_grid_line( @@ -255,7 +259,7 @@ impl Window { rows: i64, cols: i64, ) { - self.grid.scroll_region( + let is_pure_updown = self.grid.scroll_region( top as usize, bottom as usize, left as usize, @@ -274,6 +278,24 @@ impl Window { rows, cols, }); + + // There's no need to send any updates for pure up/down scrolling, the actual new lines + // will be sent later + if !is_pure_updown { + let mut top = top as isize; + let mut bottom = bottom as isize; + // Send only the scrolled lines + // neovim will send the rest later + if rows > 0 { + bottom -= rows as isize; + } else { + top -= rows as isize; + } + + for row in top..bottom { + self.redraw_line(row as usize); + } + } } pub fn clear(&mut self) { diff --git a/src/renderer/rendered_window.rs b/src/renderer/rendered_window.rs index ad906d2f8..ac9d8188d 100644 --- a/src/renderer/rendered_window.rs +++ b/src/renderer/rendered_window.rs @@ -1,11 +1,9 @@ -use std::{collections::VecDeque, sync::Arc}; +use std::sync::Arc; use skia_safe::{ - canvas::{SaveLayerRec, SrcRectConstraint}, - gpu::SurfaceOrigin, - image_filters::blur, - BlendMode, Budgeted, Canvas, Color, Image, ImageInfo, Paint, Point, Rect, SamplingOptions, - Surface, SurfaceProps, SurfacePropsFlags, + canvas::SaveLayerRec, gpu::SurfaceOrigin, image_filters::blur, BlendMode, Budgeted, Canvas, + Color, ImageInfo, Matrix, Paint, Picture, PictureRecorder, Point, Rect, Surface, SurfaceProps, + SurfacePropsFlags, }; use crate::{ @@ -20,7 +18,6 @@ use crate::{ pub struct LineFragment { pub text: String, pub window_left: u64, - pub window_top: u64, pub width: u64, pub style: Option>, } @@ -32,7 +29,10 @@ pub enum WindowDrawCommand { grid_size: (u64, u64), floating_order: Option, }, - DrawLine(Vec), + DrawLine { + row: usize, + line_fragments: Vec, + }, Scroll { top: u64, bottom: u64, @@ -99,11 +99,6 @@ fn build_window_surface_with_grid_size( surface } -pub struct LocatedSnapshot { - image: Image, - top_line: u64, -} - pub struct LocatedSurface { surface: Surface, pub top_line: u64, @@ -120,18 +115,9 @@ impl LocatedSurface { LocatedSurface { surface, top_line } } - - fn snapshot(&mut self) -> LocatedSnapshot { - let image = self.surface.image_snapshot(); - LocatedSnapshot { - image, - top_line: self.top_line, - } - } } pub struct RenderedWindow { - snapshots: VecDeque, pub current_surface: LocatedSurface, pub id: u64, @@ -140,6 +126,9 @@ pub struct RenderedWindow { pub grid_size: Dimensions, + lines: Vec>, + top_index: isize, + grid_start_position: Point, pub grid_current_position: Point, grid_destination: Point, @@ -172,7 +161,6 @@ impl RenderedWindow { let current_surface = LocatedSurface::new(parent_canvas, grid_renderer, grid_size, 0); RenderedWindow { - snapshots: VecDeque::new(), current_surface, id, hidden: false, @@ -180,6 +168,9 @@ impl RenderedWindow { grid_size, + lines: vec![None; (grid_size.height * 2) as usize], + top_index: 0, + grid_start_position: grid_position, grid_current_position: grid_position, grid_destination: grid_position, @@ -229,7 +220,6 @@ impl RenderedWindow { if 1.0 - self.scroll_t < std::f32::EPSILON { // We are at destination, move t out of 0-1 range to stop the animation. self.scroll_t = 2.0; - self.snapshots.clear(); } else { animating = true; self.scroll_t = (self.scroll_t + dt / settings.scroll_animation_length).min(1.0); @@ -246,6 +236,20 @@ impl RenderedWindow { animating } + fn draw_surface(&mut self, font_dimensions: Dimensions) { + let canvas = self.current_surface.surface.canvas(); + let mut matrix = Matrix::new_identity(); + + for i in 0..self.grid_size.height { + matrix.set_translate((0.0, (i * font_dimensions.height) as f32)); + let line_index = + (self.top_index + i as isize).rem_euclid(self.lines.len() as isize) as usize; + if let Some(picture) = &self.lines[line_index] { + canvas.draw_picture(picture, Some(&matrix), None); + } + } + } + pub fn draw( &mut self, root_canvas: &mut Canvas, @@ -258,6 +262,8 @@ impl RenderedWindow { REDRAW_SCHEDULER.queue_next_frame(); } + self.draw_surface(font_dimensions); + let pixel_region = self.pixel_region(font_dimensions); root_canvas.save(); @@ -303,31 +309,9 @@ impl RenderedWindow { paint.set_color(Color::from_argb(255, 255, 255, 255)); - let font_height = font_dimensions.height; - - // Draw scrolling snapshots. - for snapshot in self.snapshots.iter_mut().rev() { - let scroll_offset = (snapshot.top_line * font_height) as f32 - - (self.current_scroll * font_height as f32); - let image = &mut snapshot.image; - root_canvas.draw_image_rect( - image, - None, - pixel_region.with_offset((0.0, scroll_offset)), - &paint, - ); - } - // Draw current surface. - let scroll_offset = (self.current_surface.top_line * font_height) as f32 - - (self.current_scroll * font_height as f32); let snapshot = self.current_surface.surface.image_snapshot(); - root_canvas.draw_image_rect( - snapshot, - None, - pixel_region.with_offset((0.0, scroll_offset)), - &paint, - ); + root_canvas.draw_image_rect(snapshot, None, pixel_region, &paint); root_canvas.restore(); @@ -386,22 +370,19 @@ impl RenderedWindow { } if self.grid_size != new_grid_size { - let mut new_surface = build_window_surface_with_grid_size( + self.current_surface.surface = build_window_surface_with_grid_size( self.current_surface.surface.canvas(), grid_renderer, new_grid_size, ); - self.current_surface.surface.draw( - new_surface.canvas(), - (0.0, 0.0), - SamplingOptions::default(), - None, - ); - - self.current_surface.surface = new_surface; self.grid_size = new_grid_size; } + // This could perhaps be optimized, setting the position does not necessarily need + // to rezize + self.lines = vec![None; (new_grid_size.height * 2) as usize]; + self.top_index = 0; + self.floating_order = floating_order; if self.hidden { @@ -412,20 +393,28 @@ impl RenderedWindow { self.grid_destination = new_destination; } } - WindowDrawCommand::DrawLine(line_fragments) => { + WindowDrawCommand::DrawLine { + row, + line_fragments, + } => { tracy_zone!("draw_line_cmd", 0); - let canvas = self.current_surface.surface.canvas(); + let font_dimensions = grid_renderer.font_dimensions; + let mut recorder = PictureRecorder::new(); + + let grid_rect = Rect::from_wh( + (self.grid_size.width * font_dimensions.width) as f32, + font_dimensions.height as f32, + ); + let canvas = recorder.begin_recording(grid_rect, None); - canvas.save(); for line_fragment in line_fragments.iter() { let LineFragment { window_left, - window_top, width, style, .. } = line_fragment; - let grid_position = (*window_left, *window_top); + let grid_position = (*window_left, 0); grid_renderer.draw_background( canvas, grid_position, @@ -439,65 +428,28 @@ impl RenderedWindow { let LineFragment { text, window_left, - window_top, width, style, } = line_fragment; - let grid_position = (window_left, window_top); + let grid_position = (window_left, 0); grid_renderer.draw_foreground(canvas, text, grid_position, width, &style); } - canvas.restore(); + + let line_index = + (self.top_index + row as isize).rem_euclid(self.lines.len() as isize) as usize; + self.lines[line_index] = recorder.finish_recording_as_picture(None); } - WindowDrawCommand::Scroll { - top, - bottom, - left, - right, - rows, - cols, - } => { + WindowDrawCommand::Scroll { .. } => { tracy_zone!("scroll_cmd", 0); - let Dimensions { - width: font_width, - height: font_height, - } = grid_renderer.font_dimensions; - let scrolled_region = Rect::new( - (left * font_width) as f32, - (top * font_height) as f32, - (right * font_width) as f32, - (bottom * font_height) as f32, - ); - - let mut translated_region = scrolled_region; - translated_region.offset(( - -cols as f32 * font_width as f32, - -rows as f32 * font_height as f32, - )); - - let snapshot = self.current_surface.surface.image_snapshot(); - let canvas = self.current_surface.surface.canvas(); - - canvas.save(); - - canvas.clip_rect(scrolled_region, None, Some(false)); - canvas.draw_image_rect( - snapshot, - Some((&scrolled_region, SrcRectConstraint::Fast)), - translated_region, - &grid_renderer.paint, - ); - - canvas.restore(); } WindowDrawCommand::Clear => { tracy_zone!("clear_cmd", 0); + self.top_index = 0; self.current_surface.surface = build_window_surface_with_grid_size( self.current_surface.surface.canvas(), grid_renderer, self.grid_size, ); - - self.snapshots.clear(); } WindowDrawCommand::Show => { tracy_zone!("show_cmd", 0); @@ -512,24 +464,7 @@ impl RenderedWindow { tracy_zone!("hide_cmd", 0); self.hidden = true; } - WindowDrawCommand::Viewport { top_line, .. } => { - tracy_zone!("viewport_cmd", 0); - if self.current_surface.top_line != top_line as u64 { - let new_snapshot = self.current_surface.snapshot(); - self.snapshots.push_back(new_snapshot); - - if self.snapshots.len() > 5 { - self.snapshots.pop_front(); - } - - self.current_surface.top_line = top_line as u64; - - // Set new target viewport position and initialize animation timer. - self.start_scroll = self.current_scroll; - self.scroll_destination = top_line as f32; - self.scroll_t = 0.0; - } - } + WindowDrawCommand::Viewport { .. } => {} _ => {} }; } From 05f651fc02410efbb95cd06496c51804bc38e7b0 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 15 Jan 2023 12:38:58 +0200 Subject: [PATCH 12/53] Reimplement scrolling --- src/renderer/cursor_renderer/mod.rs | 4 +- src/renderer/rendered_window.rs | 63 ++++++++++++++++++++++------- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/src/renderer/cursor_renderer/mod.rs b/src/renderer/cursor_renderer/mod.rs index 56d56adbd..e4f503d6d 100644 --- a/src/renderer/cursor_renderer/mod.rs +++ b/src/renderer/cursor_renderer/mod.rs @@ -252,8 +252,8 @@ impl CursorRenderer { if let Some(window) = windows.get(&self.cursor.parent_window_id) { let grid_x = cursor_grid_x as f32 + window.grid_current_position.x; - let mut grid_y = cursor_grid_y as f32 + window.grid_current_position.y - - (window.current_scroll - window.current_surface.top_line as f32); + let mut grid_y = + cursor_grid_y as f32 + window.grid_current_position.y - window.current_scroll; // Prevent the cursor from targeting a position outside its current window. Since only // the vertical direction is effected by scrolling, we only have to clamp the vertical diff --git a/src/renderer/rendered_window.rs b/src/renderer/rendered_window.rs index ac9d8188d..4f1efc389 100644 --- a/src/renderer/rendered_window.rs +++ b/src/renderer/rendered_window.rs @@ -127,7 +127,7 @@ pub struct RenderedWindow { pub grid_size: Dimensions, lines: Vec>, - top_index: isize, + pub top_index: isize, grid_start_position: Point, pub grid_current_position: Point, @@ -136,7 +136,6 @@ pub struct RenderedWindow { start_scroll: f32, pub current_scroll: f32, - scroll_destination: f32, scroll_t: f32, pub padding: WindowPadding, @@ -178,7 +177,6 @@ impl RenderedWindow { start_scroll: 0.0, current_scroll: 0.0, - scroll_destination: 0.0, scroll_t: 2.0, // 2.0 is out of the 0.0 to 1.0 range and stops animation. padding, } @@ -225,12 +223,7 @@ impl RenderedWindow { self.scroll_t = (self.scroll_t + dt / settings.scroll_animation_length).min(1.0); } - self.current_scroll = ease( - ease_out_expo, - self.start_scroll, - self.scroll_destination, - self.scroll_t, - ); + self.current_scroll = ease(ease_out_expo, self.start_scroll, 0.0, self.scroll_t); } animating @@ -240,10 +233,16 @@ impl RenderedWindow { let canvas = self.current_surface.surface.canvas(); let mut matrix = Matrix::new_identity(); - for i in 0..self.grid_size.height { - matrix.set_translate((0.0, (i * font_dimensions.height) as f32)); - let line_index = - (self.top_index + i as isize).rem_euclid(self.lines.len() as isize) as usize; + let scroll_offset_lines = self.current_scroll.floor(); + let scroll_offset = scroll_offset_lines - self.current_scroll; + + for i in 0..self.grid_size.height + 1 { + matrix.set_translate(( + 0.0, + (scroll_offset + i as f32) * font_dimensions.height as f32, + )); + let line_index = (self.top_index + scroll_offset_lines as isize + i as isize) + .rem_euclid(self.lines.len() as isize) as usize; if let Some(picture) = &self.lines[line_index] { canvas.draw_picture(picture, Some(&matrix), None); } @@ -328,6 +327,11 @@ impl RenderedWindow { } } + fn reset_scroll(&mut self) { + self.start_scroll = 0.0; + self.scroll_t = 2.0; + } + pub fn handle_window_draw_command( &mut self, grid_renderer: &mut GridRenderer, @@ -392,6 +396,7 @@ impl RenderedWindow { self.grid_start_position = new_destination; self.grid_destination = new_destination; } + self.reset_scroll(); } WindowDrawCommand::DrawLine { row, @@ -439,12 +444,41 @@ impl RenderedWindow { (self.top_index + row as isize).rem_euclid(self.lines.len() as isize) as usize; self.lines[line_index] = recorder.finish_recording_as_picture(None); } - WindowDrawCommand::Scroll { .. } => { + WindowDrawCommand::Scroll { + top, + bottom, + left, + right, + rows, + cols, + } => { tracy_zone!("scroll_cmd", 0); + if top == 0 + && bottom == self.grid_size.height + && left == 0 + && right == self.grid_size.width + && cols == 0 + { + let mut scroll_offset = self.current_scroll; + self.top_index += rows as isize; + let minmax = self.lines.len() - self.grid_size.height as usize; + if rows.unsigned_abs() as usize > minmax { + // The scroll offset has to be reset when scrolling too far + scroll_offset = 0.0; + } else { + scroll_offset -= rows as f32; + // And even when scrolling in steps, we can't let it drift too far, since the + // buffer size is limited + scroll_offset = scroll_offset.clamp(-(minmax as f32), minmax as f32); + } + self.start_scroll = scroll_offset; + self.scroll_t = 0.0; + } } WindowDrawCommand::Clear => { tracy_zone!("clear_cmd", 0); self.top_index = 0; + self.reset_scroll(); self.current_surface.surface = build_window_surface_with_grid_size( self.current_surface.surface.canvas(), grid_renderer, @@ -458,6 +492,7 @@ impl RenderedWindow { self.position_t = 2.0; // We don't want to animate since the window is becoming visible, // so we set t to 2.0 to stop animations. self.grid_start_position = self.grid_destination; + self.reset_scroll(); } } WindowDrawCommand::Hide => { From 872e1619c1140dce0bd376b144f7defad4efda32 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 29 Oct 2022 16:24:29 +0300 Subject: [PATCH 13/53] Use simple pd controller for scrolling --- src/renderer/rendered_window.rs | 35 ++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/renderer/rendered_window.rs b/src/renderer/rendered_window.rs index 4f1efc389..36f4ff6da 100644 --- a/src/renderer/rendered_window.rs +++ b/src/renderer/rendered_window.rs @@ -134,9 +134,8 @@ pub struct RenderedWindow { grid_destination: Point, position_t: f32, - start_scroll: f32, pub current_scroll: f32, - scroll_t: f32, + scroll_v: f32, pub padding: WindowPadding, } @@ -175,9 +174,8 @@ impl RenderedWindow { grid_destination: grid_position, position_t: 2.0, // 2.0 is out of the 0.0 to 1.0 range and stops animation. - start_scroll: 0.0, current_scroll: 0.0, - scroll_t: 2.0, // 2.0 is out of the 0.0 to 1.0 range and stops animation. + scroll_v: 0.0, padding, } } @@ -215,15 +213,25 @@ impl RenderedWindow { } { - if 1.0 - self.scroll_t < std::f32::EPSILON { - // We are at destination, move t out of 0-1 range to stop the animation. - self.scroll_t = 2.0; + let scroll_destination = 0.0; + let zeta = 1.0; + let omega = 4.0 / (zeta * settings.scroll_animation_length); + let k_p = omega * omega; + let k_d = -2.0 * zeta * omega; + let timestep = 0.01; + let mut dt = dt; + while dt > 0.0 { + let acc = k_p * (scroll_destination - self.current_scroll) + k_d * self.scroll_v; + self.scroll_v += acc * timestep; + self.current_scroll += self.scroll_v * timestep; + dt -= timestep; + } + + if (self.current_scroll - scroll_destination).abs() < 0.01 { + self.reset_scroll(); } else { animating = true; - self.scroll_t = (self.scroll_t + dt / settings.scroll_animation_length).min(1.0); } - - self.current_scroll = ease(ease_out_expo, self.start_scroll, 0.0, self.scroll_t); } animating @@ -328,8 +336,8 @@ impl RenderedWindow { } fn reset_scroll(&mut self) { - self.start_scroll = 0.0; - self.scroll_t = 2.0; + self.current_scroll = 0.0; + self.scroll_v = 0.0; } pub fn handle_window_draw_command( @@ -471,8 +479,7 @@ impl RenderedWindow { // buffer size is limited scroll_offset = scroll_offset.clamp(-(minmax as f32), minmax as f32); } - self.start_scroll = scroll_offset; - self.scroll_t = 0.0; + self.current_scroll = scroll_offset; } } WindowDrawCommand::Clear => { From f399580b8a2b5b0d867b93f3469b15b5f88366b1 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 5 Mar 2023 13:53:58 +0200 Subject: [PATCH 14/53] Implement floating window transparency This also optimizes the blurring step, to not be done unless needed. Also removes the neovide_floating_opacity setting, since it's not clear how that should interact with neovim's default way of dealing with transparncy. --- src/renderer/grid_renderer.rs | 35 +++++--- src/renderer/rendered_window.rs | 145 ++++++++++++++++++++------------ 2 files changed, 113 insertions(+), 67 deletions(-) diff --git a/src/renderer/grid_renderer.rs b/src/renderer/grid_renderer.rs index e93c85297..e0dae8184 100644 --- a/src/renderer/grid_renderer.rs +++ b/src/renderer/grid_renderer.rs @@ -12,7 +12,6 @@ use crate::{ profiling::tracy_zone, renderer::{CachingShaper, RendererSettings}, settings::*, - window::WindowSettings, }; pub struct GridRenderer { @@ -96,21 +95,28 @@ impl GridRenderer { self.default_style.colors.background.unwrap().to_color() } + // Returns a boolean with + // The cell uses the default background color + // The cell has transparency pub fn draw_background( &mut self, canvas: &mut Canvas, grid_position: (u64, u64), cell_width: u64, style: &Option>, - is_floating: bool, - ) { + ) -> (bool, bool) { tracy_zone!("draw_background"); - self.paint.set_blend_mode(BlendMode::Src); + let debug = SETTINGS.get::().debug_renderer; + if style.is_none() && !debug { + return (false, false); + } let region = self.compute_text_region(grid_position, cell_width); let style = style.as_ref().unwrap_or(&self.default_style); - if SETTINGS.get::().debug_renderer { + self.paint.set_blend_mode(BlendMode::Src); + + if debug { let random_hsv: HSV = (rand::random::() * 360.0, 0.3, 0.3).into(); let random_color = random_hsv.to_color(255); self.paint.set_color(random_color); @@ -118,17 +124,18 @@ impl GridRenderer { self.paint .set_color(style.background(&self.default_style.colors).to_color()); } + if style.blend > 0 { + self.paint.set_alpha_f((100 - style.blend) as f32 / 100.0); + } else { + self.paint.set_alpha_f(1.0); + } - if is_floating { - self.paint - .set_alpha((255.0 * ((100 - style.blend) as f32 / 100.0)) as u8); - } else if (SETTINGS.get::().transparency - 1.0).abs() > f32::EPSILON - // Only make background color transparent - && self.paint.color() == self.get_default_background() - { - self.paint.set_alpha(0); + if self.paint.color4f() == self.default_style.colors.background.unwrap() { + (false, style.blend > 0) + } else { + canvas.draw_rect(region, &self.paint); + (true, style.blend > 0) } - canvas.draw_rect(region, &self.paint); } pub fn draw_foreground( diff --git a/src/renderer/rendered_window.rs b/src/renderer/rendered_window.rs index 36f4ff6da..c78459a08 100644 --- a/src/renderer/rendered_window.rs +++ b/src/renderer/rendered_window.rs @@ -117,6 +117,13 @@ impl LocatedSurface { } } +#[derive(Clone)] +struct Line { + background_picture: Option, + foreground_picture: Picture, + has_transparency: bool, +} + pub struct RenderedWindow { pub current_surface: LocatedSurface, @@ -126,7 +133,7 @@ pub struct RenderedWindow { pub grid_size: Dimensions, - lines: Vec>, + lines: Vec>, pub top_index: isize, grid_start_position: Point, @@ -237,24 +244,55 @@ impl RenderedWindow { animating } - fn draw_surface(&mut self, font_dimensions: Dimensions) { + fn draw_surface(&mut self, font_dimensions: Dimensions, default_background: Color) -> bool { + let image_size: (i32, i32) = (self.grid_size * font_dimensions).into(); + let pixel_region = Rect::from_size(image_size); let canvas = self.current_surface.surface.canvas(); - let mut matrix = Matrix::new_identity(); + canvas.clip_rect(pixel_region, None, Some(false)); + canvas.clear(default_background); let scroll_offset_lines = self.current_scroll.floor(); let scroll_offset = scroll_offset_lines - self.current_scroll; + let mut has_transparency = false; + + let mut background_paint = Paint::default(); + background_paint.set_blend_mode(BlendMode::Src); + background_paint.set_alpha(default_background.a()); + + let lines: Vec<(Matrix, &Line)> = (0..self.grid_size.height + 1) + .filter_map(|i| { + let line_index = (self.top_index + scroll_offset_lines as isize + i as isize) + .rem_euclid(self.lines.len() as isize) + as usize; + if let Some(line) = &self.lines[line_index] { + let mut m = Matrix::new_identity(); + m.set_translate(( + 0.0, + (scroll_offset + i as f32) * font_dimensions.height as f32, + )); + Some((m, line)) + } else { + None + } + }) + .collect(); - for i in 0..self.grid_size.height + 1 { - matrix.set_translate(( - 0.0, - (scroll_offset + i as f32) * font_dimensions.height as f32, - )); - let line_index = (self.top_index + scroll_offset_lines as isize + i as isize) - .rem_euclid(self.lines.len() as isize) as usize; - if let Some(picture) = &self.lines[line_index] { - canvas.draw_picture(picture, Some(&matrix), None); + for (matrix, line) in &lines { + if let Some(background_picture) = &line.background_picture { + has_transparency |= line.has_transparency; + canvas.draw_picture(background_picture, Some(matrix), Some(&background_paint)); } } + let mut foreground_paint = Paint::default(); + foreground_paint.set_blend_mode(BlendMode::SrcOver); + for (matrix, line) in &lines { + canvas.draw_picture( + &line.foreground_picture, + Some(matrix), + Some(&foreground_paint), + ); + } + has_transparency } pub fn draw( @@ -269,18 +307,16 @@ impl RenderedWindow { REDRAW_SCHEDULER.queue_next_frame(); } - self.draw_surface(font_dimensions); + let has_transparency = self.draw_surface(font_dimensions, default_background); let pixel_region = self.pixel_region(font_dimensions); + let transparent_floating = self.floating_order.is_some() && has_transparency; root_canvas.save(); root_canvas.clip_rect(pixel_region, None, Some(false)); + let need_blur = transparent_floating && settings.floating_blur; - if self.floating_order.is_none() { - root_canvas.clear(default_background); - } - - if self.floating_order.is_some() && settings.floating_blur { + if need_blur { if let Some(blur) = blur( ( settings.floating_blur_amount_x, @@ -290,31 +326,28 @@ impl RenderedWindow { None, None, ) { + let paint = Paint::default() + .set_anti_alias(false) + .set_blend_mode(BlendMode::Src) + .to_owned(); let save_layer_rec = SaveLayerRec::default() .backdrop(&blur) - .bounds(&pixel_region); - + .bounds(&pixel_region) + .paint(&paint); root_canvas.save_layer(&save_layer_rec); + root_canvas.restore(); } } - let mut paint = Paint::default(); - // We want each surface to overwrite the one underneath and will use layers to ensure - // only lower priority surfaces will get clobbered and not the underlying windows. - paint.set_blend_mode(BlendMode::Src); - paint.set_anti_alias(false); - - // Save layer so that setting the blend mode doesn't effect the blur. - root_canvas.save_layer(&SaveLayerRec::default()); - let mut a = 255; - if self.floating_order.is_some() { - a = (settings.floating_opacity.min(1.0).max(0.0) * 255.0) as u8; - } - - paint.set_color(default_background.with_a(a)); - root_canvas.draw_rect(pixel_region, &paint); - - paint.set_color(Color::from_argb(255, 255, 255, 255)); + let paint = Paint::default() + .set_anti_alias(false) + .set_color(Color::from_argb(255, 255, 255, 255)) + .set_blend_mode(if self.floating_order.is_some() { + BlendMode::SrcOver + } else { + BlendMode::Src + }) + .to_owned(); // Draw current surface. let snapshot = self.current_surface.surface.image_snapshot(); @@ -322,12 +355,6 @@ impl RenderedWindow { root_canvas.restore(); - if self.floating_order.is_some() { - root_canvas.restore(); - } - - root_canvas.restore(); - WindowDrawDetails { id: self.id, region: pixel_region, @@ -420,6 +447,13 @@ impl RenderedWindow { ); let canvas = recorder.begin_recording(grid_rect, None); + let line_index = + (self.top_index + row as isize).rem_euclid(self.lines.len() as isize) as usize; + + canvas.clear(grid_renderer.get_default_background()); + let mut has_transparency = false; + let mut custom_background = false; + for line_fragment in line_fragments.iter() { let LineFragment { window_left, @@ -428,15 +462,16 @@ impl RenderedWindow { .. } = line_fragment; let grid_position = (*window_left, 0); - grid_renderer.draw_background( - canvas, - grid_position, - *width, - style, - self.floating_order.is_some(), - ); + let (custom, transparent) = + grid_renderer.draw_background(canvas, grid_position, *width, style); + custom_background |= custom; + has_transparency |= transparent; } + let background_picture = custom_background + .then_some(recorder.finish_recording_as_picture(None).unwrap()); + let canvas = recorder.begin_recording(grid_rect, None); + canvas.clear(Color::from_argb(0, 255, 255, 255)); for line_fragment in line_fragments.into_iter() { let LineFragment { text, @@ -445,12 +480,16 @@ impl RenderedWindow { style, } = line_fragment; let grid_position = (window_left, 0); + grid_renderer.draw_foreground(canvas, text, grid_position, width, &style); } + let foreground_picture = recorder.finish_recording_as_picture(None).unwrap(); - let line_index = - (self.top_index + row as isize).rem_euclid(self.lines.len() as isize) as usize; - self.lines[line_index] = recorder.finish_recording_as_picture(None); + self.lines[line_index] = Some(Line { + background_picture, + foreground_picture, + has_transparency, + }); } WindowDrawCommand::Scroll { top, From 91c0273d0ec6dfbb944bd4daf2ee15df0c764104 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 5 Mar 2023 21:52:27 +0200 Subject: [PATCH 15/53] Optimize the foreground drawing a bit --- src/renderer/grid_renderer.rs | 34 +++++++++++++++++++++++++-------- src/renderer/rendered_window.rs | 17 +++++++++-------- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/renderer/grid_renderer.rs b/src/renderer/grid_renderer.rs index e0dae8184..2100c605d 100644 --- a/src/renderer/grid_renderer.rs +++ b/src/renderer/grid_renderer.rs @@ -145,12 +145,13 @@ impl GridRenderer { grid_position: (u64, u64), cell_width: u64, style: &Option>, - ) { + ) -> bool { tracy_zone!("draw_foreground"); let (x, y) = grid_position * self.font_dimensions; let width = cell_width * self.font_dimensions.width; let style = style.as_ref().unwrap_or(&self.default_style); + let mut drawn = false; // We don't want to clip text in the x position, only the y so we add a buffer of 1 // character on either side of the region so that we clip vertically but not horizontally. @@ -169,7 +170,8 @@ impl GridRenderer { (y - line_position + self.font_dimensions.height) as f32, ); - self.draw_underline(canvas, style, underline_style, p1.into(), p2.into()) + self.draw_underline(canvas, style, underline_style, p1.into(), p2.into()); + drawn = true; } canvas.save(); @@ -187,12 +189,26 @@ impl GridRenderer { } self.paint.set_anti_alias(false); - for blob in self - .shaper - .shape_cached(text, style.bold, style.italic) - .iter() - { - canvas.draw_text_blob(blob, (x as f32, (y + y_adjustment) as f32), &self.paint); + // There's a lot of overhead for empty blobs in Skia, for some reason they never hit the + // cache, so trim all the spaces + let trimmed = text.trim_start(); + let leading_spaces = text.len() - trimmed.len(); + let trimmed = trimmed.trim_end(); + let x_adjustment = leading_spaces as u64 * self.font_dimensions.width; + + if !trimmed.is_empty() { + for blob in self + .shaper + .shape_cached(trimmed.to_string(), style.bold, style.italic) + .iter() + { + canvas.draw_text_blob( + blob, + ((x + x_adjustment) as f32, (y + y_adjustment) as f32), + &self.paint, + ); + drawn = true; + } } if style.strikethrough { @@ -204,9 +220,11 @@ impl GridRenderer { ((x + width) as f32, line_position), &self.paint, ); + drawn = true; } canvas.restore(); + drawn } fn draw_underline( diff --git a/src/renderer/rendered_window.rs b/src/renderer/rendered_window.rs index c78459a08..be538eedb 100644 --- a/src/renderer/rendered_window.rs +++ b/src/renderer/rendered_window.rs @@ -120,7 +120,7 @@ impl LocatedSurface { #[derive(Clone)] struct Line { background_picture: Option, - foreground_picture: Picture, + foreground_picture: Option, has_transparency: bool, } @@ -286,11 +286,9 @@ impl RenderedWindow { let mut foreground_paint = Paint::default(); foreground_paint.set_blend_mode(BlendMode::SrcOver); for (matrix, line) in &lines { - canvas.draw_picture( - &line.foreground_picture, - Some(matrix), - Some(&foreground_paint), - ); + if let Some(foreground_picture) = &line.foreground_picture { + canvas.draw_picture(foreground_picture, Some(matrix), Some(&foreground_paint)); + } } has_transparency } @@ -472,6 +470,7 @@ impl RenderedWindow { let canvas = recorder.begin_recording(grid_rect, None); canvas.clear(Color::from_argb(0, 255, 255, 255)); + let mut foreground_drawn = false; for line_fragment in line_fragments.into_iter() { let LineFragment { text, @@ -481,9 +480,11 @@ impl RenderedWindow { } = line_fragment; let grid_position = (window_left, 0); - grid_renderer.draw_foreground(canvas, text, grid_position, width, &style); + foreground_drawn |= + grid_renderer.draw_foreground(canvas, text, grid_position, width, &style); } - let foreground_picture = recorder.finish_recording_as_picture(None).unwrap(); + let foreground_picture = + foreground_drawn.then_some(recorder.finish_recording_as_picture(None).unwrap()); self.lines[line_index] = Some(Line { background_picture, From b3a845ab957e47a9edd6d9e8909440d612b7907c Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 12 Mar 2023 01:06:01 +0200 Subject: [PATCH 16/53] Use pixel scroll offsets --- src/renderer/rendered_window.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/renderer/rendered_window.rs b/src/renderer/rendered_window.rs index be538eedb..d15b80039 100644 --- a/src/renderer/rendered_window.rs +++ b/src/renderer/rendered_window.rs @@ -253,22 +253,23 @@ impl RenderedWindow { let scroll_offset_lines = self.current_scroll.floor(); let scroll_offset = scroll_offset_lines - self.current_scroll; + let scroll_offset_pixels = (scroll_offset * font_dimensions.height as f32).round() as isize; let mut has_transparency = false; let mut background_paint = Paint::default(); background_paint.set_blend_mode(BlendMode::Src); background_paint.set_alpha(default_background.a()); - let lines: Vec<(Matrix, &Line)> = (0..self.grid_size.height + 1) + let lines: Vec<(Matrix, &Line)> = (0..self.grid_size.height as isize + 1) .filter_map(|i| { - let line_index = (self.top_index + scroll_offset_lines as isize + i as isize) + let line_index = (self.top_index + scroll_offset_lines as isize + i) .rem_euclid(self.lines.len() as isize) as usize; if let Some(line) = &self.lines[line_index] { let mut m = Matrix::new_identity(); m.set_translate(( 0.0, - (scroll_offset + i as f32) * font_dimensions.height as f32, + (scroll_offset_pixels + (i * font_dimensions.height as isize)) as f32, )); Some((m, line)) } else { From 8ad105d509bbb9c6f61700f8d4b0a567e8b026c7 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 21 Jan 2023 01:55:49 +0200 Subject: [PATCH 17/53] Update Skia, enable Skia D3D on Windows --- Cargo.lock | 149 +++++++++++++---------- Cargo.toml | 208 +++++++++++++++++--------------- src/renderer/rendered_window.rs | 8 +- 3 files changed, 199 insertions(+), 166 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a1cd790a7..0d9054f3d 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -113,15 +113,13 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "bindgen" -version = "0.60.1" +version = "0.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6" +checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" dependencies = [ "bitflags", "cexpr", "clang-sys", - "clap 3.2.23", - "env_logger", "lazy_static", "lazycell", "log", @@ -131,6 +129,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", + "syn", "which", ] @@ -243,21 +242,6 @@ dependencies = [ "libloading 0.7.4", ] -[[package]] -name = "clap" -version = "3.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" -dependencies = [ - "atty", - "bitflags", - "clap_lex 0.2.4", - "indexmap", - "strsim 0.10.0", - "termcolor", - "textwrap", -] - [[package]] name = "clap" version = "4.0.19" @@ -267,7 +251,7 @@ dependencies = [ "atty", "bitflags", "clap_derive", - "clap_lex 0.3.0", + "clap_lex", "once_cell", "strsim 0.10.0", "termcolor", @@ -286,15 +270,6 @@ dependencies = [ "syn", ] -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - [[package]] name = "clap_lex" version = "0.3.0" @@ -652,19 +627,6 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" -[[package]] -name = "env_logger" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae4f45fe23a1cad99d61617b3c9dbc19c905f2671b25d1e2714b4b221dc3605" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - [[package]] name = "euclid" version = "0.22.7" @@ -996,12 +958,6 @@ dependencies = [ "libc", ] -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "ident_case" version = "1.0.1" @@ -1374,7 +1330,7 @@ dependencies = [ "async-trait", "backtrace", "cfg-if 1.0.0", - "clap 4.0.19", + "clap", "cocoa", "copypasta", "csscolorparser", @@ -1401,6 +1357,7 @@ dependencies = [ "serde", "serde_json", "shlex", + "simple_moving_average", "skia-safe", "swash", "time", @@ -1852,7 +1809,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ - "toml", + "toml 0.5.9", ] [[package]] @@ -1863,7 +1820,7 @@ checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" dependencies = [ "once_cell", "thiserror", - "toml", + "toml 0.5.9", ] [[package]] @@ -2188,6 +2145,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +dependencies = [ + "serde", +] + [[package]] name = "shared_library" version = "0.1.9" @@ -2213,6 +2179,15 @@ dependencies = [ "libc", ] +[[package]] +name = "simple_moving_average" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd19d3808aad2604c824399fd270260d634678b010328c9d96851bb0fb63121" +dependencies = [ + "num-traits", +] + [[package]] name = "siphasher" version = "0.3.10" @@ -2221,9 +2196,9 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "skia-bindings" -version = "0.52.0" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f249be649ae13f65934370168f742a7be27096f432f15572fb70f5367916b8f" +checksum = "52d166792c15b8ebd180b83e3b9ab38ef69c09468ed26c11692fb59c5af9bc1d" dependencies = [ "bindgen", "cc", @@ -2233,19 +2208,21 @@ dependencies = [ "regex", "serde_json", "tar", - "toml", + "toml 0.7.3", "ureq", ] [[package]] name = "skia-safe" -version = "0.52.0" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5036fc8c167b0e1886aba4f924781d8589e67e5f43bfc3af09d5c9eda4d21940" +checksum = "5fee050b67b2124a5745484f4216999951feeeb1577ef1515e9194ea6d9a9612" dependencies = [ "bitflags", "lazy_static", "skia-bindings", + "winapi", + "wio", ] [[package]] @@ -2385,12 +2362,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - [[package]] name = "thiserror" version = "1.0.37" @@ -2509,6 +2480,40 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc18466501acd8ac6a3f615dd29a3438f8ca6bb3b19537138b3106e575621274" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tracy-client-sys" version = "0.19.0" @@ -2988,13 +2993,31 @@ dependencies = [ "xkbcommon-dl", ] +[[package]] +name = "winnow" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d020b441f92996c80d94ae9166e8501e59c7bb56121189dc9eab3bd8216966" +dependencies = [ + "memchr", +] + [[package]] name = "winres" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c" dependencies = [ - "toml", + "toml 0.5.9", +] + +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 2b92d7fa2..d8f093272 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,100 +1,108 @@ -[package] -name = "neovide" -version = "0.10.3" -edition = "2021" -build = "build.rs" -description = "Neovide: No Nonsense Neovim Gui" -repository = "https://github.com/neovide/neovide" -resolver = "2" - -[workspace] -members = ["neovide-derive"] - -[features] -default = [] -embed-fonts = [] -profiling = ["dep:tracy-client-sys"] -gpu_profiling = ["profiling"] - -[dependencies] -copypasta = "0.8.1" -async-trait = "0.1.53" -backtrace = "0.3.67" -cfg-if = "1.0.0" -clap = { version = "4.0.8", features = ["cargo", "derive", "env"] } -csscolorparser = "0.6.2" -derive-new = "0.5.9" -dirs = "4.0.0" -euclid = "0.22.7" -flexi_logger = { version = "0.22.3", default-features = false } -futures = "0.3.21" -gl = "0.14.0" -glutin = { git = "https://github.com/neovide/glutin", branch = "new-keyboard-all", features = ["serde"] } -image = { version = "0.24.1", default-features = false, features = ["ico"] } -itertools = "0.10.5" -lazy_static = "1.4.0" -log = "0.4.16" -lru = "0.7.5" -neovide-derive = { path = "neovide-derive" } -nvim-rs = { version = "0.5.0", features = ["use_tokio"] } -parking_lot = "0.12.0" -pin-project = "1.0.10" -rand = "0.8.5" -rmpv = "1.0.0" -serde = { version = "1.0.136", features = ["derive"] } -serde_json = "1.0.79" -swash = "0.1.4" -time = "0.3.9" -tokio = { version = "1.25.0", features = ["full"] } -tokio-util = { version = "0.7.4", features = ["compat"] } -unicode-segmentation = "1.9.0" -which = "4.2.5" -winit = { git = "https://github.com/neovide/winit", branch = "new-keyboard-all" } -xdg = "2.4.1" -tracy-client-sys = { version = "0.19.0", optional = true } - -[dev-dependencies] -mockall = "0.11.0" - -[target.'cfg(windows)'.dependencies] -winapi = { version = "0.3.9", features = ["winuser"] } - -[target.'cfg(windows)'.build-dependencies] -winres = "0.1.12" - -[target.'cfg(linux)'.dependencies.skia-safe] -features = ["gl", "egl"] -version = "0.52.0" - -[target.'cfg(not(linux))'.dependencies.skia-safe] -features = ["gl"] -version = "0.52.0" - -[target.'cfg(target_os = "macos")'.dependencies] -cocoa = "0.24.0" -objc = "0.2.7" -shlex = "1.1.0" - -[profile.release] -lto = true -incremental = true -strip = true - -[profile.profiling] -inherits = "release" -strip = false -debug = true - -[package.metadata.bundle] -name = "Neovide" -identifier = "com.neovide.neovide" -icon = ["assets/neovide.ico"] -version = "0.10.3" -resources = [] -copyright = "Copyright (c) Neovide Contributors 2023. All rights reserved." -category = "Productivity" -short_description = "A simple GUI for Neovim." -long_description = """ -This is a simple graphical user interface for Neovim. Where possible there are some graphical improvements, but it should act functionally like the terminal UI. -""" -osx_minimum_system_version = "10.11" +[package] +name = "neovide" +version = "0.10.3" +edition = "2021" +build = "build.rs" +description = "Neovide: No Nonsense Neovim Gui" +repository = "https://github.com/neovide/neovide" +resolver = "2" + +[workspace] +members = ["neovide-derive"] + +[features] +default = [] +embed-fonts = [] +profiling = ["dep:tracy-client-sys"] +gpu_profiling = ["profiling"] + +[dependencies] +async-trait = "0.1.53" +backtrace = "0.3.67" +cfg-if = "1.0.0" +clap = { version = "4.0.8", features = ["cargo", "derive", "env"] } +copypasta = "0.8.1" +csscolorparser = "0.6.2" +derive-new = "0.5.9" +dirs = "4.0.0" +euclid = "0.22.7" +flexi_logger = { version = "0.22.3", default-features = false } +futures = "0.3.21" +gl = "0.14.0" +glutin = { git = "https://github.com/neovide/glutin", branch = "new-keyboard-all", features = ["serde"] } +image = { version = "0.24.1", default-features = false, features = ["ico"] } +itertools = "0.10.5" +lazy_static = "1.4.0" +log = "0.4.16" +lru = "0.7.5" +neovide-derive = { path = "neovide-derive" } +nvim-rs = { version = "0.5.0", features = ["use_tokio"] } +parking_lot = "0.12.0" +pin-project = "1.0.10" +rand = "0.8.5" +rmpv = "1.0.0" +serde = { version = "1.0.136", features = ["derive"] } +serde_json = "1.0.79" +simple_moving_average = "0.1.2" +swash = "0.1.4" +time = "0.3.9" +tokio = { version = "1.25.0", features = ["full"] } +tokio-util = { version = "0.7.4", features = ["compat"] } +tracy-client-sys = { version = "0.19.0", optional = true } +unicode-segmentation = "1.9.0" +which = "4.2.5" +winit = { git = "https://github.com/neovide/winit", branch = "new-keyboard-all" } +xdg = "2.4.1" + +[dev-dependencies] +mockall = "0.11.0" + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3.9", features = ["winuser"] } + +[target.'cfg(target_os = "macos")'.dependencies] +cocoa = "0.24.0" +objc = "0.2.7" +shlex = "1.1.0" + +[target.'cfg(not(windows))'.dependencies] + +[target.'cfg(windows)'.build-dependencies] +winres = "0.1.12" + +[target.'cfg(windows)'.dependencies.skia-safe] +# NOTE: Including textlayout for pre-built binaries +features = ["d3d", "gl", "textlayout"] +version = "0.60.0" + +[target.'cfg(linux)'.dependencies.skia-safe] +features = ["gl", "egl"] +version = "0.60.0" + +[target.'cfg(not(linux))'.dependencies.skia-safe] +features = ["gl"] +version = "0.60.0" + +[profile.release] +lto = true +incremental = true +strip = true + +[profile.profiling] +inherits = "release" +strip = false +debug = true + +[package.metadata.bundle] +name = "Neovide" +identifier = "com.neovide.neovide" +icon = ["assets/neovide.ico"] +version = "0.10.3" +resources = [] +copyright = "Copyright (c) Neovide Contributors 2023. All rights reserved." +category = "Productivity" +short_description = "A simple GUI for Neovim." +long_description = """ +This is a simple graphical user interface for Neovim. Where possible there are some graphical improvements, but it should act functionally like the terminal UI. +""" +osx_minimum_system_version = "10.11" diff --git a/src/renderer/rendered_window.rs b/src/renderer/rendered_window.rs index d15b80039..4e1168360 100644 --- a/src/renderer/rendered_window.rs +++ b/src/renderer/rendered_window.rs @@ -1,9 +1,11 @@ use std::sync::Arc; use skia_safe::{ - canvas::SaveLayerRec, gpu::SurfaceOrigin, image_filters::blur, BlendMode, Budgeted, Canvas, - Color, ImageInfo, Matrix, Paint, Picture, PictureRecorder, Point, Rect, Surface, SurfaceProps, - SurfacePropsFlags, + canvas::SaveLayerRec, + gpu::{Budgeted, SurfaceOrigin}, + image_filters::blur, + BlendMode, Canvas, Color, ImageInfo, Matrix, Paint, Picture, PictureRecorder, Point, Rect, + Surface, SurfaceProps, SurfacePropsFlags, }; use crate::{ From 808090cd55646948d79c51882f359d10de9b067c Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 1 Oct 2022 13:49:34 +0300 Subject: [PATCH 18/53] Improve the event loop All events are now processed before allowing rendering. All attempts to limit the frame rate is also removed temporarily. --- src/window/mod.rs | 71 +++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/src/window/mod.rs b/src/window/mod.rs index 412a8c429..f85504318 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -6,7 +6,7 @@ mod settings; #[cfg(target_os = "macos")] mod draw_background; -use std::time::{Duration, Instant}; +use std::time::Instant; use log::trace; use tokio::sync::mpsc::UnboundedReceiver; @@ -459,17 +459,38 @@ pub fn create_window() { let mut focused = FocusedState::Focused; event_loop.run(move |e, _window_target, control_flow| { - // Window focus changed - if let Event::WindowEvent { - event: WindowEvent::Focused(focused_event), - .. - } = e - { - focused = if focused_event { - FocusedState::Focused - } else { - FocusedState::UnfocusedNotDrawn - }; + match e { + // Window focus changed + Event::WindowEvent { + event: WindowEvent::Focused(focused_event), + .. + } => { + focused = if focused_event { + FocusedState::Focused + } else { + FocusedState::UnfocusedNotDrawn + }; + } + Event::MainEventsCleared => { + let frame_start = Instant::now(); + // Only render when there are no pending events + //let expected_frame_length_seconds = 1.0 / refresh_rate; + //let frame_duration = Duration::from_secs_f32(expected_frame_length_seconds); + + let dt = previous_frame_start.elapsed().as_secs_f32(); + window_wrapper.draw_frame(dt); + if let FocusedState::UnfocusedNotDrawn = focused { + focused = FocusedState::Unfocused; + } + previous_frame_start = frame_start; + let window = window_wrapper.windowed_context.window(); + + #[cfg(target_os = "macos")] + draw_background(window); + + window.request_redraw(); + } + _ => (), } if !RUNNING_TRACKER.is_running() { @@ -483,34 +504,10 @@ pub fn create_window() { std::process::exit(RUNNING_TRACKER.exit_code()); } - let frame_start = Instant::now(); - window_wrapper.handle_window_commands(); window_wrapper.synchronize_settings(); window_wrapper.handle_event(e); - let refresh_rate = match focused { - FocusedState::Focused | FocusedState::UnfocusedNotDrawn => { - SETTINGS.get::().refresh_rate as f32 - } - FocusedState::Unfocused => SETTINGS.get::().refresh_rate_idle as f32, - } - .max(1.0); - - let expected_frame_length_seconds = 1.0 / refresh_rate; - let frame_duration = Duration::from_secs_f32(expected_frame_length_seconds); - - if frame_start - previous_frame_start > frame_duration { - let dt = previous_frame_start.elapsed().as_secs_f32(); - window_wrapper.draw_frame(dt); - if let FocusedState::UnfocusedNotDrawn = focused { - focused = FocusedState::Unfocused; - } - previous_frame_start = frame_start; - #[cfg(target_os = "macos")] - draw_background(window_wrapper.windowed_context.window()); - } - - *control_flow = ControlFlow::WaitUntil(previous_frame_start + frame_duration) + *control_flow = ControlFlow::Poll; }); } From e39611cd13f9474eed249b45584c5a2c6bdf6451 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Wed, 5 Oct 2022 20:53:38 +0300 Subject: [PATCH 19/53] Temp logging changes --- src/window/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/window/mod.rs b/src/window/mod.rs index f85504318..f6089a445 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -249,11 +249,13 @@ impl WinitWindowWrapper { // which already resized window. let resized_at_startup = self.maximized_at_startup || self.has_been_resized(); + /* log::trace!( "Settings geometry {:?}", PhysicalSize::from(settings.geometry) ); log::trace!("Inner size: {:?}", new_size); + */ if self.saved_grid_size.is_none() && !resized_at_startup { let window = self.windowed_context.window(); From ccd72d33baac28190bce5139ec3c9a97de531a01 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 2 Oct 2022 15:25:12 +0300 Subject: [PATCH 20/53] Split rendering and animation --- src/redraw_scheduler.rs | 13 --- src/renderer/cursor_renderer/blink.rs | 94 +++++++++--------- src/renderer/cursor_renderer/mod.rs | 134 +++++++++++++------------- src/renderer/mod.rs | 90 +++++++++++------ src/renderer/rendered_window.rs | 8 +- src/window/mod.rs | 42 +++++--- 6 files changed, 210 insertions(+), 171 deletions(-) diff --git a/src/redraw_scheduler.rs b/src/redraw_scheduler.rs index 11393bf43..80ca452fc 100644 --- a/src/redraw_scheduler.rs +++ b/src/redraw_scheduler.rs @@ -25,19 +25,6 @@ impl RedrawScheduler { } } - pub fn schedule(&self, new_scheduled: Instant) { - trace!("Redraw scheduled for {:?}", new_scheduled); - let mut scheduled_frame = self.scheduled_frame.lock().unwrap(); - - if let Some(previous_scheduled) = *scheduled_frame { - if new_scheduled < previous_scheduled { - *scheduled_frame = Some(new_scheduled); - } - } else { - *scheduled_frame = Some(new_scheduled); - } - } - pub fn queue_next_frame(&self) { trace!("Next frame queued"); self.frame_queued.store(true, Ordering::Relaxed); diff --git a/src/renderer/cursor_renderer/blink.rs b/src/renderer/cursor_renderer/blink.rs index 9529d3569..0c4212608 100644 --- a/src/renderer/cursor_renderer/blink.rs +++ b/src/renderer/cursor_renderer/blink.rs @@ -1,6 +1,4 @@ -use std::time::{Duration, Instant}; - -use crate::{editor::Cursor, redraw_scheduler::REDRAW_SCHEDULER}; +use crate::editor::Cursor; #[derive(Debug)] pub enum BlinkState { @@ -11,68 +9,74 @@ pub enum BlinkState { pub struct BlinkStatus { state: BlinkState, - last_transition: Instant, - previous_cursor: Option, + transition_left: f32, + current_cursor: Option, +} + +fn is_static(cursor: &Cursor) -> bool { + // The documentations says that if any state is zero there's no blinking + cursor.blinkwait == Some(0) + || cursor.blinkwait.is_none() + || cursor.blinkoff == Some(0) + || cursor.blinkoff.is_none() + || cursor.blinkon == Some(0) + || cursor.blinkon.is_none() } impl BlinkStatus { pub fn new() -> BlinkStatus { BlinkStatus { state: BlinkState::Waiting, - last_transition: Instant::now(), - previous_cursor: None, + transition_left: 0.0, + current_cursor: None, } } - 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(); + fn get_delay(&self) -> f32 { + let delay_ms = if let Some(c) = &self.current_cursor { + match self.state { + BlinkState::Waiting => c.blinkwait.unwrap_or(0), + BlinkState::Off => c.blinkoff.unwrap_or(0), + BlinkState::On => c.blinkon.unwrap_or(0), + } + } else { + 0 + }; + (delay_ms as f32) / 1000.0 + } + + pub fn update_status(&mut self, new_cursor: &Cursor, dt: f32) { + if self.current_cursor.is_none() || new_cursor != self.current_cursor.as_ref().unwrap() { + self.current_cursor = Some(new_cursor.clone()); if new_cursor.blinkwait.is_some() && new_cursor.blinkwait != Some(0) { self.state = BlinkState::Waiting; } else { self.state = BlinkState::On; } + // Note we decrement by dt below, so add dt here to ensure that we wait long enough + self.transition_left = self.get_delay() + dt; } - if new_cursor.blinkwait == Some(0) - || new_cursor.blinkoff == Some(0) - || new_cursor.blinkon == Some(0) - { - return true; - } + let current_cursor = self.current_cursor.as_ref().unwrap(); - let delay = match self.state { - BlinkState::Waiting => new_cursor.blinkwait, - BlinkState::Off => new_cursor.blinkoff, - BlinkState::On => new_cursor.blinkon, - } - .filter(|millis| *millis > 0) - .map(Duration::from_millis); - - if delay - .map(|delay| self.last_transition + delay < Instant::now()) - .unwrap_or(false) - { - self.state = match self.state { - BlinkState::Waiting => BlinkState::On, - BlinkState::On => BlinkState::Off, - BlinkState::Off => BlinkState::On, - }; - self.last_transition = Instant::now(); - } + if is_static(current_cursor) { + self.state = BlinkState::On; + self.transition_left = 0.0; + } else { + self.transition_left -= dt; - 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); + if self.transition_left <= 0.0 { + self.state = match self.state { + BlinkState::Waiting => BlinkState::On, + BlinkState::On => BlinkState::Off, + BlinkState::Off => BlinkState::On, + }; + self.transition_left = self.get_delay(); + } } + } + pub fn should_render(&self) -> bool { match self.state { BlinkState::Off => false, BlinkState::On | BlinkState::Waiting => true, diff --git a/src/renderer/cursor_renderer/mod.rs b/src/renderer/cursor_renderer/mod.rs index 70436bd16..4146b571e 100644 --- a/src/renderer/cursor_renderer/mod.rs +++ b/src/renderer/cursor_renderer/mod.rs @@ -10,7 +10,6 @@ use crate::{ bridge::EditorMode, editor::{Cursor, CursorShape}, profiling::tracy_zone, - redraw_scheduler::REDRAW_SCHEDULER, renderer::animation_utils::*, renderer::{GridRenderer, RenderedWindow}, settings::{ParseFromValue, SETTINGS}, @@ -272,15 +271,76 @@ impl CursorRenderer { } } - pub fn draw( + pub fn draw(&mut self, grid_renderer: &mut GridRenderer, canvas: &mut Canvas) { + tracy_zone!("cursor_draw"); + let render = self.blink_status.should_render(); + let settings = SETTINGS.get::(); + + let mut paint = Paint::new(skia_safe::colors::WHITE, None); + paint.set_anti_alias(settings.antialiasing); + + let character = self.cursor.grid_cell.0.clone(); + + if !(self.cursor.enabled && render) { + return; + } + // Draw Background + let background_color = self + .cursor + .background(&grid_renderer.default_style.colors) + .to_color() + .with_a(self.cursor.alpha()); + paint.set_color(background_color); + + let path = if self.window_has_focus || self.cursor.shape != CursorShape::Block { + self.draw_rectangle(canvas, &paint) + } else { + let outline_width = settings.unfocused_outline_width * grid_renderer.em_size; + self.draw_rectangular_outline(canvas, &paint, outline_width) + }; + + // Draw foreground + let foreground_color = self + .cursor + .foreground(&grid_renderer.default_style.colors) + .to_color() + .with_a(self.cursor.alpha()); + paint.set_color(foreground_color); + + canvas.save(); + canvas.clip_path(&path, None, Some(false)); + + let y_adjustment = grid_renderer.shaper.y_adjustment(); + let style = &self.cursor.grid_cell.1; + + let bold = style.as_ref().map(|x| x.bold).unwrap_or(false); + let italic = style.as_ref().map(|x| x.italic).unwrap_or(false); + + let blobs = &grid_renderer.shaper.shape_cached(character, bold, italic); + + for blob in blobs.iter() { + canvas.draw_text_blob( + blob, + (self.destination.x, self.destination.y + y_adjustment as f32), + &paint, + ); + } + + canvas.restore(); + + if let Some(vfx) = self.cursor_vfx.as_ref() { + vfx.render(&settings, canvas, grid_renderer, &self.cursor); + } + } + + pub fn animate( &mut self, - grid_renderer: &mut GridRenderer, current_mode: &EditorMode, - canvas: &mut Canvas, + grid_renderer: &GridRenderer, dt: f32, - ) { - tracy_zone!("cursor_draw"); - let render = self.blink_status.update_status(&self.cursor); + ) -> bool { + tracy_zone!("cursor_animate"); + self.blink_status.update_status(&self.cursor, dt); let settings = SETTINGS.get::(); if settings.vfx_mode != self.previous_vfx_mode { @@ -288,11 +348,6 @@ impl CursorRenderer { self.previous_vfx_mode = settings.vfx_mode.clone(); } - let mut paint = Paint::new(skia_safe::colors::WHITE, None); - paint.set_anti_alias(settings.antialiasing); - - let character = self.cursor.grid_cell.0.clone(); - let mut cursor_width = grid_renderer.font_dimensions.width; if self.cursor.double_width && self.cursor.shape == CursorShape::Block { cursor_width *= 2; @@ -353,61 +408,10 @@ impl CursorRenderer { animating |= vfx_animating; } - if animating { - REDRAW_SCHEDULER.queue_next_frame(); - } else { + if !animating { self.previous_editor_mode = current_mode.clone(); } - if !(self.cursor.enabled && render) { - return; - } - // Draw Background - let background_color = self - .cursor - .background(&grid_renderer.default_style.colors) - .to_color() - .with_a(self.cursor.alpha()); - paint.set_color(background_color); - - let path = if self.window_has_focus || self.cursor.shape != CursorShape::Block { - self.draw_rectangle(canvas, &paint) - } else { - let outline_width = settings.unfocused_outline_width * grid_renderer.em_size; - self.draw_rectangular_outline(canvas, &paint, outline_width) - }; - - // Draw foreground - let foreground_color = self - .cursor - .foreground(&grid_renderer.default_style.colors) - .to_color() - .with_a(self.cursor.alpha()); - paint.set_color(foreground_color); - - canvas.save(); - canvas.clip_path(&path, None, Some(false)); - - let y_adjustment = grid_renderer.shaper.y_adjustment(); - let style = &self.cursor.grid_cell.1; - - let bold = style.as_ref().map(|x| x.bold).unwrap_or(false); - let italic = style.as_ref().map(|x| x.italic).unwrap_or(false); - - let blobs = &grid_renderer.shaper.shape_cached(character, bold, italic); - - for blob in blobs.iter() { - canvas.draw_text_blob( - blob, - (self.destination.x, self.destination.y + y_adjustment as f32), - &paint, - ); - } - - canvas.restore(); - - if let Some(vfx) = self.cursor_vfx.as_ref() { - vfx.render(&settings, canvas, grid_renderer, &self.cursor); - } + animating } fn draw_rectangle(&self, canvas: &mut Canvas, paint: &Paint) -> Path { diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index ab219792b..9328bf55f 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -143,22 +143,8 @@ impl Renderer { /// # Returns /// `bool` indicating whether or not font was changed during this frame. #[allow(clippy::needless_collect)] - pub fn draw_frame(&mut self, root_canvas: &mut Canvas, dt: f32) -> bool { + pub fn draw_frame(&mut self, root_canvas: &mut Canvas, dt: f32) { tracy_zone!("renderer_draw_frame"); - let mut draw_commands = Vec::new(); - while let Ok(draw_command) = self.batched_draw_command_receiver.try_recv() { - draw_commands.extend(draw_command); - } - - let mut font_changed = false; - - for draw_command in draw_commands.into_iter() { - if let DrawCommand::FontChanged(_) | DrawCommand::LineSpaceChanged(_) = draw_command { - font_changed = true; - } - self.handle_draw_command(root_canvas, draw_command); - } - let default_background = self.grid_renderer.get_default_background(); let font_dimensions = self.grid_renderer.font_dimensions; @@ -167,14 +153,6 @@ impl Renderer { root_canvas.save(); root_canvas.reset_matrix(); - let user_scale_factor = SETTINGS.get::().scale_factor.into(); - if user_scale_factor != self.user_scale_factor { - self.user_scale_factor = user_scale_factor; - self.grid_renderer - .handle_scale_factor_update(self.os_scale_factor * self.user_scale_factor); - font_changed = true; - } - if let Some(root_window) = self.rendered_windows.get(&1) { let clip_rect = root_window.pixel_region(font_dimensions); root_canvas.clip_rect(clip_rect, None, Some(false)); @@ -214,21 +192,79 @@ impl Renderer { &settings, default_background.with_a((255.0 * transparency) as u8), font_dimensions, - dt, ) }) .collect(); + self.cursor_renderer + .draw(&mut self.grid_renderer, root_canvas); + + self.profiler.draw(root_canvas, dt); + + root_canvas.restore(); + } + + pub fn animate_frame(&mut self, dt: f32) -> bool { + let windows: Vec<&mut RenderedWindow> = { + let (mut root_windows, mut floating_windows): ( + Vec<&mut RenderedWindow>, + Vec<&mut RenderedWindow>, + ) = self + .rendered_windows + .values_mut() + .filter(|window| !window.hidden) + .partition(|window| window.floating_order.is_none()); + + root_windows + .sort_by(|window_a, window_b| window_a.id.partial_cmp(&window_b.id).unwrap()); + + floating_windows.sort_by(floating_sort); + + root_windows + .into_iter() + .chain(floating_windows.into_iter()) + .collect() + }; + + let settings = SETTINGS.get::(); + let animating = windows + .into_iter() + .map(|window| window.animate(&settings, dt)) + .any(|a| a); + let windows = &self.rendered_windows; + let font_dimensions = self.grid_renderer.font_dimensions; self.cursor_renderer .update_cursor_destination(font_dimensions.into(), windows); self.cursor_renderer - .draw(&mut self.grid_renderer, &self.current_mode, root_canvas, dt); + .animate(&self.current_mode, &self.grid_renderer, dt); - self.profiler.draw(root_canvas, dt); + animating + } - root_canvas.restore(); + pub fn handle_draw_commands(&mut self, root_canvas: &mut Canvas) -> bool { + let mut draw_commands = Vec::new(); + while let Ok(draw_command) = self.batched_draw_command_receiver.try_recv() { + draw_commands.extend(draw_command); + } + + let mut font_changed = false; + + for draw_command in draw_commands.into_iter() { + if let DrawCommand::FontChanged(_) | DrawCommand::LineSpaceChanged(_) = draw_command { + font_changed = true; + } + self.handle_draw_command(root_canvas, draw_command); + } + + let user_scale_factor = SETTINGS.get::().scale_factor.into(); + if user_scale_factor != self.user_scale_factor { + self.user_scale_factor = user_scale_factor; + self.grid_renderer + .handle_scale_factor_update(self.os_scale_factor * self.user_scale_factor); + font_changed = true; + } font_changed } diff --git a/src/renderer/rendered_window.rs b/src/renderer/rendered_window.rs index 4e1168360..947e0d56b 100644 --- a/src/renderer/rendered_window.rs +++ b/src/renderer/rendered_window.rs @@ -12,7 +12,6 @@ use crate::{ dimensions::Dimensions, editor::Style, profiling::tracy_zone, - redraw_scheduler::REDRAW_SCHEDULER, renderer::{animation_utils::*, GridRenderer, RendererSettings}, }; @@ -200,7 +199,7 @@ impl RenderedWindow { Rect::from_point_and_size(current_pixel_position, image_size) } - pub fn update(&mut self, settings: &RendererSettings, dt: f32) -> bool { + pub fn animate(&mut self, settings: &RendererSettings, dt: f32) -> bool { let mut animating = false; { @@ -302,12 +301,7 @@ impl RenderedWindow { settings: &RendererSettings, default_background: Color, font_dimensions: Dimensions, - dt: f32, ) -> WindowDrawDetails { - if self.update(settings, dt) { - REDRAW_SCHEDULER.queue_next_frame(); - } - let has_transparency = self.draw_surface(font_dimensions, default_background); let pixel_region = self.pixel_region(font_dimensions); diff --git a/src/window/mod.rs b/src/window/mod.rs index f6089a445..87dc4daa8 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -200,6 +200,29 @@ impl WinitWindowWrapper { pub fn draw_frame(&mut self, dt: f32) { tracy_zone!("draw_frame"); + if REDRAW_SCHEDULER.should_draw() || SETTINGS.get::().no_idle { + self.renderer.draw_frame(self.skia_renderer.canvas(), dt); + { + tracy_gpu_zone!("skia flush"); + self.skia_renderer.gr_context.flush(None); + } + { + tracy_gpu_zone!("swap buffers"); + self.windowed_context.swap_buffers().unwrap(); + } + emit_frame_mark(); + tracy_gpu_collect(); + } + } + + pub fn animate_frame(&mut self, dt: f32) -> bool { + tracy_zone!("animate_frame", 0); + self.renderer.animate_frame(dt) + } + + pub fn prepare_frame(&mut self) { + tracy_zone!("prepare_frame", 0); + let window = self.windowed_context.window(); let new_size = window.inner_size(); @@ -224,20 +247,9 @@ impl WinitWindowWrapper { self.skia_renderer.resize(&self.windowed_context); } - if REDRAW_SCHEDULER.should_draw() || SETTINGS.get::().no_idle { - self.font_changed_last_frame = - self.renderer.draw_frame(self.skia_renderer.canvas(), dt); - { - tracy_gpu_zone!("skia flush"); - self.skia_renderer.gr_context.flush(None); - } - { - tracy_gpu_zone!("swap buffers"); - self.windowed_context.swap_buffers().unwrap(); - } - emit_frame_mark(); - tracy_gpu_collect(); - } + self.font_changed_last_frame = self + .renderer + .handle_draw_commands(self.skia_renderer.canvas()); // Wait until fonts are loaded, so we can set proper window size. if !self.renderer.grid_renderer.is_ready { @@ -480,6 +492,8 @@ pub fn create_window() { //let frame_duration = Duration::from_secs_f32(expected_frame_length_seconds); let dt = previous_frame_start.elapsed().as_secs_f32(); + window_wrapper.prepare_frame(); + window_wrapper.animate_frame(dt); window_wrapper.draw_frame(dt); if let FocusedState::UnfocusedNotDrawn = focused { focused = FocusedState::Unfocused; From 6458040125f108e0ac35279636a73d69048f12c3 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Fri, 4 Nov 2022 12:56:07 +0200 Subject: [PATCH 21/53] WIP render thread --- src/renderer/opengl.rs | 9 +- src/window/mod.rs | 226 ++++++++++++++++++++++------------------- 2 files changed, 126 insertions(+), 109 deletions(-) diff --git a/src/renderer/opengl.rs b/src/renderer/opengl.rs index 5542f35a4..97c482960 100644 --- a/src/renderer/opengl.rs +++ b/src/renderer/opengl.rs @@ -1,6 +1,6 @@ use crate::cmd_line::CmdLineSettings; -use glutin::{ContextBuilder, GlProfile, PossiblyCurrent, WindowedContext}; +use glutin::{ContextBuilder, GlProfile, NotCurrent, WindowedContext}; use winit::{event_loop::EventLoop, window::WindowBuilder}; @@ -10,7 +10,7 @@ pub fn build_context( cmd_line_settings: &CmdLineSettings, winit_window_builder: WindowBuilder, event_loop: &EventLoop, -) -> WindowedContext { +) -> WindowedContext { let builder = ContextBuilder::new() .with_pixel_format(24, 8) .with_stencil_buffer(8) @@ -18,7 +18,7 @@ pub fn build_context( .with_srgb(cmd_line_settings.srgb) .with_vsync(cmd_line_settings.vsync); - let ctx = match builder + match builder .clone() .build_windowed(winit_window_builder.clone(), event_loop) { @@ -35,6 +35,5 @@ pub fn build_context( panic!("{}", err); } } - }; - unsafe { ctx.make_current().unwrap() } + } } diff --git a/src/window/mod.rs b/src/window/mod.rs index 87dc4daa8..5fe41c4d4 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -6,6 +6,8 @@ mod settings; #[cfg(target_os = "macos")] mod draw_background; +use std::sync::mpsc; +use std::thread; use std::time::Instant; use log::trace; @@ -400,130 +402,146 @@ pub fn create_window() { let windowed_context = build_context(&cmd_line_settings, winit_window_builder, &event_loop); - let window = windowed_context.window(); - let initial_size = window.inner_size(); - - // Check that window is visible in some monitor, and reposition it if not. - let did_reposition = window - .current_monitor() - .and_then(|current_monitor| { - let monitor_position = current_monitor.position(); - let monitor_size = current_monitor.size(); - let monitor_width = monitor_size.width as i32; - let monitor_height = monitor_size.height as i32; - - let window_position = window.outer_position().ok()?; - let window_size = window.outer_size(); - let window_width = window_size.width as i32; - let window_height = window_size.height as i32; - - if window_position.x + window_width < monitor_position.x - || window_position.y + window_height < monitor_position.y - || window_position.x > monitor_position.x + monitor_width - || window_position.y > monitor_position.y + monitor_height - { - window.set_outer_position(monitor_position); - } - - Some(()) - }) - .is_some(); - - log::trace!("repositioned window: {}", did_reposition); - - let scale_factor = windowed_context.window().scale_factor(); - let renderer = Renderer::new(scale_factor); - let saved_inner_size = window.inner_size(); - - let skia_renderer = SkiaRenderer::new(&windowed_context); - - let window_command_receiver = EVENT_AGGREGATOR.register_event::(); - - log::info!( - "window created (scale_factor: {:.4}, font_dimensions: {:?})", - scale_factor, - renderer.grid_renderer.font_dimensions, - ); - - let mut window_wrapper = WinitWindowWrapper { - windowed_context, - skia_renderer, - renderer, - keyboard_manager: KeyboardManager::new(), - mouse_manager: MouseManager::new(), - title: String::from("Neovide"), - fullscreen: false, - font_changed_last_frame: false, - size_at_startup: initial_size, - maximized_at_startup: maximized, - saved_inner_size, - saved_grid_size: None, - window_command_receiver, - }; - - tracy_create_gpu_context("main_render_context"); - - let mut previous_frame_start = Instant::now(); - enum FocusedState { Focused, UnfocusedNotDrawn, Unfocused, } - let mut focused = FocusedState::Focused; - event_loop.run(move |e, _window_target, control_flow| { - match e { - // Window focus changed - Event::WindowEvent { - event: WindowEvent::Focused(focused_event), - .. - } => { - focused = if focused_event { - FocusedState::Focused - } else { - FocusedState::UnfocusedNotDrawn - }; + let (txtemp, rx) = mpsc::channel::>(); + let mut tx = Some(txtemp); + let mut render_thread_handle = Some(thread::spawn(move || { + let windowed_context = unsafe { windowed_context.make_current().unwrap() }; + let window = windowed_context.window(); + let initial_size = window.inner_size(); + + // Check that window is visible in some monitor, and reposition it if not. + let did_reposition = window + .current_monitor() + .and_then(|current_monitor| { + let monitor_position = current_monitor.position(); + let monitor_size = current_monitor.size(); + let monitor_width = monitor_size.width as i32; + let monitor_height = monitor_size.height as i32; + + let window_position = window.outer_position().ok()?; + let window_size = window.outer_size(); + let window_width = window_size.width as i32; + let window_height = window_size.height as i32; + + if window_position.x + window_width < monitor_position.x + || window_position.y + window_height < monitor_position.y + || window_position.x > monitor_position.x + monitor_width + || window_position.y > monitor_position.y + monitor_height + { + window.set_outer_position(monitor_position); + } + + Some(()) + }) + .is_some(); + + log::trace!("repositioned window: {}", did_reposition); + + let scale_factor = windowed_context.window().scale_factor(); + let renderer = Renderer::new(scale_factor); + let saved_inner_size = window.inner_size(); + + let window_command_receiver = EVENT_AGGREGATOR.register_event::(); + + log::info!( + "window created (scale_factor: {:.4}, font_dimensions: {:?})", + scale_factor, + renderer.grid_renderer.font_dimensions, + ); + + let skia_renderer = SkiaRenderer::new(&windowed_context); + + let mut window_wrapper = WinitWindowWrapper { + windowed_context, + skia_renderer, + renderer, + keyboard_manager: KeyboardManager::new(), + mouse_manager: MouseManager::new(), + title: String::from("Neovide"), + fullscreen: false, + font_changed_last_frame: false, + size_at_startup: initial_size, + maximized_at_startup: maximized, + saved_inner_size, + saved_grid_size: None, + window_command_receiver, + }; + + tracy_create_gpu_context("main render context"); + + let mut focused = FocusedState::Focused; + let mut prev_frame_start = Instant::now(); + + loop { + tracy_zone!("render loop", 0); + let e = rx.try_recv(); + + match e { + // Window focus changed + Ok(Event::WindowEvent { + event: WindowEvent::Focused(focused_event), + .. + }) => { + focused = if focused_event { + FocusedState::Focused + } else { + FocusedState::UnfocusedNotDrawn + }; + } + Err(mpsc::TryRecvError::Disconnected) => { + break; + } + _ => {} } - Event::MainEventsCleared => { + if let Ok(e) = e { + window_wrapper.handle_event(e); + window_wrapper.handle_window_commands(); + window_wrapper.synchronize_settings(); + } else { let frame_start = Instant::now(); - // Only render when there are no pending events - //let expected_frame_length_seconds = 1.0 / refresh_rate; - //let frame_duration = Duration::from_secs_f32(expected_frame_length_seconds); - - let dt = previous_frame_start.elapsed().as_secs_f32(); + let dt = prev_frame_start.elapsed().as_secs_f32(); window_wrapper.prepare_frame(); window_wrapper.animate_frame(dt); window_wrapper.draw_frame(dt); if let FocusedState::UnfocusedNotDrawn = focused { focused = FocusedState::Unfocused; } - previous_frame_start = frame_start; - let window = window_wrapper.windowed_context.window(); - + prev_frame_start = frame_start; #[cfg(target_os = "macos")] - draw_background(window); + draw_background(&window_wrapper.window); + } + } + let window = window_wrapper.windowed_context.window(); + save_window_geometry( + window.is_maximized(), + window_wrapper.saved_grid_size, + window.outer_position().ok(), + ); + std::process::exit(RUNNING_TRACKER.exit_code()); + })); - window.request_redraw(); + event_loop.run(move |e, _window_target, control_flow| { + if let Some(event) = e.to_static() { + if let Some(tx) = &tx { + tx.send(event).unwrap(); } - _ => (), } if !RUNNING_TRACKER.is_running() { - let window = window_wrapper.windowed_context.window(); - save_window_geometry( - window.is_maximized(), - window_wrapper.saved_grid_size, - window.outer_position().ok(), - ); - - std::process::exit(RUNNING_TRACKER.exit_code()); + let tx = tx.take().unwrap(); + drop(tx); + let handle = render_thread_handle.take().unwrap(); + handle.join().unwrap(); } - - window_wrapper.handle_window_commands(); - window_wrapper.synchronize_settings(); - window_wrapper.handle_event(e); - - *control_flow = ControlFlow::Poll; + // We need to wake up regularly to check the running tracker, so that we can exit + *control_flow = ControlFlow::WaitUntil( + std::time::Instant::now() + std::time::Duration::from_millis(100), + ); }); } From 6a348221e78dacf7f3f97a87fd7ff6c9c8986d69 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 19 Mar 2023 02:26:31 +0200 Subject: [PATCH 22/53] Deal with scale factor changed events --- src/renderer/cursor_renderer/mod.rs | 3 ++- src/renderer/mod.rs | 3 ++- src/window/keyboard_manager.rs | 4 ++-- src/window/mod.rs | 34 ++++++++++++++++++++--------- src/window/mouse_manager.rs | 4 ++-- 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/renderer/cursor_renderer/mod.rs b/src/renderer/cursor_renderer/mod.rs index 4146b571e..f849edc21 100644 --- a/src/renderer/cursor_renderer/mod.rs +++ b/src/renderer/cursor_renderer/mod.rs @@ -13,6 +13,7 @@ use crate::{ renderer::animation_utils::*, renderer::{GridRenderer, RenderedWindow}, settings::{ParseFromValue, SETTINGS}, + window::UserEvent, }; use blink::*; @@ -198,7 +199,7 @@ impl CursorRenderer { renderer } - pub fn handle_event(&mut self, event: &Event<()>) { + pub fn handle_event(&mut self, event: &Event) { if let Event::WindowEvent { event: WindowEvent::Focused(is_focused), .. diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 9328bf55f..440670e54 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -23,6 +23,7 @@ use crate::{ event_aggregator::EVENT_AGGREGATOR, profiling::tracy_zone, settings::*, + window::UserEvent, WindowSettings, }; @@ -130,7 +131,7 @@ impl Renderer { } } - pub fn handle_event(&mut self, event: &Event<()>) { + pub fn handle_event(&mut self, event: &Event) { self.cursor_renderer.handle_event(event); } diff --git a/src/window/keyboard_manager.rs b/src/window/keyboard_manager.rs index 37dd1144d..58a30b547 100644 --- a/src/window/keyboard_manager.rs +++ b/src/window/keyboard_manager.rs @@ -2,7 +2,7 @@ use crate::{ bridge::{SerialCommand, UiCommand}, event_aggregator::EVENT_AGGREGATOR, settings::SETTINGS, - window::KeyboardSettings, + window::{KeyboardSettings, UserEvent}, }; use winit::{ event::{ElementState, Event, KeyEvent, WindowEvent}, @@ -37,7 +37,7 @@ impl KeyboardManager { } } - pub fn handle_event(&mut self, event: &Event<()>) { + pub fn handle_event(&mut self, event: &Event) { match event { Event::WindowEvent { event: WindowEvent::Focused(_focused), diff --git a/src/window/mod.rs b/src/window/mod.rs index 5fe41c4d4..ee21549fb 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -67,6 +67,11 @@ pub enum WindowCommand { ListAvailableFonts, } +#[derive(Clone, Debug)] +pub enum UserEvent { + ScaleFactorChanged(f64), +} + pub struct WinitWindowWrapper { windowed_context: WindowedContext, skia_renderer: SkiaRenderer, @@ -147,7 +152,7 @@ impl WinitWindowWrapper { REDRAW_SCHEDULER.queue_next_frame(); } - pub fn handle_event(&mut self, event: Event<()>) { + pub fn handle_event(&mut self, event: Event) { tracy_zone!("handle_event", 0); self.keyboard_manager.handle_event(&event); self.mouse_manager.handle_event( @@ -170,10 +175,7 @@ impl WinitWindowWrapper { } => { self.handle_quit(); } - Event::WindowEvent { - event: WindowEvent::ScaleFactorChanged { scale_factor, .. }, - .. - } => { + Event::UserEvent(UserEvent::ScaleFactorChanged(scale_factor)) => { self.handle_scale_factor_update(scale_factor); } Event::WindowEvent { @@ -337,7 +339,7 @@ pub fn create_window() { Icon::from_rgba(rgba, width, height).expect("Failed to create icon object") }; - let event_loop = EventLoop::new(); + let event_loop = EventLoop::::with_user_event(); let cmd_line_settings = SETTINGS.get::(); @@ -408,7 +410,7 @@ pub fn create_window() { Unfocused, } - let (txtemp, rx) = mpsc::channel::>(); + let (txtemp, rx) = mpsc::channel::>(); let mut tx = Some(txtemp); let mut render_thread_handle = Some(thread::spawn(move || { let windowed_context = unsafe { windowed_context.make_current().unwrap() }; @@ -527,10 +529,22 @@ pub fn create_window() { })); event_loop.run(move |e, _window_target, control_flow| { - if let Some(event) = e.to_static() { - if let Some(tx) = &tx { - tx.send(event).unwrap(); + let e = match e { + Event::WindowEvent { + event: WindowEvent::ScaleFactorChanged { scale_factor, .. }, + .. + } => { + // It's really unfortunate that we have to do this, but + // https://github.com/rust-windowing/winit/issues/1387 + Event::UserEvent(UserEvent::ScaleFactorChanged(scale_factor)) + } + _ => { + // With the current Winit version, all events, except ScaleFactorChanged are static + e.to_static().expect("Unexpected event received") } + }; + if let Some(tx) = &tx { + tx.send(e).unwrap(); } if !RUNNING_TRACKER.is_running() { diff --git a/src/window/mouse_manager.rs b/src/window/mouse_manager.rs index 7e8f5778c..9e5a55dde 100644 --- a/src/window/mouse_manager.rs +++ b/src/window/mouse_manager.rs @@ -20,7 +20,7 @@ use crate::{ renderer::{Renderer, WindowDrawDetails}, settings::SETTINGS, window::keyboard_manager::KeyboardManager, - window::WindowSettings, + window::{UserEvent, WindowSettings}, }; fn clamp_position( @@ -412,7 +412,7 @@ impl MouseManager { pub fn handle_event( &mut self, - event: &Event<()>, + event: &Event, keyboard_manager: &KeyboardManager, renderer: &Renderer, window: &Window, From 38d8359fdcfb67993c34186f53f9d444013087d5 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 12 Mar 2023 14:15:30 +0200 Subject: [PATCH 23/53] Remove the redraw_scheduler The idea is to have the update phase determine if rendering is needed. But currently that's not dealt with correctly. --- src/editor/mod.rs | 6 ----- src/main.rs | 1 - src/redraw_scheduler.rs | 52 ----------------------------------------- src/window/mod.rs | 50 ++++++++++++++++++++++----------------- src/window/renderer.rs | 2 -- 5 files changed, 29 insertions(+), 82 deletions(-) delete mode 100644 src/redraw_scheduler.rs diff --git a/src/editor/mod.rs b/src/editor/mod.rs index 8a5272e17..28618aa72 100644 --- a/src/editor/mod.rs +++ b/src/editor/mod.rs @@ -12,7 +12,6 @@ use crate::{ bridge::{GuiOption, RedrawEvent, WindowAnchor}, event_aggregator::EVENT_AGGREGATOR, profiling::tracy_zone, - redraw_scheduler::REDRAW_SCHEDULER, renderer::DrawCommand, window::WindowCommand, }; @@ -136,10 +135,6 @@ impl Editor { trace!("send_batch"); self.draw_command_batcher.send_batch(); } - { - trace!("queue_next_frame"); - REDRAW_SCHEDULER.queue_next_frame(); - } } RedrawEvent::DefaultColorsSet { colors } => { tracy_zone!("EditorDefaultColorsSet"); @@ -148,7 +143,6 @@ impl Editor { .ok(); self.redraw_screen(); self.draw_command_batcher.send_batch(); - REDRAW_SCHEDULER.queue_next_frame(); } RedrawEvent::HighlightAttributesDefine { id, style } => { tracy_zone!("EditorHighlightAttributesDefine"); diff --git a/src/main.rs b/src/main.rs index 8434d27b6..6fd03f442 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,6 @@ mod error_handling; mod event_aggregator; mod frame; mod profiling; -mod redraw_scheduler; mod renderer; mod running_tracker; mod settings; diff --git a/src/redraw_scheduler.rs b/src/redraw_scheduler.rs deleted file mode 100644 index 80ca452fc..000000000 --- a/src/redraw_scheduler.rs +++ /dev/null @@ -1,52 +0,0 @@ -use std::{ - sync::{ - atomic::{AtomicBool, Ordering}, - Mutex, - }, - time::Instant, -}; - -use log::trace; - -lazy_static! { - pub static ref REDRAW_SCHEDULER: RedrawScheduler = RedrawScheduler::new(); -} - -pub struct RedrawScheduler { - scheduled_frame: Mutex>, - frame_queued: AtomicBool, -} - -impl RedrawScheduler { - pub fn new() -> RedrawScheduler { - RedrawScheduler { - scheduled_frame: Mutex::new(None), - frame_queued: AtomicBool::new(true), - } - } - - pub fn queue_next_frame(&self) { - trace!("Next frame queued"); - self.frame_queued.store(true, Ordering::Relaxed); - } - - pub fn should_draw(&self) -> bool { - if self.frame_queued.load(Ordering::Relaxed) { - self.frame_queued.store(false, Ordering::Relaxed); - true - } else { - let mut next_scheduled_frame = self.scheduled_frame.lock().unwrap(); - - if let Some(scheduled_frame) = *next_scheduled_frame { - if scheduled_frame < Instant::now() { - *next_scheduled_frame = None; - true - } else { - false - } - } else { - false - } - } - } -} diff --git a/src/window/mod.rs b/src/window/mod.rs index ee21549fb..3799bd14f 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -44,7 +44,6 @@ use crate::{ editor::EditorCommand, event_aggregator::EVENT_AGGREGATOR, frame::Frame, - redraw_scheduler::REDRAW_SCHEDULER, renderer::Renderer, renderer::WindowPadding, renderer::{build_context, WindowedContext}, @@ -149,11 +148,11 @@ impl WinitWindowWrapper { pub fn handle_focus_gained(&mut self) { EVENT_AGGREGATOR.send(UiCommand::Parallel(ParallelCommand::FocusGained)); - REDRAW_SCHEDULER.queue_next_frame(); } - pub fn handle_event(&mut self, event: Event) { + pub fn handle_event(&mut self, event: Event) -> bool { tracy_zone!("handle_event", 0); + let mut should_render = false; self.keyboard_manager.handle_event(&event); self.mouse_manager.handle_event( &event, @@ -191,32 +190,32 @@ impl WinitWindowWrapper { } => { if focus { self.handle_focus_gained(); + should_render = true; } else { self.handle_focus_lost(); } } Event::RedrawRequested(..) | Event::WindowEvent { .. } => { - REDRAW_SCHEDULER.queue_next_frame() + should_render = true; } _ => {} } + should_render } pub fn draw_frame(&mut self, dt: f32) { tracy_zone!("draw_frame"); - if REDRAW_SCHEDULER.should_draw() || SETTINGS.get::().no_idle { - self.renderer.draw_frame(self.skia_renderer.canvas(), dt); - { - tracy_gpu_zone!("skia flush"); - self.skia_renderer.gr_context.flush(None); - } - { - tracy_gpu_zone!("swap buffers"); - self.windowed_context.swap_buffers().unwrap(); - } - emit_frame_mark(); - tracy_gpu_collect(); + self.renderer.draw_frame(self.skia_renderer.canvas(), dt); + { + tracy_gpu_zone!("skia flush"); + self.skia_renderer.gr_context.flush(None); + } + { + tracy_gpu_zone!("swap buffers"); + self.windowed_context.swap_buffers().unwrap(); } + emit_frame_mark(); + tracy_gpu_collect(); } pub fn animate_frame(&mut self, dt: f32) -> bool { @@ -224,8 +223,9 @@ impl WinitWindowWrapper { self.renderer.animate_frame(dt) } - pub fn prepare_frame(&mut self) { + pub fn prepare_frame(&mut self) -> bool { tracy_zone!("prepare_frame", 0); + let mut should_render = false; let window = self.windowed_context.window(); let new_size = window.inner_size(); @@ -249,6 +249,7 @@ impl WinitWindowWrapper { self.handle_new_grid_size(new_size); self.skia_renderer.resize(&self.windowed_context); + should_render = true; } self.font_changed_last_frame = self @@ -257,7 +258,7 @@ impl WinitWindowWrapper { // Wait until fonts are loaded, so we can set proper window size. if !self.renderer.grid_renderer.is_ready { - return; + return false; } let settings = SETTINGS.get::(); @@ -285,6 +286,7 @@ impl WinitWindowWrapper { // But only when not resized yet. With maximized or resized window we should redraw grid. self.font_changed_last_frame = false; } + should_render } fn handle_new_grid_size(&mut self, new_size: PhysicalSize) { @@ -480,9 +482,11 @@ pub fn create_window() { let mut focused = FocusedState::Focused; let mut prev_frame_start = Instant::now(); + #[allow(unused_assignments)] loop { tracy_zone!("render loop", 0); let e = rx.try_recv(); + let mut should_render = false; match e { // Window focus changed @@ -502,15 +506,19 @@ pub fn create_window() { _ => {} } if let Ok(e) = e { - window_wrapper.handle_event(e); + should_render |= window_wrapper.handle_event(e); window_wrapper.handle_window_commands(); window_wrapper.synchronize_settings(); } else { let frame_start = Instant::now(); let dt = prev_frame_start.elapsed().as_secs_f32(); - window_wrapper.prepare_frame(); + should_render |= window_wrapper.prepare_frame(); window_wrapper.animate_frame(dt); - window_wrapper.draw_frame(dt); + // Always render for now + #[allow(clippy::overly_complex_bool_expr)] + if should_render || true { + window_wrapper.draw_frame(dt); + } if let FocusedState::UnfocusedNotDrawn = focused { focused = FocusedState::Unfocused; } diff --git a/src/window/renderer.rs b/src/window/renderer.rs index 707f040b3..ca531f695 100644 --- a/src/window/renderer.rs +++ b/src/window/renderer.rs @@ -1,6 +1,5 @@ use std::convert::TryInto; -use crate::redraw_scheduler::REDRAW_SCHEDULER; use crate::renderer::WindowedContext; use gl::types::*; use skia_safe::{ @@ -86,6 +85,5 @@ impl SkiaRenderer { pub fn resize(&mut self, windowed_context: &WindowedContext) { self.surface = create_surface(windowed_context, &mut self.gr_context, self.fb_info); - REDRAW_SCHEDULER.queue_next_frame(); } } From 60c886ac955788075afb474a5f8e1240d8b844fa Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 2 Oct 2022 15:54:49 +0300 Subject: [PATCH 24/53] Use maximum dt timesteps for animation --- src/renderer/rendered_window.rs | 11 +++-------- src/window/mod.rs | 13 ++++++++++--- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/renderer/rendered_window.rs b/src/renderer/rendered_window.rs index 947e0d56b..37c0165c1 100644 --- a/src/renderer/rendered_window.rs +++ b/src/renderer/rendered_window.rs @@ -226,14 +226,9 @@ impl RenderedWindow { let omega = 4.0 / (zeta * settings.scroll_animation_length); let k_p = omega * omega; let k_d = -2.0 * zeta * omega; - let timestep = 0.01; - let mut dt = dt; - while dt > 0.0 { - let acc = k_p * (scroll_destination - self.current_scroll) + k_d * self.scroll_v; - self.scroll_v += acc * timestep; - self.current_scroll += self.scroll_v * timestep; - dt -= timestep; - } + let acc = k_p * (scroll_destination - self.current_scroll) + k_d * self.scroll_v; + self.scroll_v += acc * dt; + self.current_scroll += self.scroll_v * dt; if (self.current_scroll - scroll_destination).abs() < 0.01 { self.reset_scroll(); diff --git a/src/window/mod.rs b/src/window/mod.rs index 3799bd14f..551fecffa 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -479,6 +479,7 @@ pub fn create_window() { tracy_create_gpu_context("main render context"); + let max_animation_dt = 1.0 / 120.0; let mut focused = FocusedState::Focused; let mut prev_frame_start = Instant::now(); @@ -511,13 +512,19 @@ pub fn create_window() { window_wrapper.synchronize_settings(); } else { let frame_start = Instant::now(); - let dt = prev_frame_start.elapsed().as_secs_f32(); + let frame_dt = prev_frame_start.elapsed().as_secs_f64(); + let mut dt = frame_dt; should_render |= window_wrapper.prepare_frame(); - window_wrapper.animate_frame(dt); + while dt > 0.0 { + let step = dt.min(max_animation_dt); + + window_wrapper.animate_frame(step as f32); + dt -= step; + } // Always render for now #[allow(clippy::overly_complex_bool_expr)] if should_render || true { - window_wrapper.draw_frame(dt); + window_wrapper.draw_frame(frame_dt as f32); } if let FocusedState::UnfocusedNotDrawn = focused { focused = FocusedState::Unfocused; From 339a5fb7bd94264fd605a559d9c4e6d90ec4aeec Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 30 Oct 2022 19:46:33 +0200 Subject: [PATCH 25/53] Calculate the frame time directly after the previous frame is ready --- src/window/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/window/mod.rs b/src/window/mod.rs index 551fecffa..90ceb35c9 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -482,6 +482,7 @@ pub fn create_window() { let max_animation_dt = 1.0 / 120.0; let mut focused = FocusedState::Focused; let mut prev_frame_start = Instant::now(); + let mut frame_dt: f32 = 0.0; #[allow(unused_assignments)] loop { @@ -511,25 +512,24 @@ pub fn create_window() { window_wrapper.handle_window_commands(); window_wrapper.synchronize_settings(); } else { - let frame_start = Instant::now(); - let frame_dt = prev_frame_start.elapsed().as_secs_f64(); let mut dt = frame_dt; should_render |= window_wrapper.prepare_frame(); while dt > 0.0 { let step = dt.min(max_animation_dt); - window_wrapper.animate_frame(step as f32); + window_wrapper.animate_frame(step); dt -= step; } // Always render for now #[allow(clippy::overly_complex_bool_expr)] if should_render || true { - window_wrapper.draw_frame(frame_dt as f32); + window_wrapper.draw_frame(frame_dt); + frame_dt = prev_frame_start.elapsed().as_secs_f32(); + prev_frame_start = Instant::now(); } if let FocusedState::UnfocusedNotDrawn = focused { focused = FocusedState::Unfocused; } - prev_frame_start = frame_start; #[cfg(target_os = "macos")] draw_background(&window_wrapper.window); } From f5490594e80371e1beab4784504bd38a7c0fc35a Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Mon, 31 Oct 2022 22:14:55 +0200 Subject: [PATCH 26/53] Filter the frame dt by a moving average --- Cargo.toml | 216 +++++++++++++++++++++++----------------------- src/window/mod.rs | 13 +-- 2 files changed, 116 insertions(+), 113 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d8f093272..698c9d464 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,108 +1,108 @@ -[package] -name = "neovide" -version = "0.10.3" -edition = "2021" -build = "build.rs" -description = "Neovide: No Nonsense Neovim Gui" -repository = "https://github.com/neovide/neovide" -resolver = "2" - -[workspace] -members = ["neovide-derive"] - -[features] -default = [] -embed-fonts = [] -profiling = ["dep:tracy-client-sys"] -gpu_profiling = ["profiling"] - -[dependencies] -async-trait = "0.1.53" -backtrace = "0.3.67" -cfg-if = "1.0.0" -clap = { version = "4.0.8", features = ["cargo", "derive", "env"] } -copypasta = "0.8.1" -csscolorparser = "0.6.2" -derive-new = "0.5.9" -dirs = "4.0.0" -euclid = "0.22.7" -flexi_logger = { version = "0.22.3", default-features = false } -futures = "0.3.21" -gl = "0.14.0" -glutin = { git = "https://github.com/neovide/glutin", branch = "new-keyboard-all", features = ["serde"] } -image = { version = "0.24.1", default-features = false, features = ["ico"] } -itertools = "0.10.5" -lazy_static = "1.4.0" -log = "0.4.16" -lru = "0.7.5" -neovide-derive = { path = "neovide-derive" } -nvim-rs = { version = "0.5.0", features = ["use_tokio"] } -parking_lot = "0.12.0" -pin-project = "1.0.10" -rand = "0.8.5" -rmpv = "1.0.0" -serde = { version = "1.0.136", features = ["derive"] } -serde_json = "1.0.79" -simple_moving_average = "0.1.2" -swash = "0.1.4" -time = "0.3.9" -tokio = { version = "1.25.0", features = ["full"] } -tokio-util = { version = "0.7.4", features = ["compat"] } -tracy-client-sys = { version = "0.19.0", optional = true } -unicode-segmentation = "1.9.0" -which = "4.2.5" -winit = { git = "https://github.com/neovide/winit", branch = "new-keyboard-all" } -xdg = "2.4.1" - -[dev-dependencies] -mockall = "0.11.0" - -[target.'cfg(windows)'.dependencies] -winapi = { version = "0.3.9", features = ["winuser"] } - -[target.'cfg(target_os = "macos")'.dependencies] -cocoa = "0.24.0" -objc = "0.2.7" -shlex = "1.1.0" - -[target.'cfg(not(windows))'.dependencies] - -[target.'cfg(windows)'.build-dependencies] -winres = "0.1.12" - -[target.'cfg(windows)'.dependencies.skia-safe] -# NOTE: Including textlayout for pre-built binaries -features = ["d3d", "gl", "textlayout"] -version = "0.60.0" - -[target.'cfg(linux)'.dependencies.skia-safe] -features = ["gl", "egl"] -version = "0.60.0" - -[target.'cfg(not(linux))'.dependencies.skia-safe] -features = ["gl"] -version = "0.60.0" - -[profile.release] -lto = true -incremental = true -strip = true - -[profile.profiling] -inherits = "release" -strip = false -debug = true - -[package.metadata.bundle] -name = "Neovide" -identifier = "com.neovide.neovide" -icon = ["assets/neovide.ico"] -version = "0.10.3" -resources = [] -copyright = "Copyright (c) Neovide Contributors 2023. All rights reserved." -category = "Productivity" -short_description = "A simple GUI for Neovim." -long_description = """ -This is a simple graphical user interface for Neovim. Where possible there are some graphical improvements, but it should act functionally like the terminal UI. -""" -osx_minimum_system_version = "10.11" +[package] +name = "neovide" +version = "0.10.3" +edition = "2021" +build = "build.rs" +description = "Neovide: No Nonsense Neovim Gui" +repository = "https://github.com/neovide/neovide" +resolver = "2" + +[workspace] +members = ["neovide-derive"] + +[features] +default = [] +embed-fonts = [] +profiling = ["dep:tracy-client-sys"] +gpu_profiling = ["profiling"] + +[dependencies] +async-trait = "0.1.53" +backtrace = "0.3.67" +cfg-if = "1.0.0" +clap = { version = "4.0.8", features = ["cargo", "derive", "env"] } +copypasta = "0.8.1" +csscolorparser = "0.6.2" +derive-new = "0.5.9" +dirs = "4.0.0" +euclid = "0.22.7" +flexi_logger = { version = "0.22.3", default-features = false } +futures = "0.3.21" +gl = "0.14.0" +glutin = { git = "https://github.com/neovide/glutin", branch = "new-keyboard-all", features = ["serde"] } +image = { version = "0.24.1", default-features = false, features = ["ico"] } +itertools = "0.10.5" +lazy_static = "1.4.0" +log = "0.4.16" +lru = "0.7.5" +neovide-derive = { path = "neovide-derive" } +nvim-rs = { version = "0.5.0", features = ["use_tokio"] } +parking_lot = "0.12.0" +pin-project = "1.0.10" +rand = "0.8.5" +rmpv = "1.0.0" +serde = { version = "1.0.136", features = ["derive"] } +serde_json = "1.0.79" +simple_moving_average = "0.1.2" +swash = "0.1.4" +time = "0.3.9" +tokio = { version = "1.25.0", features = ["full"] } +tokio-util = { version = "0.7.4", features = ["compat"] } +tracy-client-sys = { version = "0.19.0", optional = true } +unicode-segmentation = "1.9.0" +which = "4.2.5" +winit = { git = "https://github.com/neovide/winit", branch = "new-keyboard-all" } +xdg = "2.4.1" + +[dev-dependencies] +mockall = "0.11.0" + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3.9", features = ["winuser"] } + +[target.'cfg(target_os = "macos")'.dependencies] +cocoa = "0.24.0" +objc = "0.2.7" +shlex = "1.1.0" + +[target.'cfg(not(windows))'.dependencies] + +[target.'cfg(windows)'.build-dependencies] +winres = "0.1.12" + +[target.'cfg(windows)'.dependencies.skia-safe] +# NOTE: Including textlayout for pre-built binaries +features = ["d3d", "gl", "textlayout"] +version = "0.60.0" + +[target.'cfg(linux)'.dependencies.skia-safe] +features = ["gl", "egl"] +version = "0.60.0" + +[target.'cfg(not(linux))'.dependencies.skia-safe] +features = ["gl"] +version = "0.60.0" + +[profile.release] +lto = true +incremental = true +strip = true + +[profile.profiling] +inherits = "release" +strip = false +debug = true + +[package.metadata.bundle] +name = "Neovide" +identifier = "com.neovide.neovide" +icon = ["assets/neovide.ico"] +version = "0.10.3" +resources = [] +copyright = "Copyright (c) Neovide Contributors 2023. All rights reserved." +category = "Productivity" +short_description = "A simple GUI for Neovim." +long_description = """ +This is a simple graphical user interface for Neovim. Where possible there are some graphical improvements, but it should act functionally like the terminal UI. +""" +osx_minimum_system_version = "10.11" diff --git a/src/window/mod.rs b/src/window/mod.rs index 90ceb35c9..9beebaa8e 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -11,6 +11,7 @@ use std::thread; use std::time::Instant; use log::trace; +use simple_moving_average::{NoSumSMA, SMA}; use tokio::sync::mpsc::UnboundedReceiver; use winit::{ dpi::PhysicalSize, @@ -482,7 +483,7 @@ pub fn create_window() { let max_animation_dt = 1.0 / 120.0; let mut focused = FocusedState::Focused; let mut prev_frame_start = Instant::now(); - let mut frame_dt: f32 = 0.0; + let mut frame_dt_avg = NoSumSMA::::new(); #[allow(unused_assignments)] loop { @@ -512,21 +513,23 @@ pub fn create_window() { window_wrapper.handle_window_commands(); window_wrapper.synchronize_settings(); } else { - let mut dt = frame_dt; + let mut dt = frame_dt_avg.get_average(); should_render |= window_wrapper.prepare_frame(); while dt > 0.0 { let step = dt.min(max_animation_dt); - window_wrapper.animate_frame(step); + window_wrapper.animate_frame(step as f32); dt -= step; } // Always render for now #[allow(clippy::overly_complex_bool_expr)] if should_render || true { - window_wrapper.draw_frame(frame_dt); - frame_dt = prev_frame_start.elapsed().as_secs_f32(); + window_wrapper + .draw_frame(frame_dt_avg.get_most_recent_sample().unwrap_or(0.0) as f32); + frame_dt_avg.add_sample(prev_frame_start.elapsed().as_secs_f64()); prev_frame_start = Instant::now(); } + if let FocusedState::UnfocusedNotDrawn = focused { focused = FocusedState::Unfocused; } From f66c634446a2a8674485ea4efce7280e84adaf35 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 30 Oct 2022 23:54:58 +0200 Subject: [PATCH 27/53] Use gr_context.flush_and_submit This properly submits everything to the GPU --- src/window/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/window/mod.rs b/src/window/mod.rs index 9beebaa8e..af25a6995 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -209,7 +209,7 @@ impl WinitWindowWrapper { self.renderer.draw_frame(self.skia_renderer.canvas(), dt); { tracy_gpu_zone!("skia flush"); - self.skia_renderer.gr_context.flush(None); + self.skia_renderer.gr_context.flush_and_submit(); } { tracy_gpu_zone!("swap buffers"); From aff413bb2d67c2f365c5b78972c8263b98e48dd1 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Tue, 24 Jan 2023 18:35:55 +0200 Subject: [PATCH 28/53] Split up skia operations for more performance and accurate profiling It now draws the surfaces when waiting for the next backbuffer to become ready for increased performance. --- src/renderer/mod.rs | 60 +++++++++++++++++++++------------ src/renderer/rendered_window.rs | 2 +- src/window/mod.rs | 14 ++++++++ 3 files changed, 53 insertions(+), 23 deletions(-) diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 440670e54..11bfab897 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -139,26 +139,7 @@ impl Renderer { self.grid_renderer.font_names() } - /// Draws frame - /// - /// # Returns - /// `bool` indicating whether or not font was changed during this frame. - #[allow(clippy::needless_collect)] - pub fn draw_frame(&mut self, root_canvas: &mut Canvas, dt: f32) { - tracy_zone!("renderer_draw_frame"); - let default_background = self.grid_renderer.get_default_background(); - let font_dimensions = self.grid_renderer.font_dimensions; - - let transparency = { SETTINGS.get::().transparency }; - root_canvas.clear(default_background.with_a((255.0 * transparency) as u8)); - root_canvas.save(); - root_canvas.reset_matrix(); - - if let Some(root_window) = self.rendered_windows.get(&1) { - let clip_rect = root_window.pixel_region(font_dimensions); - root_canvas.clip_rect(clip_rect, None, Some(false)); - } - + fn get_sorted_windows(&mut self) -> Vec<&mut RenderedWindow> { let windows: Vec<&mut RenderedWindow> = { let (mut root_windows, mut floating_windows): ( Vec<&mut RenderedWindow>, @@ -179,13 +160,48 @@ impl Renderer { .chain(floating_windows.into_iter()) .collect() }; + windows + } + + pub fn draw_window_surfaces(&mut self) { + let font_dimensions = self.grid_renderer.font_dimensions; + let default_background = self.grid_renderer.get_default_background(); + let windows = self.get_sorted_windows(); + + for window in windows { + window.draw_surface(font_dimensions, default_background); + } + } + + /// Draws frame + /// + /// # Returns + /// `bool` indicating whether or not font was changed during this frame. + #[allow(clippy::needless_collect)] + pub fn draw_frame(&mut self, root_canvas: &mut Canvas, dt: f32) { + tracy_zone!("renderer_draw_frame"); + let default_background = self.grid_renderer.get_default_background(); + let font_dimensions = self.grid_renderer.font_dimensions; + + let transparency = { SETTINGS.get::().transparency }; + root_canvas.clear(default_background.with_a((255.0 * transparency) as u8)); + root_canvas.save(); + root_canvas.reset_matrix(); + + if let Some(root_window) = self.rendered_windows.get(&1) { + let clip_rect = root_window.pixel_region(font_dimensions); + root_canvas.clip_rect(clip_rect, None, Some(false)); + } + + let self_window_padding = self.window_padding; + let windows = self.get_sorted_windows(); let settings = SETTINGS.get::(); self.window_regions = windows .into_iter() .map(|window| { - if window.padding != self.window_padding { - window.padding = self.window_padding; + if window.padding != self_window_padding { + window.padding = self_window_padding; } window.draw( diff --git a/src/renderer/rendered_window.rs b/src/renderer/rendered_window.rs index 37c0165c1..11192643a 100644 --- a/src/renderer/rendered_window.rs +++ b/src/renderer/rendered_window.rs @@ -240,7 +240,7 @@ impl RenderedWindow { animating } - fn draw_surface(&mut self, font_dimensions: Dimensions, default_background: Color) -> bool { + pub fn draw_surface(&mut self, font_dimensions: Dimensions, default_background: Color) -> bool { let image_size: (i32, i32) = (self.grid_size * font_dimensions).into(); let pixel_region = Rect::from_size(image_size); let canvas = self.current_surface.surface.canvas(); diff --git a/src/window/mod.rs b/src/window/mod.rs index af25a6995..1695b2fc3 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -206,6 +206,20 @@ impl WinitWindowWrapper { pub fn draw_frame(&mut self, dt: f32) { tracy_zone!("draw_frame"); + + { + tracy_gpu_zone!("draw window surfaces"); + self.renderer.draw_window_surfaces(); + self.skia_renderer.gr_context.flush_and_submit(); + } + + { + tracy_gpu_zone!("skia clear"); + let default_background = self.renderer.grid_renderer.get_default_background(); + self.skia_renderer.canvas().clear(default_background); + self.skia_renderer.gr_context.flush_and_submit(); + } + self.renderer.draw_frame(self.skia_renderer.canvas(), dt); { tracy_gpu_zone!("skia flush"); From a3dce08a8101b706e5e3508b6bbf03e15ce2c461 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Fri, 24 Mar 2023 19:42:36 +0200 Subject: [PATCH 29/53] Add wgpu, remove glutin and use only base Skia --- Cargo.lock | 542 +++++++++++++++++++++++++++++++++-------------------- Cargo.toml | 16 +- 2 files changed, 341 insertions(+), 217 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 90268109a..f53d1a6c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,10 +78,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" [[package]] -name = "android_glue" -version = "0.2.3" +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] [[package]] name = "anstream" @@ -135,6 +138,15 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +[[package]] +name = "ash" +version = "0.37.2+1.3.238" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28bf19c1f0a470be5fbf7522a308a05df06610252c5bcf5143e1b23f629a9a03" +dependencies = [ + "libloading", +] + [[package]] name = "async-trait" version = "0.1.68" @@ -195,6 +207,21 @@ dependencies = [ "which", ] +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" @@ -303,15 +330,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" -[[package]] -name = "cgl" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" -dependencies = [ - "libc", -] - [[package]] name = "clang-sys" version = "1.6.1" @@ -407,6 +425,16 @@ dependencies = [ "objc", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -501,6 +529,17 @@ dependencies = [ "phf", ] +[[package]] +name = "d3d12" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "827914e1f53b1e0e025ecd3d967a7836b7bcb54520f90e21ef8df7b4d88a2759" +dependencies = [ + "bitflags", + "libloading", + "winapi", +] + [[package]] name = "derive-new" version = "0.5.9" @@ -773,6 +812,15 @@ dependencies = [ "tokio-io", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "gethostname" version = "0.2.3" @@ -838,69 +886,54 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] -name = "glutin" -version = "0.26.0" -source = "git+https://github.com/neovide/glutin?branch=new-keyboard-v3#ba68bd7e8ce11c33bf14a90ee4c9a159b72e82d8" +name = "glow" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8bd5877156a19b8ac83a29b2306fe20537429d318f3ff0a1a2119f8d9c61919" dependencies = [ - "android_glue", - "cgl", - "cocoa", - "core-foundation", - "glutin_egl_sys", - "glutin_emscripten_sys", - "glutin_gles2_sys", - "glutin_glx_sys", - "glutin_wgl_sys", - "lazy_static", - "libloading", - "log", - "objc", - "osmesa-sys", - "parking_lot 0.11.2", - "wayland-client 0.28.6", - "wayland-egl", - "winapi", - "winit", + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", ] [[package]] -name = "glutin_egl_sys" -version = "0.1.5" -source = "git+https://github.com/neovide/glutin?branch=new-keyboard-v3#ba68bd7e8ce11c33bf14a90ee4c9a159b72e82d8" +name = "gpu-alloc" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc59e5f710e310e76e6707f86c561dd646f69a8876da9131703b2f717de818d" dependencies = [ - "gl_generator", - "winapi", + "bitflags", + "gpu-alloc-types", ] [[package]] -name = "glutin_emscripten_sys" -version = "0.1.1" -source = "git+https://github.com/neovide/glutin?branch=new-keyboard-v3#ba68bd7e8ce11c33bf14a90ee4c9a159b72e82d8" - -[[package]] -name = "glutin_gles2_sys" -version = "0.1.5" -source = "git+https://github.com/neovide/glutin?branch=new-keyboard-v3#ba68bd7e8ce11c33bf14a90ee4c9a159b72e82d8" +name = "gpu-alloc-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5" dependencies = [ - "gl_generator", - "objc", + "bitflags", ] [[package]] -name = "glutin_glx_sys" -version = "0.1.7" -source = "git+https://github.com/neovide/glutin?branch=new-keyboard-v3#ba68bd7e8ce11c33bf14a90ee4c9a159b72e82d8" +name = "gpu-descriptor" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b0c02e1ba0bdb14e965058ca34e09c020f8e507a760df1121728e0aef68d57a" dependencies = [ - "gl_generator", - "x11-dl", + "bitflags", + "gpu-descriptor-types", + "hashbrown", ] [[package]] -name = "glutin_wgl_sys" -version = "0.1.5" -source = "git+https://github.com/neovide/glutin?branch=new-keyboard-v3#ba68bd7e8ce11c33bf14a90ee4c9a159b72e82d8" +name = "gpu-descriptor-types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "363e3677e55ad168fef68cf9de3a4a310b53124c5e784c53a1d70e92d23f2126" dependencies = [ - "gl_generator", + "bitflags", ] [[package]] @@ -933,6 +966,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + [[package]] name = "idna" version = "0.3.0" @@ -1050,6 +1089,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "khronos-egl" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3" +dependencies = [ + "libc", + "libloading", + "pkg-config", +] + [[package]] name = "khronos_api" version = "3.1.0" @@ -1166,6 +1216,20 @@ dependencies = [ "autocfg", ] +[[package]] +name = "metal" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de11355d1f6781482d027a3b4d4de7825dcedb197bf573e0596d00008402d060" +dependencies = [ + "bitflags", + "block", + "core-graphics-types", + "foreign-types", + "log", + "objc", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1220,6 +1284,26 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "naga" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "262d2840e72dbe250e8cf2f522d080988dfca624c4112c096238a4845f591707" +dependencies = [ + "bit-set", + "bitflags", + "codespan-reporting", + "hexf-parse", + "indexmap", + "log", + "num-traits", + "rustc-hash", + "spirv", + "termcolor", + "thiserror", + "unicode-xid", +] + [[package]] name = "nameof" version = "1.2.2" @@ -1272,7 +1356,6 @@ dependencies = [ "flexi_logger", "futures 0.3.28", "gl", - "glutin", "image", "itertools", "lazy_static", @@ -1282,7 +1365,7 @@ dependencies = [ "neovide-derive", "nvim-rs", "objc", - "parking_lot 0.12.1", + "parking_lot", "pin-project", "rand 0.8.5", "rmpv", @@ -1297,6 +1380,7 @@ dependencies = [ "tokio-util", "tracy-client-sys", "unicode-segmentation", + "wgpu", "which", "winapi", "winit", @@ -1312,18 +1396,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "nix" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" -dependencies = [ - "bitflags", - "cc", - "cfg-if", - "libc", -] - [[package]] name = "nix" version = "0.24.3" @@ -1458,6 +1530,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" dependencies = [ "malloc_buf", + "objc_exception", ] [[package]] @@ -1497,6 +1570,15 @@ dependencies = [ "objc-sys", ] +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + [[package]] name = "objc_id" version = "0.1.1" @@ -1533,15 +1615,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "osmesa-sys" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" -dependencies = [ - "shared_library", -] - [[package]] name = "owned_ttf_parser" version = "0.18.1" @@ -1565,17 +1638,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.1" @@ -1583,21 +1645,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.7", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] @@ -1778,6 +1826,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "profiling" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74605f360ce573babfe43964cbe520294dcb081afbf8c108fc6e23036b4da2df" + [[package]] name = "quote" version = "1.0.26" @@ -1858,6 +1912,12 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "range-alloc" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" + [[package]] name = "raw-window-handle" version = "0.5.2" @@ -1910,6 +1970,12 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +[[package]] +name = "renderdoc-sys" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" + [[package]] name = "ring" version = "0.16.20" @@ -2071,16 +2137,6 @@ dependencies = [ "serde", ] -[[package]] -name = "shared_library" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" -dependencies = [ - "lazy_static", - "libc", -] - [[package]] name = "shlex" version = "1.1.0" @@ -2138,8 +2194,6 @@ dependencies = [ "bitflags", "lazy_static", "skia-bindings", - "winapi", - "wio", ] [[package]] @@ -2180,7 +2234,7 @@ dependencies = [ "memmap2 0.5.10", "nix 0.24.3", "pkg-config", - "wayland-client 0.29.5", + "wayland-client", "wayland-cursor", "wayland-protocols", ] @@ -2192,7 +2246,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a345c870a1fae0b1b779085e81b51e614767c239e93503588e54c5b17f4b0e8" dependencies = [ "smithay-client-toolkit", - "wayland-client 0.29.5", + "wayland-client", ] [[package]] @@ -2211,6 +2265,22 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spirv" +version = "0.2.0+1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830" +dependencies = [ + "bitflags", + "num-traits", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strict-num" version = "0.1.0" @@ -2266,6 +2336,15 @@ dependencies = [ "xattr", ] +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + [[package]] name = "termtree" version = "0.4.1" @@ -2361,7 +2440,7 @@ dependencies = [ "libc", "mio", "num_cpus", - "parking_lot 0.12.1", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", @@ -2490,6 +2569,18 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + [[package]] name = "untrusted" version = "0.7.1" @@ -2578,6 +2669,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.84" @@ -2607,22 +2710,6 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" -[[package]] -name = "wayland-client" -version = "0.28.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ab332350e502f159382201394a78e3cc12d0f04db863429260164ea40e0355" -dependencies = [ - "bitflags", - "downcast-rs", - "libc", - "nix 0.20.0", - "scoped-tls", - "wayland-commons 0.28.6", - "wayland-scanner 0.28.6", - "wayland-sys 0.28.6", -] - [[package]] name = "wayland-client" version = "0.29.5" @@ -2634,21 +2721,9 @@ dependencies = [ "libc", "nix 0.24.3", "scoped-tls", - "wayland-commons 0.29.5", - "wayland-scanner 0.29.5", - "wayland-sys 0.29.5", -] - -[[package]] -name = "wayland-commons" -version = "0.28.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21817947c7011bbd0a27e11b17b337bfd022e8544b071a2641232047966fbda" -dependencies = [ - "nix 0.20.0", - "once_cell", - "smallvec", - "wayland-sys 0.28.6", + "wayland-commons", + "wayland-scanner", + "wayland-sys", ] [[package]] @@ -2660,7 +2735,7 @@ dependencies = [ "nix 0.24.3", "once_cell", "smallvec", - "wayland-sys 0.29.5", + "wayland-sys", ] [[package]] @@ -2670,20 +2745,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" dependencies = [ "nix 0.24.3", - "wayland-client 0.29.5", + "wayland-client", "xcursor", ] -[[package]] -name = "wayland-egl" -version = "0.28.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99ba1ab1e18756b23982d36f08856d521d7df45015f404a2d7c4f0b2d2f66956" -dependencies = [ - "wayland-client 0.28.6", - "wayland-sys 0.28.6", -] - [[package]] name = "wayland-protocols" version = "0.29.5" @@ -2691,20 +2756,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" dependencies = [ "bitflags", - "wayland-client 0.29.5", - "wayland-commons 0.29.5", - "wayland-scanner 0.29.5", -] - -[[package]] -name = "wayland-scanner" -version = "0.28.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce923eb2deb61de332d1f356ec7b6bf37094dc5573952e1c8936db03b54c03f1" -dependencies = [ - "proc-macro2", - "quote", - "xml-rs", + "wayland-client", + "wayland-commons", + "wayland-scanner", ] [[package]] @@ -2718,17 +2772,6 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "wayland-sys" -version = "0.28.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d841fca9aed7febf9bed2e9796c49bf58d4152ceda8ac949ebe00868d8f0feb8" -dependencies = [ - "dlib", - "lazy_static", - "pkg-config", -] - [[package]] name = "wayland-sys" version = "0.29.5" @@ -2769,6 +2812,100 @@ dependencies = [ "webpki", ] +[[package]] +name = "wgpu" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f643110d228fd62a60c5ed2ab56c4d5b3704520bd50561174ec4ec74932937" +dependencies = [ + "arrayvec", + "js-sys", + "log", + "naga", + "parking_lot", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6000d1284ef8eec6076fd5544a73125fd7eb9b635f18dceeb829d826f41724ca" +dependencies = [ + "arrayvec", + "bit-vec", + "bitflags", + "cfg_aliases", + "codespan-reporting", + "fxhash", + "log", + "naga", + "parking_lot", + "profiling", + "raw-window-handle", + "smallvec", + "thiserror", + "web-sys", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-hal" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cc320a61acb26be4f549c9b1b53405c10a223fbfea363ec39474c32c348d12f" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set", + "bitflags", + "block", + "core-graphics-types", + "d3d12", + "foreign-types", + "fxhash", + "glow", + "gpu-alloc", + "gpu-descriptor", + "js-sys", + "khronos-egl", + "libloading", + "log", + "metal", + "naga", + "objc", + "parking_lot", + "profiling", + "range-alloc", + "raw-window-handle", + "renderdoc-sys", + "smallvec", + "thiserror", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "winapi", +] + +[[package]] +name = "wgpu-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb6b28ef22cac17b9109b25b3bf8c9a103eeb293d7c5f78653979b09140375f6" +dependencies = [ + "bitflags", +] + [[package]] name = "which" version = "4.4.0" @@ -2796,6 +2933,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-wsapoll" version = "0.1.1" @@ -2903,14 +3049,13 @@ dependencies = [ "raw-window-handle", "redox_syscall 0.3.5", "sctk-adwaita", - "serde", "smithay-client-toolkit", "unicode-segmentation", "wasm-bindgen", - "wayland-client 0.29.5", - "wayland-commons 0.29.5", + "wayland-client", + "wayland-commons", "wayland-protocols", - "wayland-scanner 0.29.5", + "wayland-scanner", "web-sys", "windows-sys", "x11-dl", @@ -2935,15 +3080,6 @@ dependencies = [ "toml 0.5.11", ] -[[package]] -name = "wio" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" -dependencies = [ - "winapi", -] - [[package]] name = "x11-clipboard" version = "0.7.1" diff --git a/Cargo.toml b/Cargo.toml index e1dfe3115..3805c4cf7 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,6 @@ euclid = "0.22.7" flexi_logger = { version = "0.22.3", default-features = false } futures = "0.3.21" gl = "0.14.0" -glutin = { git = "https://github.com/neovide/glutin", branch = "new-keyboard-v3", features = ["serde"] } image = { version = "0.24.1", default-features = false, features = ["ico"] } itertools = "0.10.5" lazy_static = "1.4.0" @@ -53,6 +52,8 @@ unicode-segmentation = "1.9.0" which = "4.2.5" winit = { git = "https://github.com/neovide/winit", branch = "new-keyboard-v3" } xdg = "2.4.1" +wgpu = "0.14.2" +skia-safe = "0.60.0" [dev-dependencies] mockall = "0.11.0" @@ -70,19 +71,6 @@ shlex = "1.1.0" [target.'cfg(windows)'.build-dependencies] winres = "0.1.12" -[target.'cfg(windows)'.dependencies.skia-safe] -# NOTE: Including textlayout for pre-built binaries -features = ["d3d", "gl", "textlayout"] -version = "0.60.0" - -[target.'cfg(linux)'.dependencies.skia-safe] -features = ["gl", "egl"] -version = "0.60.0" - -[target.'cfg(not(linux))'.dependencies.skia-safe] -features = ["gl"] -version = "0.60.0" - [profile.release] lto = true incremental = true From ef8c21e30b299129e25d7306ef5199b9f30d8f05 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 25 Mar 2023 00:39:07 +0200 Subject: [PATCH 30/53] Change types and comment out code to make it compile --- Cargo.lock | 1 + Cargo.toml | 2 +- src/bridge/events.rs | 11 ++--- src/dimensions.rs | 18 +++++++ src/editor/cursor.rs | 26 +++++----- src/editor/style.rs | 27 +++++----- src/renderer/animation_utils.rs | 17 ++++--- src/renderer/cursor_renderer/mod.rs | 77 +++++++++++++++-------------- src/renderer/grid_renderer.rs | 28 ++++++----- src/renderer/mod.rs | 23 +++++---- src/renderer/profiler.rs | 2 +- src/renderer/rendered_window.rs | 53 ++++++++++---------- src/window/mod.rs | 48 +++++++++++------- src/window/mouse_manager.rs | 26 +++++----- 14 files changed, 202 insertions(+), 157 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f53d1a6c9..b05296c37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3049,6 +3049,7 @@ dependencies = [ "raw-window-handle", "redox_syscall 0.3.5", "sctk-adwaita", + "serde", "smithay-client-toolkit", "unicode-segmentation", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index 3805c4cf7..85e96254c 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ tokio-util = { version = "0.7.4", features = ["compat"] } tracy-client-sys = { version = "0.19.0", optional = true } unicode-segmentation = "1.9.0" which = "4.2.5" -winit = { git = "https://github.com/neovide/winit", branch = "new-keyboard-v3" } +winit = { git = "https://github.com/neovide/winit", branch = "new-keyboard-v3", features = ["serde"]} xdg = "2.4.1" wgpu = "0.14.2" skia-safe = "0.60.0" diff --git a/src/bridge/events.rs b/src/bridge/events.rs index cbcadd9f8..9b15459c3 100644 --- a/src/bridge/events.rs +++ b/src/bridge/events.rs @@ -4,9 +4,9 @@ use std::{ fmt::{self, Debug}, }; +use csscolorparser::Color; use log::debug; use rmpv::Value; -use skia_safe::Color4f; use crate::editor::{Colors, CursorMode, CursorShape, Style, UnderlineStyle}; @@ -276,17 +276,12 @@ pub enum RedrawEvent { }, } -fn unpack_color(packed_color: u64) -> Color4f { +fn unpack_color(packed_color: u64) -> Color { let packed_color = packed_color as u32; let r = ((packed_color & 0x00ff_0000) >> 16) as f32; let g = ((packed_color & 0xff00) >> 8) as f32; let b = (packed_color & 0xff) as f32; - Color4f { - r: r / 255.0, - g: g / 255.0, - b: b / 255.0, - a: 1.0, - } + Color::from([r, g, b]) } fn extract_values(values: Vec) -> Result<[Value; REQ]> { diff --git a/src/dimensions.rs b/src/dimensions.rs index 3c161cb80..9122f55a1 100644 --- a/src/dimensions.rs +++ b/src/dimensions.rs @@ -9,6 +9,8 @@ use winit::dpi::PhysicalSize; use crate::settings; +use euclid::default::Size2D; + // Maybe this should be independent from serialization? #[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)] pub struct Dimensions { @@ -83,6 +85,22 @@ impl From for PhysicalSize { } } +macro_rules! impl_from_dimensions_to_size2d { + ($type:ty) => { + impl From for Size2D<$type> { + fn from(dimensions: Dimensions) -> Size2D<$type> { + Size2D::new(dimensions.width as $type, dimensions.height as $type) + } + } + }; +} + +impl_from_dimensions_to_size2d!(u64); +impl_from_dimensions_to_size2d!(u32); +impl_from_dimensions_to_size2d!(i32); +impl_from_dimensions_to_size2d!(f32); +impl_from_dimensions_to_size2d!(f64); + impl Mul for Dimensions { type Output = Self; diff --git a/src/editor/cursor.rs b/src/editor/cursor.rs index a5bbfa797..d347fa759 100644 --- a/src/editor/cursor.rs +++ b/src/editor/cursor.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, sync::Arc}; -use skia_safe::Color4f; +use csscolorparser::Color; use crate::editor::style::{Colors, Style}; @@ -66,18 +66,18 @@ impl Cursor { } } - pub fn foreground(&self, default_colors: &Colors) -> Color4f { + pub fn foreground(&self, default_colors: &Colors) -> Color { self.style .as_ref() - .and_then(|s| s.colors.foreground) - .unwrap_or_else(|| default_colors.background.unwrap()) + .and_then(|s| s.colors.foreground.clone()) + .unwrap_or_else(|| default_colors.background.clone().unwrap()) } - pub fn background(&self, default_colors: &Colors) -> Color4f { + pub fn background(&self, default_colors: &Colors) -> Color { self.style .as_ref() - .and_then(|s| s.colors.background) - .unwrap_or_else(|| default_colors.foreground.unwrap()) + .and_then(|s| s.colors.background.clone()) + .unwrap_or_else(|| default_colors.foreground.clone().unwrap()) } pub fn alpha(&self) -> u8 { @@ -118,15 +118,15 @@ mod tests { use super::*; const COLORS: Colors = Colors { - foreground: Some(Color4f::new(0.1, 0.1, 0.1, 0.1)), - background: Some(Color4f::new(0.2, 0.1, 0.1, 0.1)), - special: Some(Color4f::new(0.3, 0.1, 0.1, 0.1)), + foreground: Some(Color::new(0.1, 0.1, 0.1, 0.1)), + background: Some(Color::new(0.2, 0.1, 0.1, 0.1)), + special: Some(Color::new(0.3, 0.1, 0.1, 0.1)), }; const DEFAULT_COLORS: Colors = Colors { - foreground: Some(Color4f::new(0.1, 0.2, 0.1, 0.1)), - background: Some(Color4f::new(0.2, 0.2, 0.1, 0.1)), - special: Some(Color4f::new(0.3, 0.2, 0.1, 0.1)), + foreground: Some(Color::new(0.1, 0.2, 0.1, 0.1)), + background: Some(Color::new(0.2, 0.2, 0.1, 0.1)), + special: Some(Color::new(0.3, 0.2, 0.1, 0.1)), }; const NONE_COLORS: Colors = Colors { diff --git a/src/editor/style.rs b/src/editor/style.rs index f712cdfdb..56f2fd722 100644 --- a/src/editor/style.rs +++ b/src/editor/style.rs @@ -1,10 +1,10 @@ -use skia_safe::Color4f; +use csscolorparser::Color; #[derive(new, Debug, Clone, PartialEq)] pub struct Colors { - pub foreground: Option, - pub background: Option, - pub special: Option, + pub foreground: Option, + pub background: Option, + pub special: Option, } #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -34,33 +34,38 @@ pub struct Style { } impl Style { - pub fn foreground(&self, default_colors: &Colors) -> Color4f { + pub fn foreground(&self, default_colors: &Colors) -> Color { if self.reverse { self.colors .background - .unwrap_or_else(|| default_colors.background.unwrap()) + .clone() + .unwrap_or_else(|| default_colors.background.clone().unwrap()) } else { self.colors .foreground - .unwrap_or_else(|| default_colors.foreground.unwrap()) + .clone() + .unwrap_or_else(|| default_colors.foreground.clone().unwrap()) } } - pub fn background(&self, default_colors: &Colors) -> Color4f { + pub fn background(&self, default_colors: &Colors) -> Color { if self.reverse { self.colors .foreground - .unwrap_or_else(|| default_colors.foreground.unwrap()) + .clone() + .unwrap_or_else(|| default_colors.foreground.clone().unwrap()) } else { self.colors .background - .unwrap_or_else(|| default_colors.background.unwrap()) + .clone() + .unwrap_or_else(|| default_colors.background.clone().unwrap()) } } - pub fn special(&self, default_colors: &Colors) -> Color4f { + pub fn special(&self, default_colors: &Colors) -> Color { self.colors .special + .clone() .unwrap_or_else(|| self.foreground(default_colors)) } } diff --git a/src/renderer/animation_utils.rs b/src/renderer/animation_utils.rs index e884490e3..b44758c28 100644 --- a/src/renderer/animation_utils.rs +++ b/src/renderer/animation_utils.rs @@ -1,4 +1,4 @@ -use skia_safe::Point; +use euclid::default::Point2D; #[allow(dead_code)] pub fn ease_linear(t: f32) -> f32 { @@ -73,11 +73,16 @@ pub fn ease(ease_func: fn(f32) -> f32, start: f32, end: f32, t: f32) -> f32 { lerp(start, end, ease_func(t)) } -pub fn ease_point(ease_func: fn(f32) -> f32, start: Point, end: Point, t: f32) -> Point { - Point { - x: ease(ease_func, start.x, end.x, t), - y: ease(ease_func, start.y, end.y, t), - } +pub fn ease_point( + ease_func: fn(f32) -> f32, + start: Point2D, + end: Point2D, + t: f32, +) -> Point2D { + Point2D::new( + ease(ease_func, start.x, end.x, t), + ease(ease_func, start.y, end.y, t), + ) } #[cfg(test)] diff --git a/src/renderer/cursor_renderer/mod.rs b/src/renderer/cursor_renderer/mod.rs index f849edc21..6e18e13c3 100644 --- a/src/renderer/cursor_renderer/mod.rs +++ b/src/renderer/cursor_renderer/mod.rs @@ -1,9 +1,13 @@ mod blink; -mod cursor_vfx; +//mod cursor_vfx; use std::collections::HashMap; -use skia_safe::{op, Canvas, Paint, Path, Point}; +//use skia_safe::{op, Canvas, Paint, Path, Point}; +use euclid::{ + approxeq::ApproxEq, + default::{Point2D, Vector2D}, +}; use winit::event::{Event, WindowEvent}; use crate::{ @@ -34,7 +38,7 @@ pub struct CursorSettings { trail_size: f32, unfocused_outline_width: f32, - vfx_mode: cursor_vfx::VfxMode, + //vfx_mode: cursor_vfx::VfxMode, vfx_opacity: f32, vfx_particle_lifetime: f32, vfx_particle_density: f32, @@ -53,7 +57,7 @@ impl Default for CursorSettings { animate_command_line: true, trail_size: 0.7, unfocused_outline_width: 1.0 / 8.0, - vfx_mode: cursor_vfx::VfxMode::Disabled, + //vfx_mode: cursor_vfx::VfxMode::Disabled, vfx_opacity: 200.0, vfx_particle_lifetime: 1.2, vfx_particle_density: 7.0, @@ -66,10 +70,10 @@ impl Default for CursorSettings { #[derive(Debug, Clone)] pub struct Corner { - start_position: Point, - current_position: Point, - relative_position: Point, - previous_destination: Point, + start_position: Point2D, + current_position: Point2D, + relative_position: Point2D, + previous_destination: Point2D, length_multiplier: f32, t: f32, } @@ -77,10 +81,10 @@ pub struct Corner { impl Corner { pub fn new() -> Corner { Corner { - start_position: Point::new(0.0, 0.0), - current_position: Point::new(0.0, 0.0), - relative_position: Point::new(0.0, 0.0), - previous_destination: Point::new(-1000.0, -1000.0), + start_position: Point2D::new(0.0, 0.0), + current_position: Point2D::new(0.0, 0.0), + relative_position: Point2D::new(0.0, 0.0), + previous_destination: Point2D::new(-1000.0, -1000.0), length_multiplier: 1.0, t: 0.0, } @@ -89,8 +93,8 @@ impl Corner { pub fn update( &mut self, settings: &CursorSettings, - font_dimensions: Point, - destination: Point, + font_dimensions: Point2D, + destination: Point2D, dt: f32, immediate_movement: bool, ) -> bool { @@ -114,7 +118,7 @@ impl Corner { } // Calculate window-space destination for corner - let relative_scaled_position: Point = ( + let relative_scaled_position: Vector2D = ( self.relative_position.x * font_dimensions.x, self.relative_position.y * font_dimensions.y, ) @@ -131,17 +135,9 @@ impl Corner { // Calculate how much a corner will be lagging behind based on how much it's aligned // with the direction of motion. Corners in front will move faster than corners in the // back - let travel_direction = { - let mut d = destination - self.current_position; - d.normalize(); - d - }; + let travel_direction = { (destination - self.current_position).normalize() }; - let corner_direction = { - let mut d = self.relative_position; - d.normalize(); - d - }; + let corner_direction = { self.relative_position.to_vector().normalize() }; let direction_alignment = travel_direction.dot(corner_direction); @@ -173,12 +169,12 @@ impl Corner { pub struct CursorRenderer { pub corners: Vec, cursor: Cursor, - destination: Point, + destination: Point2D, blink_status: BlinkStatus, previous_cursor_shape: Option, previous_editor_mode: EditorMode, - cursor_vfx: Option>, - previous_vfx_mode: cursor_vfx::VfxMode, + //cursor_vfx: Option>, + //previous_vfx_mode: cursor_vfx::VfxMode, window_has_focus: bool, } @@ -191,8 +187,8 @@ impl CursorRenderer { blink_status: BlinkStatus::new(), previous_cursor_shape: None, previous_editor_mode: EditorMode::Normal, - cursor_vfx: None, - previous_vfx_mode: cursor_vfx::VfxMode::Disabled, + //cursor_vfx: None, + //previous_vfx_mode: cursor_vfx::VfxMode::Disabled, window_has_focus: true, }; renderer.set_cursor_shape(&CursorShape::Block, DEFAULT_CELL_PERCENTAGE); @@ -272,6 +268,7 @@ impl CursorRenderer { } } + /* pub fn draw(&mut self, grid_renderer: &mut GridRenderer, canvas: &mut Canvas) { tracy_zone!("cursor_draw"); let render = self.blink_status.should_render(); @@ -332,7 +329,7 @@ impl CursorRenderer { if let Some(vfx) = self.cursor_vfx.as_ref() { vfx.render(&settings, canvas, grid_renderer, &self.cursor); } - } + }*/ pub fn animate( &mut self, @@ -344,21 +341,22 @@ impl CursorRenderer { self.blink_status.update_status(&self.cursor, dt); let settings = SETTINGS.get::(); + /* if settings.vfx_mode != self.previous_vfx_mode { self.cursor_vfx = cursor_vfx::new_cursor_vfx(&settings.vfx_mode); self.previous_vfx_mode = settings.vfx_mode.clone(); } + */ let mut cursor_width = grid_renderer.font_dimensions.width; if self.cursor.double_width && self.cursor.shape == CursorShape::Block { cursor_width *= 2; } - let cursor_dimensions: Point = ( + let cursor_dimensions = Vector2D::new( cursor_width as f32, grid_renderer.font_dimensions.height as f32, - ) - .into(); + ); let in_insert_mode = matches!(current_mode, EditorMode::Insert); @@ -377,21 +375,23 @@ impl CursorRenderer { .unwrap_or(DEFAULT_CELL_PERCENTAGE), ); + /* if let Some(vfx) = self.cursor_vfx.as_mut() { vfx.restart(center_destination); } + */ } let mut animating = false; - if !center_destination.is_zero() { + if !center_destination.approx_eq(&Point2D::origin()) { for corner in self.corners.iter_mut() { let immediate_movement = !settings.animate_in_insert_mode && in_insert_mode || !settings.animate_command_line && !changed_to_from_cmdline; let corner_animating = corner.update( &settings, - cursor_dimensions, + cursor_dimensions.to_point(), center_destination, dt, immediate_movement, @@ -400,6 +400,7 @@ impl CursorRenderer { animating |= corner_animating; } + /* let vfx_animating = if let Some(vfx) = self.cursor_vfx.as_mut() { vfx.update(&settings, center_destination, cursor_dimensions, dt) } else { @@ -407,6 +408,7 @@ impl CursorRenderer { }; animating |= vfx_animating; + */ } if !animating { @@ -414,7 +416,7 @@ impl CursorRenderer { } animating } - + /* fn draw_rectangle(&self, canvas: &mut Canvas, paint: &Paint) -> Path { // The cursor is made up of four points, so I create a path with each of the four // corners. @@ -464,4 +466,5 @@ impl CursorRenderer { canvas.draw_path(&path, paint); path } + */ } diff --git a/src/renderer/grid_renderer.rs b/src/renderer/grid_renderer.rs index 9fa748ce3..4f8ebe2c2 100644 --- a/src/renderer/grid_renderer.rs +++ b/src/renderer/grid_renderer.rs @@ -1,22 +1,26 @@ use std::sync::Arc; use log::trace; +/* use skia_safe::{ colors, dash_path_effect, BlendMode, Canvas, Color, Paint, Path, Point, Rect, HSV, }; +*/ +use csscolorparser::Color; +use euclid::{default::Rect, rect}; use winit::dpi::PhysicalSize; use crate::{ dimensions::Dimensions, editor::{Colors, Style, UnderlineStyle}, profiling::tracy_zone, - renderer::{CachingShaper, RendererSettings}, + renderer::{RendererSettings, CachingShaper}, settings::*, }; pub struct GridRenderer { pub shaper: CachingShaper, - pub paint: Paint, + //pub paint: Paint, pub default_style: Arc