Compare commits
9 Commits
fix-git-ht
...
v0.152.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21ab87dc64 | ||
|
|
67b49eca00 | ||
|
|
85a6dde65c | ||
|
|
1705af1684 | ||
|
|
08d8ab1121 | ||
|
|
9981f3614c | ||
|
|
1ff6ef3fa7 | ||
|
|
c304bf9a2c | ||
|
|
ac93366295 |
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -339,7 +339,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
bundle-linux-aarch64:
|
||||
bundle-linux-aarch64: # this runs on ubuntu22.04
|
||||
timeout-minutes: 60
|
||||
name: Create arm64 Linux bundle
|
||||
runs-on:
|
||||
@@ -360,8 +360,8 @@ jobs:
|
||||
- name: Set up Clang
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y llvm-10 clang-10 build-essential cmake pkg-config libasound2-dev libfontconfig-dev libwayland-dev libxkbcommon-x11-dev libssl-dev libsqlite3-dev libzstd-dev libvulkan1 libgit2-dev
|
||||
echo "/usr/lib/llvm-10/bin" >> $GITHUB_PATH
|
||||
sudo apt-get install -y llvm-15 clang-15 build-essential cmake pkg-config libasound2-dev libfontconfig-dev libwayland-dev libxkbcommon-x11-dev libssl-dev libsqlite3-dev libzstd-dev libvulkan1 libgit2-dev
|
||||
echo "/usr/lib/llvm-15/bin" >> $GITHUB_PATH
|
||||
|
||||
- uses: rui314/setup-mold@0bf4f07ef9048ec62a45f9dbf2f098afa49695f0 # v1
|
||||
with:
|
||||
|
||||
32
Cargo.lock
generated
32
Cargo.lock
generated
@@ -1661,8 +1661,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "blade-graphics"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/kvark/blade?rev=fee06c42f658b36dd9ac85444a9ee2a481383695#fee06c42f658b36dd9ac85444a9ee2a481383695"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/kvark/blade?rev=e142a3a5e678eb6a13e642ad8401b1f3aa38e969#e142a3a5e678eb6a13e642ad8401b1f3aa38e969"
|
||||
dependencies = [
|
||||
"ash",
|
||||
"ash-window",
|
||||
@@ -1691,8 +1691,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "blade-macros"
|
||||
version = "0.2.1"
|
||||
source = "git+https://github.com/kvark/blade?rev=fee06c42f658b36dd9ac85444a9ee2a481383695#fee06c42f658b36dd9ac85444a9ee2a481383695"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/kvark/blade?rev=e142a3a5e678eb6a13e642ad8401b1f3aa38e969#e142a3a5e678eb6a13e642ad8401b1f3aa38e969"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1702,7 +1702,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "blade-util"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/kvark/blade?rev=fee06c42f658b36dd9ac85444a9ee2a481383695#fee06c42f658b36dd9ac85444a9ee2a481383695"
|
||||
source = "git+https://github.com/kvark/blade?rev=e142a3a5e678eb6a13e642ad8401b1f3aa38e969#e142a3a5e678eb6a13e642ad8401b1f3aa38e969"
|
||||
dependencies = [
|
||||
"blade-graphics",
|
||||
"bytemuck",
|
||||
@@ -4887,9 +4887,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "glow"
|
||||
version = "0.13.1"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1"
|
||||
checksum = "f865cbd94bd355b89611211e49508da98a1fce0ad755c1e8448fb96711b24528"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"slotmap",
|
||||
@@ -7879,21 +7879,6 @@ version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "performance"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collections",
|
||||
"gpui",
|
||||
"log",
|
||||
"schemars",
|
||||
"serde",
|
||||
"settings",
|
||||
"util",
|
||||
"workspace",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "perplexity"
|
||||
version = "0.1.0"
|
||||
@@ -14212,7 +14197,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.152.0"
|
||||
version = "0.152.3"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"anyhow",
|
||||
@@ -14275,7 +14260,6 @@ dependencies = [
|
||||
"outline_panel",
|
||||
"parking_lot",
|
||||
"paths",
|
||||
"performance",
|
||||
"profiling",
|
||||
"project",
|
||||
"project_panel",
|
||||
|
||||
10
Cargo.toml
10
Cargo.toml
@@ -72,7 +72,6 @@ members = [
|
||||
"crates/outline",
|
||||
"crates/outline_panel",
|
||||
"crates/paths",
|
||||
"crates/performance",
|
||||
"crates/picker",
|
||||
"crates/prettier",
|
||||
"crates/project",
|
||||
@@ -167,7 +166,7 @@ members = [
|
||||
# Tooling
|
||||
#
|
||||
|
||||
"tooling/xtask"
|
||||
"tooling/xtask",
|
||||
]
|
||||
default-members = ["crates/zed"]
|
||||
|
||||
@@ -246,7 +245,6 @@ open_ai = { path = "crates/open_ai" }
|
||||
outline = { path = "crates/outline" }
|
||||
outline_panel = { path = "crates/outline_panel" }
|
||||
paths = { path = "crates/paths" }
|
||||
performance = { path = "crates/performance" }
|
||||
picker = { path = "crates/picker" }
|
||||
plugin = { path = "crates/plugin" }
|
||||
plugin_macros = { path = "crates/plugin_macros" }
|
||||
@@ -324,9 +322,9 @@ async-watch = "0.3.1"
|
||||
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
|
||||
base64 = "0.22"
|
||||
bitflags = "2.6.0"
|
||||
blade-graphics = { git = "https://github.com/kvark/blade", rev = "fee06c42f658b36dd9ac85444a9ee2a481383695" }
|
||||
blade-macros = { git = "https://github.com/kvark/blade", rev = "fee06c42f658b36dd9ac85444a9ee2a481383695" }
|
||||
blade-util = { git = "https://github.com/kvark/blade", rev = "fee06c42f658b36dd9ac85444a9ee2a481383695" }
|
||||
blade-graphics = { git = "https://github.com/kvark/blade", rev = "e142a3a5e678eb6a13e642ad8401b1f3aa38e969" }
|
||||
blade-macros = { git = "https://github.com/kvark/blade", rev = "e142a3a5e678eb6a13e642ad8401b1f3aa38e969" }
|
||||
blade-util = { git = "https://github.com/kvark/blade", rev = "e142a3a5e678eb6a13e642ad8401b1f3aa38e969" }
|
||||
cargo_metadata = "0.18"
|
||||
cargo_toml = "0.20"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
@@ -134,8 +134,11 @@ impl InlineAssistant {
|
||||
})
|
||||
.detach();
|
||||
|
||||
let workspace = workspace.clone();
|
||||
let workspace = workspace.downgrade();
|
||||
cx.observe_global::<SettingsStore>(move |cx| {
|
||||
let Some(workspace) = workspace.upgrade() else {
|
||||
return;
|
||||
};
|
||||
let Some(terminal_panel) = workspace.read(cx).panel::<TerminalPanel>(cx) else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
rc::{Rc, Weak},
|
||||
sync::{atomic::Ordering::SeqCst, Arc},
|
||||
time::{Duration, Instant},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
@@ -142,12 +142,6 @@ impl App {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a start time for tracking time to first window draw.
|
||||
pub fn measure_time_to_first_window_draw(self, start: Instant) -> Self {
|
||||
self.0.borrow_mut().time_to_first_window_draw = Some(TimeToFirstWindowDraw::Pending(start));
|
||||
self
|
||||
}
|
||||
|
||||
/// Start the application. The provided callback will be called once the
|
||||
/// app is fully launched.
|
||||
pub fn run<F>(self, on_finish_launching: F)
|
||||
@@ -253,7 +247,6 @@ pub struct AppContext {
|
||||
pub(crate) layout_id_buffer: Vec<LayoutId>, // We recycle this memory across layout requests.
|
||||
pub(crate) propagate_event: bool,
|
||||
pub(crate) prompt_builder: Option<PromptBuilder>,
|
||||
pub(crate) time_to_first_window_draw: Option<TimeToFirstWindowDraw>,
|
||||
}
|
||||
|
||||
impl AppContext {
|
||||
@@ -307,7 +300,6 @@ impl AppContext {
|
||||
layout_id_buffer: Default::default(),
|
||||
propagate_event: true,
|
||||
prompt_builder: Some(PromptBuilder::Default),
|
||||
time_to_first_window_draw: None,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1310,14 +1302,6 @@ impl AppContext {
|
||||
|
||||
(task, is_first)
|
||||
}
|
||||
|
||||
/// Returns the time to first window draw, if available.
|
||||
pub fn time_to_first_window_draw(&self) -> Option<Duration> {
|
||||
match self.time_to_first_window_draw {
|
||||
Some(TimeToFirstWindowDraw::Done(duration)) => Some(duration),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Context for AppContext {
|
||||
@@ -1481,15 +1465,6 @@ impl<G: Global> DerefMut for GlobalLease<G> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the initialization duration of the application.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum TimeToFirstWindowDraw {
|
||||
/// The application is still initializing, and contains the start time.
|
||||
Pending(Instant),
|
||||
/// The application has finished initializing, and contains the total duration.
|
||||
Done(Duration),
|
||||
}
|
||||
|
||||
/// Contains state associated with an active drag operation, started by dragging an element
|
||||
/// within the window or by dragging into the app from the underlying platform.
|
||||
pub struct AnyDrag {
|
||||
|
||||
@@ -16,7 +16,6 @@ mod blade;
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
mod test;
|
||||
|
||||
mod fps;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod windows;
|
||||
|
||||
@@ -52,7 +51,6 @@ use strum::EnumIter;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub use app_menu::*;
|
||||
pub use fps::*;
|
||||
pub use keystroke::*;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
@@ -356,7 +354,7 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
|
||||
fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>);
|
||||
fn on_close(&self, callback: Box<dyn FnOnce()>);
|
||||
fn on_appearance_changed(&self, callback: Box<dyn FnMut()>);
|
||||
fn draw(&self, scene: &Scene, on_complete: Option<oneshot::Sender<()>>);
|
||||
fn draw(&self, scene: &Scene);
|
||||
fn completed_frame(&self) {}
|
||||
fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
|
||||
|
||||
@@ -381,7 +379,6 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
|
||||
}
|
||||
fn set_client_inset(&self, _inset: Pixels) {}
|
||||
fn gpu_specs(&self) -> Option<GPUSpecs>;
|
||||
fn fps(&self) -> Option<f32>;
|
||||
|
||||
fn update_ime_position(&self, _bounds: Bounds<Pixels>);
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ use crate::{
|
||||
};
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use collections::HashMap;
|
||||
use futures::channel::oneshot;
|
||||
#[cfg(target_os = "macos")]
|
||||
use media::core_video::CVMetalTextureCache;
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -336,6 +335,17 @@ impl BladePipelines {
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn destroy(&mut self, gpu: &gpu::Context) {
|
||||
gpu.destroy_render_pipeline(&mut self.quads);
|
||||
gpu.destroy_render_pipeline(&mut self.shadows);
|
||||
gpu.destroy_render_pipeline(&mut self.path_rasterization);
|
||||
gpu.destroy_render_pipeline(&mut self.paths);
|
||||
gpu.destroy_render_pipeline(&mut self.underlines);
|
||||
gpu.destroy_render_pipeline(&mut self.mono_sprites);
|
||||
gpu.destroy_render_pipeline(&mut self.poly_sprites);
|
||||
gpu.destroy_render_pipeline(&mut self.surfaces);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BladeSurfaceConfig {
|
||||
@@ -438,6 +448,7 @@ impl BladeRenderer {
|
||||
self.wait_for_gpu();
|
||||
self.surface_config.transparent = transparent;
|
||||
let surface_info = self.gpu.resize(self.surface_config);
|
||||
self.pipelines.destroy(&self.gpu);
|
||||
self.pipelines = BladePipelines::new(&self.gpu, surface_info);
|
||||
self.alpha_mode = surface_info.alpha;
|
||||
}
|
||||
@@ -538,16 +549,13 @@ impl BladeRenderer {
|
||||
pub fn destroy(&mut self) {
|
||||
self.wait_for_gpu();
|
||||
self.atlas.destroy();
|
||||
self.gpu.destroy_sampler(self.atlas_sampler);
|
||||
self.instance_belt.destroy(&self.gpu);
|
||||
self.gpu.destroy_command_encoder(&mut self.command_encoder);
|
||||
self.pipelines.destroy(&self.gpu);
|
||||
}
|
||||
|
||||
pub fn draw(
|
||||
&mut self,
|
||||
scene: &Scene,
|
||||
// Required to compile on macOS, but not currently supported.
|
||||
_on_complete: Option<oneshot::Sender<()>>,
|
||||
) {
|
||||
pub fn draw(&mut self, scene: &Scene) {
|
||||
self.command_encoder.start();
|
||||
self.atlas.before_frame(&mut self.command_encoder);
|
||||
self.rasterize_paths(scene.paths());
|
||||
@@ -776,10 +784,4 @@ impl BladeRenderer {
|
||||
self.wait_for_gpu();
|
||||
self.last_sync_point = Some(sync_point);
|
||||
}
|
||||
|
||||
/// Required to compile on macOS, but not currently supported.
|
||||
#[cfg_attr(any(target_os = "linux", target_os = "windows"), allow(dead_code))]
|
||||
pub fn fps(&self) -> f32 {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
const NANOS_PER_SEC: u64 = 1_000_000_000;
|
||||
const WINDOW_SIZE: usize = 128;
|
||||
|
||||
/// Represents a rolling FPS (Frames Per Second) counter.
|
||||
///
|
||||
/// This struct provides a lock-free mechanism to measure and calculate FPS
|
||||
/// continuously, updating with every frame. It uses atomic operations to
|
||||
/// ensure thread-safety without the need for locks.
|
||||
pub struct FpsCounter {
|
||||
frame_times: [AtomicU64; WINDOW_SIZE],
|
||||
head: AtomicUsize,
|
||||
tail: AtomicUsize,
|
||||
}
|
||||
|
||||
impl FpsCounter {
|
||||
/// Creates a new `Fps` counter.
|
||||
///
|
||||
/// Returns an `Arc<Fps>` for safe sharing across threads.
|
||||
pub fn new() -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
frame_times: std::array::from_fn(|_| AtomicU64::new(0)),
|
||||
head: AtomicUsize::new(0),
|
||||
tail: AtomicUsize::new(0),
|
||||
})
|
||||
}
|
||||
|
||||
/// Increments the FPS counter with a new frame timestamp.
|
||||
///
|
||||
/// This method updates the internal state to maintain a rolling window
|
||||
/// of frame data for the last second. It uses atomic operations to
|
||||
/// ensure thread-safety.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `timestamp_ns` - The timestamp of the new frame in nanoseconds.
|
||||
pub fn increment(&self, timestamp_ns: u64) {
|
||||
let mut head = self.head.load(Ordering::Relaxed);
|
||||
let mut tail = self.tail.load(Ordering::Relaxed);
|
||||
|
||||
// Add new timestamp
|
||||
self.frame_times[head].store(timestamp_ns, Ordering::Relaxed);
|
||||
// Increment head and wrap around to 0 if it reaches WINDOW_SIZE
|
||||
head = (head + 1) % WINDOW_SIZE;
|
||||
self.head.store(head, Ordering::Relaxed);
|
||||
|
||||
// Remove old timestamps (older than 1 second)
|
||||
while tail != head {
|
||||
let oldest = self.frame_times[tail].load(Ordering::Relaxed);
|
||||
if timestamp_ns.wrapping_sub(oldest) <= NANOS_PER_SEC {
|
||||
break;
|
||||
}
|
||||
// Increment tail and wrap around to 0 if it reaches WINDOW_SIZE
|
||||
tail = (tail + 1) % WINDOW_SIZE;
|
||||
self.tail.store(tail, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates and returns the current FPS.
|
||||
///
|
||||
/// This method computes the FPS based on the frames recorded in the last second.
|
||||
/// It uses atomic loads to ensure thread-safety.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The calculated FPS as a `f32`, or 0.0 if no frames have been recorded.
|
||||
pub fn fps(&self) -> f32 {
|
||||
let head = self.head.load(Ordering::Relaxed);
|
||||
let tail = self.tail.load(Ordering::Relaxed);
|
||||
|
||||
if head == tail {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
let newest =
|
||||
self.frame_times[head.wrapping_sub(1) & (WINDOW_SIZE - 1)].load(Ordering::Relaxed);
|
||||
let oldest = self.frame_times[tail].load(Ordering::Relaxed);
|
||||
|
||||
let time_diff = newest.wrapping_sub(oldest) as f32;
|
||||
if time_diff == 0.0 {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
let frame_count = if head > tail {
|
||||
head - tail
|
||||
} else {
|
||||
WINDOW_SIZE - tail + head
|
||||
};
|
||||
|
||||
(frame_count as f32 - 1.0) * NANOS_PER_SEC as f32 / time_diff
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ use std::sync::Arc;
|
||||
|
||||
use blade_graphics as gpu;
|
||||
use collections::HashMap;
|
||||
use futures::channel::oneshot;
|
||||
use futures::channel::oneshot::Receiver;
|
||||
|
||||
use raw_window_handle as rwh;
|
||||
use wayland_backend::client::ObjectId;
|
||||
@@ -831,7 +831,7 @@ impl PlatformWindow for WaylandWindow {
|
||||
_msg: &str,
|
||||
_detail: Option<&str>,
|
||||
_answers: &[&str],
|
||||
) -> Option<oneshot::Receiver<usize>> {
|
||||
) -> Option<Receiver<usize>> {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -938,9 +938,9 @@ impl PlatformWindow for WaylandWindow {
|
||||
self.0.callbacks.borrow_mut().appearance_changed = Some(callback);
|
||||
}
|
||||
|
||||
fn draw(&self, scene: &Scene, on_complete: Option<oneshot::Sender<()>>) {
|
||||
fn draw(&self, scene: &Scene) {
|
||||
let mut state = self.borrow_mut();
|
||||
state.renderer.draw(scene, on_complete);
|
||||
state.renderer.draw(scene);
|
||||
}
|
||||
|
||||
fn completed_frame(&self) {
|
||||
@@ -1020,10 +1020,6 @@ impl PlatformWindow for WaylandWindow {
|
||||
fn gpu_specs(&self) -> Option<GPUSpecs> {
|
||||
self.borrow().renderer.gpu_specs().into()
|
||||
}
|
||||
|
||||
fn fps(&self) -> Option<f32> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn update_window(mut state: RefMut<WaylandWindowState>) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use anyhow::Context;
|
||||
|
||||
use crate::{
|
||||
platform::blade::{BladeRenderer, BladeSurfaceConfig},
|
||||
px, size, AnyWindowHandle, Bounds, Decorations, DevicePixels, ForegroundExecutor, GPUSpecs,
|
||||
@@ -7,9 +9,7 @@ use crate::{
|
||||
X11ClientStatePtr,
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
use blade_graphics as gpu;
|
||||
use futures::channel::oneshot;
|
||||
use raw_window_handle as rwh;
|
||||
use util::{maybe, ResultExt};
|
||||
use x11rb::{
|
||||
@@ -1210,10 +1210,9 @@ impl PlatformWindow for X11Window {
|
||||
self.0.callbacks.borrow_mut().appearance_changed = Some(callback);
|
||||
}
|
||||
|
||||
// TODO: on_complete not yet supported for X11 windows
|
||||
fn draw(&self, scene: &Scene, on_complete: Option<oneshot::Sender<()>>) {
|
||||
fn draw(&self, scene: &Scene) {
|
||||
let mut inner = self.0.state.borrow_mut();
|
||||
inner.renderer.draw(scene, on_complete);
|
||||
inner.renderer.draw(scene);
|
||||
}
|
||||
|
||||
fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
|
||||
@@ -1406,8 +1405,4 @@ impl PlatformWindow for X11Window {
|
||||
fn gpu_specs(&self) -> Option<GPUSpecs> {
|
||||
self.0.state.borrow().renderer.gpu_specs().into()
|
||||
}
|
||||
|
||||
fn fps(&self) -> Option<f32> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::metal_atlas::MetalAtlas;
|
||||
use crate::{
|
||||
point, size, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, ContentMask, DevicePixels,
|
||||
FpsCounter, Hsla, MonochromeSprite, PaintSurface, Path, PathId, PathVertex, PolychromeSprite,
|
||||
Hsla, MonochromeSprite, PaintSurface, Path, PathId, PathVertex, PolychromeSprite,
|
||||
PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size, Surface, Underline,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
@@ -14,7 +14,6 @@ use cocoa::{
|
||||
use collections::HashMap;
|
||||
use core_foundation::base::TCFType;
|
||||
use foreign_types::ForeignType;
|
||||
use futures::channel::oneshot;
|
||||
use media::core_video::CVMetalTextureCache;
|
||||
use metal::{CAMetalLayer, CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange};
|
||||
use objc::{self, msg_send, sel, sel_impl};
|
||||
@@ -106,7 +105,6 @@ pub(crate) struct MetalRenderer {
|
||||
instance_buffer_pool: Arc<Mutex<InstanceBufferPool>>,
|
||||
sprite_atlas: Arc<MetalAtlas>,
|
||||
core_video_texture_cache: CVMetalTextureCache,
|
||||
fps_counter: Arc<FpsCounter>,
|
||||
}
|
||||
|
||||
impl MetalRenderer {
|
||||
@@ -252,7 +250,6 @@ impl MetalRenderer {
|
||||
instance_buffer_pool,
|
||||
sprite_atlas,
|
||||
core_video_texture_cache,
|
||||
fps_counter: FpsCounter::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,8 +292,7 @@ impl MetalRenderer {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
pub fn draw(&mut self, scene: &Scene, on_complete: Option<oneshot::Sender<()>>) {
|
||||
let on_complete = Arc::new(Mutex::new(on_complete));
|
||||
pub fn draw(&mut self, scene: &Scene) {
|
||||
let layer = self.layer.clone();
|
||||
let viewport_size = layer.drawable_size();
|
||||
let viewport_size: Size<DevicePixels> = size(
|
||||
@@ -323,24 +319,13 @@ impl MetalRenderer {
|
||||
Ok(command_buffer) => {
|
||||
let instance_buffer_pool = self.instance_buffer_pool.clone();
|
||||
let instance_buffer = Cell::new(Some(instance_buffer));
|
||||
let device = self.device.clone();
|
||||
let fps_counter = self.fps_counter.clone();
|
||||
let completed_handler =
|
||||
ConcreteBlock::new(move |_: &metal::CommandBufferRef| {
|
||||
let mut cpu_timestamp = 0;
|
||||
let mut gpu_timestamp = 0;
|
||||
device.sample_timestamps(&mut cpu_timestamp, &mut gpu_timestamp);
|
||||
|
||||
fps_counter.increment(gpu_timestamp);
|
||||
if let Some(on_complete) = on_complete.lock().take() {
|
||||
on_complete.send(()).ok();
|
||||
}
|
||||
if let Some(instance_buffer) = instance_buffer.take() {
|
||||
instance_buffer_pool.lock().release(instance_buffer);
|
||||
}
|
||||
});
|
||||
let completed_handler = completed_handler.copy();
|
||||
command_buffer.add_completed_handler(&completed_handler);
|
||||
let block = ConcreteBlock::new(move |_| {
|
||||
if let Some(instance_buffer) = instance_buffer.take() {
|
||||
instance_buffer_pool.lock().release(instance_buffer);
|
||||
}
|
||||
});
|
||||
let block = block.copy();
|
||||
command_buffer.add_completed_handler(&block);
|
||||
|
||||
if self.presents_with_transaction {
|
||||
command_buffer.commit();
|
||||
@@ -1132,10 +1117,6 @@ impl MetalRenderer {
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn fps(&self) -> f32 {
|
||||
self.fps_counter.fps()
|
||||
}
|
||||
}
|
||||
|
||||
fn build_pipeline_state(
|
||||
|
||||
@@ -784,14 +784,14 @@ impl PlatformWindow for MacWindow {
|
||||
self.0.as_ref().lock().bounds()
|
||||
}
|
||||
|
||||
fn is_maximized(&self) -> bool {
|
||||
self.0.as_ref().lock().is_maximized()
|
||||
}
|
||||
|
||||
fn window_bounds(&self) -> WindowBounds {
|
||||
self.0.as_ref().lock().window_bounds()
|
||||
}
|
||||
|
||||
fn is_maximized(&self) -> bool {
|
||||
self.0.as_ref().lock().is_maximized()
|
||||
}
|
||||
|
||||
fn content_size(&self) -> Size<Pixels> {
|
||||
self.0.as_ref().lock().content_size()
|
||||
}
|
||||
@@ -975,6 +975,8 @@ impl PlatformWindow for MacWindow {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_app_id(&mut self, _app_id: &str) {}
|
||||
|
||||
fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) {
|
||||
let mut this = self.0.as_ref().lock();
|
||||
this.renderer
|
||||
@@ -1005,6 +1007,30 @@ impl PlatformWindow for MacWindow {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_edited(&mut self, edited: bool) {
|
||||
unsafe {
|
||||
let window = self.0.lock().native_window;
|
||||
msg_send![window, setDocumentEdited: edited as BOOL]
|
||||
}
|
||||
|
||||
// Changing the document edited state resets the traffic light position,
|
||||
// so we have to move it again.
|
||||
self.0.lock().move_traffic_light();
|
||||
}
|
||||
|
||||
fn show_character_palette(&self) {
|
||||
let this = self.0.lock();
|
||||
let window = this.native_window;
|
||||
this.executor
|
||||
.spawn(async move {
|
||||
unsafe {
|
||||
let app = NSApplication::sharedApplication(nil);
|
||||
let _: () = msg_send![app, orderFrontCharacterPalette: window];
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn minimize(&self) {
|
||||
let window = self.0.lock().native_window;
|
||||
unsafe {
|
||||
@@ -1081,41 +1107,15 @@ impl PlatformWindow for MacWindow {
|
||||
self.0.lock().appearance_changed_callback = Some(callback);
|
||||
}
|
||||
|
||||
fn draw(&self, scene: &crate::Scene, on_complete: Option<oneshot::Sender<()>>) {
|
||||
fn draw(&self, scene: &crate::Scene) {
|
||||
let mut this = self.0.lock();
|
||||
this.renderer.draw(scene, on_complete);
|
||||
this.renderer.draw(scene);
|
||||
}
|
||||
|
||||
fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
|
||||
self.0.lock().renderer.sprite_atlas().clone()
|
||||
}
|
||||
|
||||
fn set_edited(&mut self, edited: bool) {
|
||||
unsafe {
|
||||
let window = self.0.lock().native_window;
|
||||
msg_send![window, setDocumentEdited: edited as BOOL]
|
||||
}
|
||||
|
||||
// Changing the document edited state resets the traffic light position,
|
||||
// so we have to move it again.
|
||||
self.0.lock().move_traffic_light();
|
||||
}
|
||||
|
||||
fn show_character_palette(&self) {
|
||||
let this = self.0.lock();
|
||||
let window = this.native_window;
|
||||
this.executor
|
||||
.spawn(async move {
|
||||
unsafe {
|
||||
let app = NSApplication::sharedApplication(nil);
|
||||
let _: () = msg_send![app, orderFrontCharacterPalette: window];
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn set_app_id(&mut self, _app_id: &str) {}
|
||||
|
||||
fn gpu_specs(&self) -> Option<crate::GPUSpecs> {
|
||||
None
|
||||
}
|
||||
@@ -1126,10 +1126,6 @@ impl PlatformWindow for MacWindow {
|
||||
let _: () = msg_send![input_context, invalidateCharacterCoordinates];
|
||||
}
|
||||
}
|
||||
|
||||
fn fps(&self) -> Option<f32> {
|
||||
Some(self.0.lock().renderer.fps())
|
||||
}
|
||||
}
|
||||
|
||||
impl rwh::HasWindowHandle for MacWindow {
|
||||
|
||||
@@ -251,12 +251,7 @@ impl PlatformWindow for TestWindow {
|
||||
|
||||
fn on_appearance_changed(&self, _callback: Box<dyn FnMut()>) {}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
_scene: &crate::Scene,
|
||||
_on_complete: Option<futures::channel::oneshot::Sender<()>>,
|
||||
) {
|
||||
}
|
||||
fn draw(&self, _scene: &crate::Scene) {}
|
||||
|
||||
fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
|
||||
self.0.lock().sprite_atlas.clone()
|
||||
@@ -284,10 +279,6 @@ impl PlatformWindow for TestWindow {
|
||||
fn gpu_specs(&self) -> Option<GPUSpecs> {
|
||||
None
|
||||
}
|
||||
|
||||
fn fps(&self) -> Option<f32> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct TestAtlasState {
|
||||
|
||||
@@ -660,8 +660,8 @@ impl PlatformWindow for WindowsWindow {
|
||||
self.0.state.borrow_mut().callbacks.appearance_changed = Some(callback);
|
||||
}
|
||||
|
||||
fn draw(&self, scene: &Scene, on_complete: Option<oneshot::Sender<()>>) {
|
||||
self.0.state.borrow_mut().renderer.draw(scene, on_complete)
|
||||
fn draw(&self, scene: &Scene) {
|
||||
self.0.state.borrow_mut().renderer.draw(scene)
|
||||
}
|
||||
|
||||
fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
|
||||
@@ -679,10 +679,6 @@ impl PlatformWindow for WindowsWindow {
|
||||
fn update_ime_position(&self, _bounds: Bounds<Pixels>) {
|
||||
// todo(windows)
|
||||
}
|
||||
|
||||
fn fps(&self) -> Option<f32> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[implement(IDropTarget)]
|
||||
|
||||
@@ -11,9 +11,9 @@ use crate::{
|
||||
PromptLevel, Quad, Render, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams,
|
||||
Replay, ResizeEdge, ScaledPixels, Scene, Shadow, SharedString, Size, StrikethroughStyle, Style,
|
||||
SubscriberSet, Subscription, TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement,
|
||||
TimeToFirstWindowDraw, TransformationMatrix, Underline, UnderlineStyle, View, VisualContext,
|
||||
WeakView, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControls,
|
||||
WindowDecorations, WindowOptions, WindowParams, WindowTextSystem, SUBPIXEL_VARIANTS,
|
||||
TransformationMatrix, Underline, UnderlineStyle, View, VisualContext, WeakView,
|
||||
WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControls, WindowDecorations,
|
||||
WindowOptions, WindowParams, WindowTextSystem, SUBPIXEL_VARIANTS,
|
||||
};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use collections::{FxHashMap, FxHashSet};
|
||||
@@ -545,8 +545,6 @@ pub struct Window {
|
||||
hovered: Rc<Cell<bool>>,
|
||||
pub(crate) dirty: Rc<Cell<bool>>,
|
||||
pub(crate) needs_present: Rc<Cell<bool>>,
|
||||
/// We assign this to be notified when the platform graphics backend fires the next completion callback for drawing the window.
|
||||
present_completed: RefCell<Option<oneshot::Sender<()>>>,
|
||||
pub(crate) last_input_timestamp: Rc<Cell<Instant>>,
|
||||
pub(crate) refreshing: bool,
|
||||
pub(crate) draw_phase: DrawPhase,
|
||||
@@ -824,7 +822,6 @@ impl Window {
|
||||
hovered,
|
||||
dirty,
|
||||
needs_present,
|
||||
present_completed: RefCell::default(),
|
||||
last_input_timestamp,
|
||||
refreshing: false,
|
||||
draw_phase: DrawPhase::None,
|
||||
@@ -1494,29 +1491,13 @@ impl<'a> WindowContext<'a> {
|
||||
self.window.refreshing = false;
|
||||
self.window.draw_phase = DrawPhase::None;
|
||||
self.window.needs_present.set(true);
|
||||
|
||||
if let Some(TimeToFirstWindowDraw::Pending(start)) = self.app.time_to_first_window_draw {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
*self.window.present_completed.borrow_mut() = Some(tx);
|
||||
self.spawn(|mut cx| async move {
|
||||
rx.await.ok();
|
||||
cx.update(|cx| {
|
||||
let duration = start.elapsed();
|
||||
cx.time_to_first_window_draw = Some(TimeToFirstWindowDraw::Done(duration));
|
||||
log::info!("time to first window draw: {:?}", duration);
|
||||
cx.push_effect(Effect::Refresh);
|
||||
})
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
|
||||
#[profiling::function]
|
||||
fn present(&self) {
|
||||
let on_complete = self.window.present_completed.take();
|
||||
self.window
|
||||
.platform_window
|
||||
.draw(&self.window.rendered_frame.scene, on_complete);
|
||||
.draw(&self.window.rendered_frame.scene);
|
||||
self.window.needs_present.set(false);
|
||||
profiling::finish_frame!();
|
||||
}
|
||||
@@ -3799,12 +3780,6 @@ impl<'a> WindowContext<'a> {
|
||||
pub fn gpu_specs(&self) -> Option<GPUSpecs> {
|
||||
self.window.platform_window.gpu_specs()
|
||||
}
|
||||
|
||||
/// Get the current FPS (frames per second) of the window.
|
||||
/// This is only supported on macOS currently.
|
||||
pub fn fps(&self) -> Option<f32> {
|
||||
self.window.platform_window.fps()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
|
||||
@@ -91,7 +91,7 @@ pub struct OutlinePanel {
|
||||
fs: Arc<dyn Fs>,
|
||||
width: Option<Pixels>,
|
||||
project: Model<Project>,
|
||||
workspace: View<Workspace>,
|
||||
workspace: WeakView<Workspace>,
|
||||
active: bool,
|
||||
pinned: bool,
|
||||
scroll_handle: UniformListScrollHandle,
|
||||
@@ -607,7 +607,7 @@ impl OutlinePanel {
|
||||
|
||||
fn new(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) -> View<Self> {
|
||||
let project = workspace.project().clone();
|
||||
let workspace_handle = cx.view().clone();
|
||||
let workspace_handle = cx.view().downgrade();
|
||||
let outline_panel = cx.new_view(|cx| {
|
||||
let filter_editor = cx.new_view(|cx| {
|
||||
let mut editor = Editor::single_line(cx);
|
||||
@@ -865,7 +865,8 @@ impl OutlinePanel {
|
||||
};
|
||||
|
||||
if let Some((offset, anchor)) = scroll_target {
|
||||
self.workspace
|
||||
let activate = self
|
||||
.workspace
|
||||
.update(cx, |workspace, cx| match self.active_item() {
|
||||
Some(active_item) => {
|
||||
workspace.activate_item(active_item.as_ref(), true, change_selection, cx)
|
||||
@@ -873,21 +874,23 @@ impl OutlinePanel {
|
||||
None => workspace.activate_item(&active_editor, true, change_selection, cx),
|
||||
});
|
||||
|
||||
self.select_entry(entry.clone(), true, cx);
|
||||
if change_selection {
|
||||
active_editor.update(cx, |editor, cx| {
|
||||
editor.change_selections(
|
||||
Some(Autoscroll::Strategy(AutoscrollStrategy::Top)),
|
||||
cx,
|
||||
|s| s.select_ranges(Some(anchor..anchor)),
|
||||
);
|
||||
});
|
||||
active_editor.focus_handle(cx).focus(cx);
|
||||
} else {
|
||||
active_editor.update(cx, |editor, cx| {
|
||||
editor.set_scroll_anchor(ScrollAnchor { offset, anchor }, cx);
|
||||
});
|
||||
self.focus_handle.focus(cx);
|
||||
if activate.is_ok() {
|
||||
self.select_entry(entry.clone(), true, cx);
|
||||
if change_selection {
|
||||
active_editor.update(cx, |editor, cx| {
|
||||
editor.change_selections(
|
||||
Some(Autoscroll::Strategy(AutoscrollStrategy::Top)),
|
||||
cx,
|
||||
|s| s.select_ranges(Some(anchor..anchor)),
|
||||
);
|
||||
});
|
||||
active_editor.focus_handle(cx).focus(cx);
|
||||
} else {
|
||||
active_editor.update(cx, |editor, cx| {
|
||||
editor.set_scroll_anchor(ScrollAnchor { offset, anchor }, cx);
|
||||
});
|
||||
self.focus_handle.focus(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3320,7 +3323,11 @@ impl OutlinePanel {
|
||||
let buffer_search = self
|
||||
.active_item()
|
||||
.as_deref()
|
||||
.and_then(|active_item| self.workspace.read(cx).pane_for(active_item))
|
||||
.and_then(|active_item| {
|
||||
self.workspace
|
||||
.upgrade()
|
||||
.and_then(|workspace| workspace.read(cx).pane_for(active_item))
|
||||
})
|
||||
.and_then(|pane| {
|
||||
pane.read(cx)
|
||||
.toolbar()
|
||||
@@ -3572,8 +3579,10 @@ impl OutlinePanel {
|
||||
) {
|
||||
self.pinned = !self.pinned;
|
||||
if !self.pinned {
|
||||
if let Some((active_item, active_editor)) =
|
||||
workspace_active_editor(self.workspace.read(cx), cx)
|
||||
if let Some((active_item, active_editor)) = self
|
||||
.workspace
|
||||
.upgrade()
|
||||
.and_then(|workspace| workspace_active_editor(workspace.read(cx), cx))
|
||||
{
|
||||
if self.should_replace_active_item(active_item.as_ref()) {
|
||||
self.replace_active_editor(active_item, active_editor, cx);
|
||||
@@ -3839,8 +3848,10 @@ impl Panel for OutlinePanel {
|
||||
let old_active = outline_panel.active;
|
||||
outline_panel.active = active;
|
||||
if active && old_active != active {
|
||||
if let Some((active_item, active_editor)) =
|
||||
workspace_active_editor(outline_panel.workspace.read(cx), cx)
|
||||
if let Some((active_item, active_editor)) = outline_panel
|
||||
.workspace
|
||||
.upgrade()
|
||||
.and_then(|workspace| workspace_active_editor(workspace.read(cx), cx))
|
||||
{
|
||||
if outline_panel.should_replace_active_item(active_item.as_ref()) {
|
||||
outline_panel.replace_active_editor(active_item, active_editor, cx);
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
[package]
|
||||
name = "performance"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
path = "src/performance.rs"
|
||||
doctest = false
|
||||
|
||||
[features]
|
||||
test-support = [
|
||||
"collections/test-support",
|
||||
"gpui/test-support",
|
||||
"workspace/test-support",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
gpui.workspace = true
|
||||
log.workspace = true
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
settings.workspace = true
|
||||
workspace.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
collections = { workspace = true, features = ["test-support"] }
|
||||
gpui = { workspace = true, features = ["test-support"] }
|
||||
settings = { workspace = true, features = ["test-support"] }
|
||||
util = { workspace = true, features = ["test-support"] }
|
||||
workspace = { workspace = true, features = ["test-support"] }
|
||||
@@ -1 +0,0 @@
|
||||
../../LICENSE-GPL
|
||||
@@ -1,189 +0,0 @@
|
||||
use std::time::Instant;
|
||||
|
||||
use anyhow::Result;
|
||||
use gpui::{
|
||||
div, AppContext, InteractiveElement as _, Render, StatefulInteractiveElement as _,
|
||||
Subscription, ViewContext, VisualContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{Settings, SettingsSources, SettingsStore};
|
||||
use workspace::{
|
||||
ui::{Label, LabelCommon, LabelSize, Tooltip},
|
||||
ItemHandle, StatusItemView, Workspace,
|
||||
};
|
||||
|
||||
const SHOW_STARTUP_TIME_DURATION: std::time::Duration = std::time::Duration::from_secs(5);
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
PerformanceSettings::register(cx);
|
||||
|
||||
let mut enabled = PerformanceSettings::get_global(cx)
|
||||
.show_in_status_bar
|
||||
.unwrap_or(false);
|
||||
let start_time = Instant::now();
|
||||
let mut _observe_workspaces = toggle_status_bar_items(enabled, start_time, cx);
|
||||
|
||||
cx.observe_global::<SettingsStore>(move |cx| {
|
||||
let new_value = PerformanceSettings::get_global(cx)
|
||||
.show_in_status_bar
|
||||
.unwrap_or(false);
|
||||
if new_value != enabled {
|
||||
enabled = new_value;
|
||||
_observe_workspaces = toggle_status_bar_items(enabled, start_time, cx);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn toggle_status_bar_items(
|
||||
enabled: bool,
|
||||
start_time: Instant,
|
||||
cx: &mut AppContext,
|
||||
) -> Option<Subscription> {
|
||||
for window in cx.windows() {
|
||||
if let Some(workspace) = window.downcast::<Workspace>() {
|
||||
workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
toggle_status_bar_item(workspace, enabled, start_time, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
if enabled {
|
||||
log::info!("performance metrics display enabled");
|
||||
Some(cx.observe_new_views::<Workspace>(move |workspace, cx| {
|
||||
toggle_status_bar_item(workspace, true, start_time, cx);
|
||||
}))
|
||||
} else {
|
||||
log::info!("performance metrics display disabled");
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
struct PerformanceStatusBarItem {
|
||||
display_mode: DisplayMode,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum DisplayMode {
|
||||
StartupTime,
|
||||
Fps,
|
||||
}
|
||||
|
||||
impl PerformanceStatusBarItem {
|
||||
fn new(start_time: Instant, cx: &mut ViewContext<Self>) -> Self {
|
||||
let now = Instant::now();
|
||||
let display_mode = if now < start_time + SHOW_STARTUP_TIME_DURATION {
|
||||
DisplayMode::StartupTime
|
||||
} else {
|
||||
DisplayMode::Fps
|
||||
};
|
||||
|
||||
let this = Self { display_mode };
|
||||
|
||||
if let DisplayMode::StartupTime = display_mode {
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let now = Instant::now();
|
||||
let remaining_duration =
|
||||
(start_time + SHOW_STARTUP_TIME_DURATION).saturating_duration_since(now);
|
||||
cx.background_executor().timer(remaining_duration).await;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.display_mode = DisplayMode::Fps;
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for PerformanceStatusBarItem {
|
||||
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl gpui::IntoElement {
|
||||
let text = match self.display_mode {
|
||||
DisplayMode::StartupTime => cx
|
||||
.time_to_first_window_draw()
|
||||
.map_or("Pending".to_string(), |duration| {
|
||||
format!("{}ms", duration.as_millis())
|
||||
}),
|
||||
DisplayMode::Fps => cx.fps().map_or("".to_string(), |fps| {
|
||||
format!("{:3} FPS", fps.round() as u32)
|
||||
}),
|
||||
};
|
||||
|
||||
use gpui::ParentElement;
|
||||
let display_mode = self.display_mode;
|
||||
div()
|
||||
.id("performance status")
|
||||
.child(Label::new(text).size(LabelSize::Small))
|
||||
.tooltip(move |cx| match display_mode {
|
||||
DisplayMode::StartupTime => Tooltip::text("Time to first window draw", cx),
|
||||
DisplayMode::Fps => cx
|
||||
.new_view(|cx| {
|
||||
let tooltip = Tooltip::new("Current FPS");
|
||||
if let Some(time_to_first) = cx.time_to_first_window_draw() {
|
||||
tooltip.meta(format!(
|
||||
"Time to first window draw: {}ms",
|
||||
time_to_first.as_millis()
|
||||
))
|
||||
} else {
|
||||
tooltip
|
||||
}
|
||||
})
|
||||
.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl StatusItemView for PerformanceStatusBarItem {
|
||||
fn set_active_pane_item(
|
||||
&mut self,
|
||||
_active_pane_item: Option<&dyn ItemHandle>,
|
||||
_cx: &mut gpui::ViewContext<Self>,
|
||||
) {
|
||||
// This is not currently used.
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_status_bar_item(
|
||||
workspace: &mut Workspace,
|
||||
enabled: bool,
|
||||
start_time: Instant,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) {
|
||||
if enabled {
|
||||
workspace.status_bar().update(cx, |bar, cx| {
|
||||
bar.add_right_item(
|
||||
cx.new_view(|cx| PerformanceStatusBarItem::new(start_time, cx)),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
} else {
|
||||
workspace.status_bar().update(cx, |bar, cx| {
|
||||
bar.remove_items_of_type::<PerformanceStatusBarItem>(cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration of the display of performance details.
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
|
||||
pub struct PerformanceSettings {
|
||||
/// Display the time to first window draw and frame rate in the status bar.
|
||||
///
|
||||
/// Default: false
|
||||
pub show_in_status_bar: Option<bool>,
|
||||
}
|
||||
|
||||
impl Settings for PerformanceSettings {
|
||||
const KEY: Option<&'static str> = Some("performance");
|
||||
|
||||
type FileContent = Self;
|
||||
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
@@ -153,17 +153,6 @@ impl StatusBar {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn remove_items_of_type<T>(&mut self, cx: &mut ViewContext<Self>)
|
||||
where
|
||||
T: 'static + StatusItemView,
|
||||
{
|
||||
self.left_items
|
||||
.retain(|item| item.item_type() != TypeId::of::<T>());
|
||||
self.right_items
|
||||
.retain(|item| item.item_type() != TypeId::of::<T>());
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn add_right_item<T>(&mut self, item: View<T>, cx: &mut ViewContext<Self>)
|
||||
where
|
||||
T: 'static + StatusItemView,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
description = "The fast, collaborative code editor."
|
||||
edition = "2021"
|
||||
name = "zed"
|
||||
version = "0.152.0"
|
||||
version = "0.152.3"
|
||||
publish = false
|
||||
license = "GPL-3.0-or-later"
|
||||
authors = ["Zed Team <hi@zed.dev>"]
|
||||
@@ -75,7 +75,6 @@ outline.workspace = true
|
||||
outline_panel.workspace = true
|
||||
parking_lot.workspace = true
|
||||
paths.workspace = true
|
||||
performance.workspace = true
|
||||
profiling.workspace = true
|
||||
project.workspace = true
|
||||
project_panel.workspace = true
|
||||
|
||||
@@ -1 +1 @@
|
||||
dev
|
||||
stable
|
||||
@@ -268,7 +268,6 @@ fn init_ui(
|
||||
welcome::init(cx);
|
||||
settings_ui::init(cx);
|
||||
extensions_ui::init(cx);
|
||||
performance::init(cx);
|
||||
|
||||
cx.observe_global::<SettingsStore>({
|
||||
let languages = app_state.languages.clone();
|
||||
@@ -318,7 +317,6 @@ fn init_ui(
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let start_time = std::time::Instant::now();
|
||||
menu::init();
|
||||
zed_actions::init();
|
||||
|
||||
@@ -330,9 +328,7 @@ fn main() {
|
||||
init_logger();
|
||||
|
||||
log::info!("========== starting zed ==========");
|
||||
let app = App::new()
|
||||
.with_assets(Assets)
|
||||
.measure_time_to_first_window_draw(start_time);
|
||||
let app = App::new().with_assets(Assets);
|
||||
|
||||
let (installation_id, existing_installation_id_found) = app
|
||||
.background_executor()
|
||||
|
||||
@@ -12,15 +12,21 @@ We also offer a preview build of Zed which receives updates about a week ahead o
|
||||
curl -f https://zed.dev/install.sh | ZED_CHANNEL=preview sh
|
||||
```
|
||||
|
||||
The Zed installed by the script does not work on systems that:
|
||||
The Zed installed by the script works best on systems that:
|
||||
|
||||
- have no Vulkan compatible GPU available (for example Linux on an M-series macBook)
|
||||
- have no system-wide glibc (for example on NixOS or Alpine by default)
|
||||
- have a glibc older than version 2.29 (for example Amazon Linux 2 or Ubuntu 18 and earlier)
|
||||
- use an architecture other than 64-bit Intel or 64-bit ARM (for example a 32-bit or RISC-V machine)
|
||||
- have a Vulkan compatible GPU available (for example Linux on an M-series macBook)
|
||||
- have a system-wide glibc (NixOS and Alpine do not by default)
|
||||
- x86_64 (Intel/AMD): glibc version >= 2.29 (Ubuntu 20 and newer; Amazon Linux >2023)
|
||||
- aarch64 (ARM): glibc version >= 2.35 (Ubuntu 22 and newer)
|
||||
|
||||
Both Nix and Alpine have third-party Zed packages available (though they are currently a few weeks out of date). If you'd like to use our builds they do work if you install a glibc compatibility layer. On NixOS you can try [nix-ld](https://github.com/Mic92/nix-ld), and on Alpine [gcompat](https://wiki.alpinelinux.org/wiki/Running_glibc_programs).
|
||||
|
||||
You will need to build from source for:
|
||||
|
||||
- architectures other than 64-bit Intel or 64-bit ARM (for example a 32-bit or RISC-V machine)
|
||||
- Amazon Linux 2 on x86_64
|
||||
- Rocky Linux 9.3
|
||||
|
||||
## Other ways to install Zed on Linux
|
||||
|
||||
Zed is open source, and [you can install from source](./development/linux.md).
|
||||
|
||||
Reference in New Issue
Block a user