linux: Better GPU debugging (#14706)

Release Notes:

- linux: Added GPU information to `editor: Copy System Specs to
Clipboard`
- linux: Show a prominant warning before running under llvmpipe and
similar.
This commit is contained in:
Conrad Irwin
2024-07-23 09:56:45 -06:00
committed by GitHub
parent c262c81e52
commit bdf1d4edea
16 changed files with 136 additions and 48 deletions
+19 -2
View File
@@ -1,10 +1,11 @@
use client::telemetry;
use gpui::{AppContext, Task};
use gpui::Task;
use human_bytes::human_bytes;
use release_channel::{AppCommitSha, AppVersion, ReleaseChannel};
use serde::Serialize;
use std::{env, fmt::Display};
use sysinfo::{MemoryRefreshKind, RefreshKind, System};
use ui::WindowContext;
#[derive(Clone, Debug, Serialize)]
pub struct SystemSpecs {
@@ -15,10 +16,11 @@ pub struct SystemSpecs {
memory: u64,
architecture: &'static str,
commit_sha: Option<String>,
gpu_specs: Option<String>,
}
impl SystemSpecs {
pub fn new(cx: &AppContext) -> Task<Self> {
pub fn new(cx: &WindowContext) -> Task<Self> {
let app_version = AppVersion::global(cx).to_string();
let release_channel = ReleaseChannel::global(cx);
let os_name = telemetry::os_name();
@@ -34,6 +36,15 @@ impl SystemSpecs {
_ => None,
};
let gpu_specs = if let Some(specs) = cx.gpu_specs() {
Some(format!(
"{} || {} || {}",
specs.device_name, specs.driver_name, specs.driver_info
))
} else {
None
};
cx.background_executor().spawn(async move {
let os_version = telemetry::os_version();
SystemSpecs {
@@ -44,6 +55,7 @@ impl SystemSpecs {
memory,
architecture,
commit_sha,
gpu_specs,
}
})
}
@@ -67,6 +79,11 @@ impl Display for SystemSpecs {
format!("Architecture: {}", self.architecture),
]
.into_iter()
.chain(
self.gpu_specs
.as_ref()
.map(|specs| format!("GPU: {}", specs)),
)
.collect::<Vec<String>>()
.join("\n");
+1 -1
View File
@@ -98,7 +98,7 @@ core-text = "20.1"
foreign-types = "0.5"
log.workspace = true
media.workspace = true
metal = "0.25"
metal = "0.29"
objc = "0.2"
[target.'cfg(any(target_os = "linux", target_os = "windows"))'.dependencies]
+13
View File
@@ -341,3 +341,16 @@ impl<T> Flatten<T> for Result<T> {
self
}
}
#[derive(Default, Debug)]
/// Information about the GPU GPUI is running on
pub struct GPUSpecs {
/// true if the GPU is really a fake (like llvmpipe) running on the CPU
pub is_software_emulated: bool,
/// Name of the device as reported by vulkan
pub device_name: String,
/// Name of the driver as reported by vulkan
pub driver_name: String,
/// Further driver info as reported by vulkan
pub driver_info: String,
}
+3 -2
View File
@@ -24,8 +24,8 @@ mod windows;
use crate::{
point, Action, AnyWindowHandle, AsyncWindowContext, BackgroundExecutor, Bounds, DevicePixels,
DispatchEventResult, Font, FontId, FontMetrics, FontRun, ForegroundExecutor, GlyphId, Keymap,
LineLayout, Pixels, PlatformInput, Point, RenderGlyphParams, RenderImageParams,
DispatchEventResult, Font, FontId, FontMetrics, FontRun, ForegroundExecutor, GPUSpecs, GlyphId,
Keymap, LineLayout, Pixels, PlatformInput, Point, RenderGlyphParams, RenderImageParams,
RenderSvgParams, Scene, SharedString, Size, Task, TaskLabel, WindowContext,
DEFAULT_WINDOW_SIZE,
};
@@ -366,6 +366,7 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
}
}
fn set_client_inset(&self, _inset: Pixels) {}
fn gpu_specs(&self) -> Option<GPUSpecs>;
#[cfg(any(test, feature = "test-support"))]
fn as_test(&mut self) -> Option<&mut TestWindow> {
@@ -3,9 +3,9 @@
use super::{BladeAtlas, PATH_TEXTURE_FORMAT};
use crate::{
AtlasTextureKind, AtlasTile, Bounds, ContentMask, DevicePixels, Hsla, MonochromeSprite, Path,
PathId, PathVertex, PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size,
Underline,
AtlasTextureKind, AtlasTile, Bounds, ContentMask, DevicePixels, GPUSpecs, Hsla,
MonochromeSprite, Path, PathId, PathVertex, PolychromeSprite, PrimitiveBatch, Quad,
ScaledPixels, Scene, Shadow, Size, Underline,
};
use bytemuck::{Pod, Zeroable};
use collections::HashMap;
@@ -451,6 +451,18 @@ impl BladeRenderer {
&self.atlas
}
#[cfg_attr(target_os = "macos", allow(dead_code))]
pub fn gpu_specs(&self) -> GPUSpecs {
let info = self.gpu.device_information();
GPUSpecs {
is_software_emulated: info.is_software_emulated,
device_name: info.device_name.clone(),
driver_name: info.driver_name.clone(),
driver_info: info.driver_info.clone(),
}
}
#[cfg(target_os = "macos")]
pub fn layer(&self) -> metal::MetalLayer {
self.gpu.metal_layer().unwrap()
@@ -25,7 +25,7 @@ use crate::platform::linux::wayland::serial::SerialKind;
use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow};
use crate::scene::Scene;
use crate::{
px, size, AnyWindowHandle, Bounds, Decorations, Globals, Modifiers, Output, Pixels,
px, size, AnyWindowHandle, Bounds, Decorations, GPUSpecs, Globals, Modifiers, Output, Pixels,
PlatformDisplay, PlatformInput, Point, PromptLevel, ResizeEdge, Size, Tiling,
WaylandClientStatePtr, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
WindowControls, WindowDecorations, WindowParams,
@@ -1007,6 +1007,10 @@ impl PlatformWindow for WaylandWindow {
update_window(state);
}
}
fn gpu_specs(&self) -> Option<GPUSpecs> {
self.borrow().renderer.gpu_specs().into()
}
}
fn update_window(mut state: RefMut<WaylandWindowState>) {
+7 -3
View File
@@ -2,9 +2,9 @@ use anyhow::Context;
use crate::{
platform::blade::{BladeRenderer, BladeSurfaceConfig},
px, size, AnyWindowHandle, Bounds, Decorations, DevicePixels, ForegroundExecutor, Modifiers,
Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow,
Point, PromptLevel, ResizeEdge, Scene, Size, Tiling, WindowAppearance,
px, size, AnyWindowHandle, Bounds, Decorations, DevicePixels, ForegroundExecutor, GPUSpecs,
Modifiers, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler,
PlatformWindow, Point, PromptLevel, ResizeEdge, Scene, Size, Tiling, WindowAppearance,
WindowBackgroundAppearance, WindowBounds, WindowDecorations, WindowKind, WindowParams,
X11ClientStatePtr,
};
@@ -1385,4 +1385,8 @@ impl PlatformWindow for X11Window {
appearance_changed();
}
}
fn gpu_specs(&self) -> Option<GPUSpecs> {
self.0.state.borrow().renderer.gpu_specs().into()
}
}
+4
View File
@@ -1096,6 +1096,10 @@ impl PlatformWindow for MacWindow {
fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
self.0.lock().renderer.sprite_atlas().clone()
}
fn gpu_specs(&self) -> Option<crate::GPUSpecs> {
None
}
}
impl rwh::HasWindowHandle for MacWindow {
+7 -3
View File
@@ -1,7 +1,7 @@
use crate::{
AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, DispatchEventResult, Pixels,
PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
Size, TestPlatform, TileId, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, DispatchEventResult, GPUSpecs,
Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow,
Point, Size, TestPlatform, TileId, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
WindowParams,
};
use collections::HashMap;
@@ -273,6 +273,10 @@ impl PlatformWindow for TestWindow {
fn start_window_move(&self) {
unimplemented!()
}
fn gpu_specs(&self) -> Option<GPUSpecs> {
None
}
}
pub(crate) struct TestAtlasState {
@@ -654,6 +654,10 @@ impl PlatformWindow for WindowsWindow {
fn get_raw_handle(&self) -> HWND {
self.0.hwnd
}
fn gpu_specs(&self) -> Option<GPUSpecs> {
Some(self.0.state.borrow().renderer.gpu_specs())
}
}
#[implement(IDropTarget)]
+7 -1
View File
@@ -3,7 +3,7 @@ use crate::{
AnyView, AppContext, Arena, Asset, AsyncWindowContext, AvailableSpace, Bounds, BoxShadow,
Context, Corners, CursorStyle, Decorations, DevicePixels, DispatchActionListener,
DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter,
FileDropEvent, Flatten, FontId, Global, GlobalElementId, GlyphId, Hsla, ImageData,
FileDropEvent, Flatten, FontId, GPUSpecs, Global, GlobalElementId, GlyphId, Hsla, ImageData,
InputHandler, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke,
KeystrokeEvent, LayoutId, LineLayoutIndex, Model, ModelContext, Modifiers,
ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent,
@@ -3733,6 +3733,12 @@ impl<'a> WindowContext<'a> {
.dispatch_tree
.on_action(action_type, Rc::new(listener));
}
/// Read information about the GPU backing this window.
/// Currently returns None on Mac and Windows.
pub fn gpu_specs(&self) -> Option<GPUSpecs> {
self.window.platform_window.gpu_specs()
}
}
#[cfg(target_os = "windows")]
+1 -1
View File
@@ -18,7 +18,7 @@ anyhow.workspace = true
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation.workspace = true
foreign-types = "0.5"
metal = "0.25"
metal = "0.29"
objc = "0.2"
[build-dependencies]
+24
View File
@@ -139,6 +139,30 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
})
.detach();
if let Some(specs) = cx.gpu_specs() {
log::info!("Using GPU: {:?}", specs);
if specs.is_software_emulated && std::env::var("ZED_ALLOW_EMULATED_GPU").is_err() {
let message = format!(db::indoc!{r#"
Zed uses Vulkan for rendering and requires a compatible GPU.
Currently you are using a software emulated GPU ({}) which
will result in awful performance.
For troubleshooting see: https://zed.dev/docs/linux
"#}, specs.device_name);
let prompt = cx.prompt(PromptLevel::Critical, "Unsupported GPU", Some(&message),
&["Troubleshoot and Quit"]);
cx.spawn(|_, mut cx| async move {
if prompt.await == Ok(0) {
cx.update(|cx| {
cx.open_url("https://zed.dev/docs/linux#zed-fails-to-open-windows");
cx.quit();
}).ok();
}
}).detach()
}
}
let inline_completion_button = cx.new_view(|cx| {
inline_completion_button::InlineCompletionButton::new(app_state.fs.clone(), cx)
});