Introduce a new `ToggleGraphicsProfiler` command (#7607)

Antonio Scandurra created

On macOS, this will enable or disable the Metal HUD at runtime. Note
that this only works when Zed is bundled because it requires to set the
`MetalHudEnabled` key in the Info.plist.

Release Notes:

- Added a new `ToggleGraphicsProfiler` command that can be used as an
action (or via the `Help -> Toggle Graphics Profiler` menu) to
investigate graphics performance.

Change summary

crates/gpui/src/platform.rs                    |  2 
crates/gpui/src/platform/linux/window.rs       |  4 ++
crates/gpui/src/platform/mac/metal_renderer.rs | 27 ++++++++++++++++---
crates/gpui/src/platform/mac/window.rs         |  6 ++++
crates/gpui/src/platform/test/window.rs        |  2 +
crates/gpui/src/window.rs                      | 10 +++++++
crates/workspace/src/workspace.rs              |  2 +
crates/zed/resources/info/Permissions.plist    |  2 +
crates/zed/src/app_menus.rs                    |  4 ++
9 files changed, 53 insertions(+), 6 deletions(-)

Detailed changes

crates/gpui/src/platform.rs 🔗

@@ -182,8 +182,8 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
     fn on_appearance_changed(&self, callback: Box<dyn FnMut()>);
     fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool;
     fn draw(&self, scene: &Scene);
-
     fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
+    fn set_graphics_profiler_enabled(&self, enabled: bool);
 
     #[cfg(any(test, feature = "test-support"))]
     fn as_test(&mut self) -> Option<&mut TestWindow> {

crates/gpui/src/platform/linux/window.rs 🔗

@@ -428,4 +428,8 @@ impl PlatformWindow for LinuxWindow {
         let inner = self.0.inner.lock();
         inner.renderer.atlas().clone()
     }
+
+    fn set_graphics_profiler_enabled(&self, enabled: bool) {
+        todo!("linux")
+    }
 }

crates/gpui/src/platform/mac/metal_renderer.rs 🔗

@@ -1,12 +1,12 @@
 use crate::{
-    point, size, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, ContentMask, DevicePixels,
-    Hsla, MetalAtlas, MonochromeSprite, Path, PathId, PathVertex, PolychromeSprite, PrimitiveBatch,
-    Quad, ScaledPixels, Scene, Shadow, Size, Surface, Underline,
+    platform::mac::ns_string, point, size, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds,
+    ContentMask, DevicePixels, Hsla, MetalAtlas, MonochromeSprite, Path, PathId, PathVertex,
+    PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size, Surface, Underline,
 };
 use block::ConcreteBlock;
 use cocoa::{
-    base::{NO, YES},
-    foundation::NSUInteger,
+    base::{nil, NO, YES},
+    foundation::{NSDictionary, NSUInteger},
     quartzcore::AutoresizingMask,
 };
 use collections::HashMap;
@@ -200,6 +200,23 @@ impl MetalRenderer {
         &self.sprite_atlas
     }
 
+    /// Enables or disables the Metal HUD for debugging purposes. Note that this only works
+    /// when the app is bundled and it has the `MetalHudEnabled` key set to true in Info.plist.
+    pub fn set_hud_enabled(&mut self, enabled: bool) {
+        unsafe {
+            if enabled {
+                let hud_properties = NSDictionary::dictionaryWithObject_forKey_(
+                    nil,
+                    ns_string("default"),
+                    ns_string("mode"),
+                );
+                let _: () = msg_send![&*self.layer, setDeveloperHUDProperties: hud_properties];
+            } else {
+                let _: () = msg_send![&*self.layer, setDeveloperHUDProperties: NSDictionary::dictionary(nil)];
+            }
+        }
+    }
+
     pub fn set_presents_with_transaction(&mut self, presents_with_transaction: bool) {
         self.presents_with_transaction = presents_with_transaction;
         self.layer

crates/gpui/src/platform/mac/window.rs 🔗

@@ -1021,6 +1021,12 @@ impl PlatformWindow for MacWindow {
     fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
         self.0.lock().renderer.sprite_atlas().clone()
     }
+
+    /// Enables or disables the Metal HUD for debugging purposes. Note that this only works
+    /// when the app is bundled and it has the `MetalHudEnabled` key set to true in Info.plist.
+    fn set_graphics_profiler_enabled(&self, enabled: bool) {
+        self.0.lock().renderer.set_hud_enabled(enabled);
+    }
 }
 
 impl HasWindowHandle for MacWindow {

crates/gpui/src/platform/test/window.rs 🔗

@@ -288,6 +288,8 @@ impl PlatformWindow for TestWindow {
         self.0.lock().sprite_atlas.clone()
     }
 
+    fn set_graphics_profiler_enabled(&self, _enabled: bool) {}
+
     fn as_test(&mut self) -> Option<&mut TestWindow> {
         Some(self)
     }

crates/gpui/src/window.rs 🔗

@@ -279,6 +279,7 @@ pub struct Window {
     pub(crate) focus: Option<FocusId>,
     focus_enabled: bool,
     pending_input: Option<PendingInput>,
+    graphics_profiler_enabled: bool,
 }
 
 #[derive(Default, Debug)]
@@ -462,6 +463,7 @@ impl Window {
             focus: None,
             focus_enabled: true,
             pending_input: None,
+            graphics_profiler_enabled: false,
         }
     }
     fn new_focus_listener(
@@ -1461,6 +1463,14 @@ impl<'a> WindowContext<'a> {
         }
     }
 
+    /// Toggle the graphics profiler to debug your application's rendering performance.
+    pub fn toggle_graphics_profiler(&mut self) {
+        self.window.graphics_profiler_enabled = !self.window.graphics_profiler_enabled;
+        self.window
+            .platform_window
+            .set_graphics_profiler_enabled(self.window.graphics_profiler_enabled);
+    }
+
     /// Register the given handler to be invoked whenever the global of the given type
     /// is updated.
     pub fn observe_global<G: Global>(

crates/workspace/src/workspace.rs 🔗

@@ -118,6 +118,7 @@ actions!(
         ToggleRightDock,
         ToggleBottomDock,
         CloseAllDocks,
+        ToggleGraphicsProfiler,
     ]
 );
 
@@ -3395,6 +3396,7 @@ impl Workspace {
                     workspace.reopen_closed_item(cx).detach();
                 }),
             )
+            .on_action(|_: &ToggleGraphicsProfiler, cx| cx.toggle_graphics_profiler())
     }
 
     #[cfg(any(test, feature = "test-support"))]

crates/zed/resources/info/Permissions.plist 🔗

@@ -22,3 +22,5 @@
 <string>An application in Zed wants to use speech recognition.</string>
 <key>NSRemindersUsageDescription</key>
 <string>An application in Zed wants to use your reminders.</string>
+<key>MetalHudEnabled</key>
+<true />

crates/zed/src/app_menus.rs 🔗

@@ -155,6 +155,10 @@ pub fn app_menus() -> Vec<Menu<'static>> {
                 MenuItem::action("View Telemetry", crate::OpenTelemetryLog),
                 MenuItem::action("View Dependency Licenses", crate::OpenLicenses),
                 MenuItem::action("Show Welcome", workspace::Welcome),
+                MenuItem::action(
+                    "Toggle Graphics Profiler",
+                    workspace::ToggleGraphicsProfiler,
+                ),
                 MenuItem::separator(),
                 MenuItem::separator(),
                 MenuItem::action(