diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 8d6f70f27e..9f92445045 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -652,20 +652,27 @@ impl AppContext { } } } else { + for window in self.windows.values() { + if let Some(window) = window.as_ref() { + if window.dirty { + window.platform_window.invalidate(); + } + } + } + #[cfg(any(test, feature = "test-support"))] for window in self .windows .values() .filter_map(|window| { let window = window.as_ref()?; - window.dirty.get().then_some(window.handle) + (window.dirty || window.focus_invalidated).then_some(window.handle) }) .collect::>() { self.update_window(window, |_, cx| cx.draw()).unwrap(); } - #[allow(clippy::collapsible_else_if)] if self.pending_effects.is_empty() { break; } @@ -742,7 +749,7 @@ impl AppContext { fn apply_refresh_effect(&mut self) { for window in self.windows.values_mut() { if let Some(window) = window.as_mut() { - window.dirty.set(true); + window.dirty = true; } } } diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 62ac543319..df886fc4d6 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -175,6 +175,7 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle { fn on_close(&self, callback: Box); fn on_appearance_changed(&self, callback: Box); fn is_topmost_for_position(&self, position: Point) -> bool; + fn invalidate(&self); fn draw(&self, scene: &Scene); fn sprite_atlas(&self) -> Arc; diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index bb8c08a879..ac3ce4c575 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -16,8 +16,8 @@ use cocoa::{ }, base::{id, nil}, foundation::{ - NSArray, NSAutoreleasePool, NSDefaultRunLoopMode, NSDictionary, NSFastEnumeration, - NSInteger, NSPoint, NSRect, NSSize, NSString, NSUInteger, + NSArray, NSAutoreleasePool, NSDictionary, NSFastEnumeration, NSInteger, NSPoint, NSRect, + NSSize, NSString, NSUInteger, }, }; use core_graphics::display::CGRect; @@ -321,7 +321,6 @@ struct MacWindowState { executor: ForegroundExecutor, native_window: id, native_view: NonNull, - display_link: id, renderer: MetalRenderer, kind: WindowKind, request_frame_callback: Option>, @@ -524,10 +523,6 @@ impl MacWindow { let native_view: id = msg_send![VIEW_CLASS, alloc]; let native_view = NSView::init(native_view); - let display_link: id = msg_send![class!(CADisplayLink), displayLinkWithTarget: native_view selector: sel!(displayLayer:)]; - let main_run_loop: id = msg_send![class!(NSRunLoop), mainRunLoop]; - let _: () = - msg_send![display_link, addToRunLoop: main_run_loop forMode: NSDefaultRunLoopMode]; assert!(!native_view.is_null()); let window = Self(Arc::new(Mutex::new(MacWindowState { @@ -535,7 +530,6 @@ impl MacWindow { executor, native_window, native_view: NonNull::new_unchecked(native_view as *mut _), - display_link, renderer: MetalRenderer::new(instance_buffer_pool), kind: options.kind, request_frame_callback: None, @@ -694,9 +688,6 @@ impl Drop for MacWindow { fn drop(&mut self) { let this = self.0.lock(); let window = this.native_window; - unsafe { - let _: () = msg_send![this.display_link, invalidate]; - } this.executor .spawn(async move { unsafe { @@ -1010,6 +1001,13 @@ impl PlatformWindow for MacWindow { } } + fn invalidate(&self) { + let this = self.0.lock(); + unsafe { + let _: () = msg_send![this.native_window.contentView(), setNeedsDisplay: YES]; + } + } + fn draw(&self, scene: &crate::Scene) { let mut this = self.0.lock(); this.renderer.draw(scene); diff --git a/crates/gpui/src/platform/test/window.rs b/crates/gpui/src/platform/test/window.rs index d56851c8b9..d5d62e0155 100644 --- a/crates/gpui/src/platform/test/window.rs +++ b/crates/gpui/src/platform/test/window.rs @@ -282,6 +282,8 @@ impl PlatformWindow for TestWindow { unimplemented!() } + fn invalidate(&self) {} + fn draw(&self, _scene: &crate::Scene) {} fn sprite_atlas(&self) -> sync::Arc { diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 19ed1c9099..8b10f5d8f4 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -22,7 +22,7 @@ use smallvec::SmallVec; use std::{ any::{Any, TypeId}, borrow::{Borrow, BorrowMut}, - cell::{Cell, RefCell}, + cell::RefCell, collections::hash_map::Entry, fmt::{Debug, Display}, future::Future, @@ -34,7 +34,7 @@ use std::{ atomic::{AtomicUsize, Ordering::SeqCst}, Arc, }, - time::{Duration, Instant}, + time::Duration, }; use util::{measure, ResultExt}; @@ -272,8 +272,7 @@ pub struct Window { appearance: WindowAppearance, appearance_observers: SubscriberSet<(), AnyObserver>, active: bool, - pub(crate) dirty: Rc>, - pub(crate) last_input_timestamp: Rc>, + pub(crate) dirty: bool, pub(crate) refreshing: bool, pub(crate) drawing: bool, activation_observers: SubscriberSet<(), AnyObserver>, @@ -340,28 +339,13 @@ impl Window { let bounds = platform_window.bounds(); let appearance = platform_window.appearance(); let text_system = Arc::new(WindowTextSystem::new(cx.text_system().clone())); - let dirty = Rc::new(Cell::new(false)); - let last_input_timestamp = Rc::new(Cell::new(Instant::now())); platform_window.on_request_frame(Box::new({ let mut cx = cx.to_async(); - let dirty = dirty.clone(); - let last_input_timestamp = last_input_timestamp.clone(); move || { - if dirty.get() { - measure("frame duration", || { - handle - .update(&mut cx, |_, cx| { - cx.draw(); - cx.present(); - }) - .log_err(); - }) - } else if last_input_timestamp.get().elapsed() < Duration::from_secs(2) { - // Keep presenting the current scene for 2 extra seconds since the - // last input to prevent the display from underclocking the refresh rate. - handle.update(&mut cx, |_, cx| cx.present()).log_err(); - } + measure("frame duration", || { + handle.update(&mut cx, |_, cx| cx.draw()).log_err(); + }) } })); platform_window.on_resize(Box::new({ @@ -440,8 +424,7 @@ impl Window { appearance, appearance_observers: SubscriberSet::new(), active: false, - dirty, - last_input_timestamp, + dirty: false, refreshing: false, drawing: false, activation_observers: SubscriberSet::new(), @@ -499,7 +482,7 @@ impl<'a> WindowContext<'a> { pub fn refresh(&mut self) { if !self.window.drawing { self.window.refreshing = true; - self.window.dirty.set(true); + self.window.dirty = true; } } @@ -967,10 +950,9 @@ impl<'a> WindowContext<'a> { &self.window.next_frame.z_index_stack } - /// Produces a new frame and assigns it to `rendered_frame`. To actually show - /// the contents of the new [Scene], use [present]. + /// Draw pixels to the display for this window based on the contents of its scene. pub(crate) fn draw(&mut self) { - self.window.dirty.set(false); + self.window.dirty = false; self.window.drawing = true; if let Some(requested_handler) = self.window.rendered_frame.requested_input_handler.as_mut() @@ -1106,19 +1088,16 @@ impl<'a> WindowContext<'a> { .clone() .retain(&(), |listener| listener(&event, self)); } + + self.window + .platform_window + .draw(&self.window.rendered_frame.scene); self.window.refreshing = false; self.window.drawing = false; } - fn present(&self) { - self.window - .platform_window - .draw(&self.window.rendered_frame.scene); - } - /// Dispatch a mouse or keyboard event on the window. pub fn dispatch_event(&mut self, event: PlatformInput) -> bool { - self.window.last_input_timestamp.set(Instant::now()); // Handlers may set this to false by calling `stop_propagation`. self.app.propagate_event = true; // Handlers may set this to true by calling `prevent_default`. @@ -2062,7 +2041,7 @@ impl<'a, V: 'static> ViewContext<'a, V> { } if !self.window.drawing { - self.window_cx.window.dirty.set(true); + self.window_cx.window.dirty = true; self.window_cx.app.push_effect(Effect::Notify { emitter: self.view.model.entity_id, });