Browse Source

Add opt-in support for the 'puffin' profiler in eframe (#1483)

pull/1493/head
Emil Ernerfeldt 3 years ago
committed by GitHub
parent
commit
170b21b63e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 108
      Cargo.lock
  2. 2
      Cargo.toml
  3. 1
      eframe/CHANGELOG.md
  4. 7
      eframe/Cargo.toml
  5. 6
      eframe/README.md
  6. 5
      eframe/src/lib.rs
  7. 1
      egui-winit/CHANGELOG.md
  8. 4
      egui-winit/Cargo.toml
  9. 13
      egui-winit/src/epi.rs
  10. 22
      egui-winit/src/lib.rs
  11. 3
      egui/examples/README.md
  12. 1
      egui_glow/CHANGELOG.md
  13. 4
      egui_glow/Cargo.toml
  14. 16
      egui_glow/src/epi_backend.rs
  15. 22
      egui_glow/src/lib.rs
  16. 6
      egui_glow/src/painter.rs
  17. 1
      examples/README.md
  18. 14
      examples/puffin_profiler/Cargo.toml
  19. 9
      examples/puffin_profiler/README.md
  20. 68
      examples/puffin_profiler/src/main.rs

108
Cargo.lock

@ -81,6 +81,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
[[package]]
name = "approx"
version = "0.4.0"
@ -423,6 +429,9 @@ name = "cc"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
dependencies = [
"jobserver",
]
[[package]]
name = "cesu8"
@ -1018,6 +1027,8 @@ dependencies = [
"glow",
"image",
"poll-promise",
"puffin",
"puffin_http",
"rfd",
"three-d",
]
@ -1044,6 +1055,7 @@ dependencies = [
"epi",
"glow",
"instant",
"puffin",
"serde",
"tracing",
"tts",
@ -1118,6 +1130,7 @@ dependencies = [
"glow",
"glutin",
"memoffset",
"puffin",
"tracing",
"wasm-bindgen",
"web-sys",
@ -1806,6 +1819,15 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
dependencies = [
"libc",
]
[[package]]
name = "jpeg-decoder"
version = "0.2.2"
@ -2529,6 +2551,43 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "puffin"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78cc1dc2f2e3e4787201dd9404c9b097967fd74b9013ba41e62448b617cb7ac2"
dependencies = [
"anyhow",
"bincode",
"byteorder",
"once_cell",
"parking_lot 0.12.0",
"ruzstd",
"serde",
"zstd",
]
[[package]]
name = "puffin_http"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784f1952bda562ec8b76651f0c879b61eb02eaf20fe9acd2a8ca69b4898b13fd"
dependencies = [
"anyhow",
"crossbeam-channel",
"log",
"puffin",
]
[[package]]
name = "puffin_profiler"
version = "0.1.0"
dependencies = [
"eframe",
"puffin",
"puffin_http",
]
[[package]]
name = "quote"
version = "1.0.15"
@ -2795,6 +2854,16 @@ dependencies = [
"unicode-script",
]
[[package]]
name = "ruzstd"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cada0ef59efa6a5f4dc5e491f93d9f31e3fc7758df421ff1de8a706338e1100"
dependencies = [
"byteorder",
"twox-hash",
]
[[package]]
name = "ryu"
version = "1.0.9"
@ -3374,6 +3443,16 @@ dependencies = [
"windows",
]
[[package]]
name = "twox-hash"
version = "1.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ee73e6e4924fe940354b8d4d98cad5231175d615cd855b758adc658c0aac6a0"
dependencies = [
"cfg-if 1.0.0",
"static_assertions",
]
[[package]]
name = "unicode-bidi"
version = "0.3.7"
@ -4051,6 +4130,35 @@ dependencies = [
"synstructure",
]
[[package]]
name = "zstd"
version = "0.10.0+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b1365becbe415f3f0fcd024e2f7b45bacfb5bdd055f0dc113571394114e7bdd"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "4.1.4+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f7cd17c9af1a4d6c24beb1cc54b17e2ef7b593dc92f19e9d9acad8b182bbaee"
dependencies = [
"libc",
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "1.6.3+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc49afa5c8d634e75761feda8c592051e7eeb4683ba827211eb0d731d3402ea8"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "zvariant"
version = "3.1.2"

2
Cargo.toml

@ -12,6 +12,8 @@ members = [
"emath",
"epaint",
"epi",
"examples/puffin_profiler",
]
[profile.dev]

1
eframe/CHANGELOG.md

@ -22,6 +22,7 @@ NOTE: [`egui_web`](../egui_web/CHANGELOG.md), [`egui-winit`](../egui-winit/CHANG
* `dark-light` (dark mode detection) is now an opt-in feature ([#1437](https://github.com/emilk/egui/pull/1437)).
* Fixed potential scale bug when DPI scaling changes (e.g. when dragging a window between different displays) ([#1441](https://github.com/emilk/egui/pull/1441)).
* MSRV (Minimum Supported Rust Version) is now `1.60.0` ([#1467](https://github.com/emilk/egui/pull/1467)).
* Added new feature `puffin` to add [`puffin profiler`](https://github.com/EmbarkStudios/puffin) scopes ([#1483](https://github.com/emilk/egui/pull/1483)).
## 0.17.0 - 2022-02-22

7
eframe/Cargo.toml

@ -37,6 +37,11 @@ persistence = [
"epi/persistence",
]
# Enable profiling with the puffin crate: https://github.com/EmbarkStudios/puffin
# Only enabled on native, because of the low resolution (1ms) of time keeping in browsers.
# eframe will call `puffin::GlobalProfiler::lock().new_frame()` for you
puffin = ["egui_glow/puffin"]
# enable screen reader support (requires `ctx.options().screen_reader = true;`)
screen_reader = [
"egui-winit/screen_reader",
@ -74,5 +79,7 @@ image = { version = "0.24", default-features = false, features = [
"png",
] }
poll-promise = "0.1"
puffin = "0.13"
puffin_http = "0.10"
rfd = "0.8"
three-d = { version = "0.11", default-features = false }

6
eframe/README.md

@ -8,9 +8,10 @@
`eframe` is the official framework library for writing apps using [`egui`](https://github.com/emilk/egui). The app can be compiled both to run natively (cross platform) or be compiled to a web app (using WASM).
To get started, go to <https://github.com/emilk/eframe_template/> and follow the instructions there!
To get started, see the [crate examples](https://github.com/emilk/egui/tree/master/examples) and [single-file examples](https://github.com/emilk/egui/tree/master/eframe/examples).
To learn how to set up `eframe` for web and native, go to <https://github.com/emilk/eframe_template/> and follow the instructions there!
You can also take a look at [the `eframe` examples folder](https://github.com/emilk/egui/tree/master/eframe/examples). There is also an excellent tutorial video at <https://www.youtube.com/watch?v=NtUkr_z7l84>.
There is also a tutorial video at <https://www.youtube.com/watch?v=NtUkr_z7l84>.
For how to use `egui`, see [the egui docs](https://docs.rs/egui).
@ -37,6 +38,7 @@ Not all rust crates work when compiled to WASM, but here are some useful crates
* Audio: [`cpal`](https://github.com/RustAudio/cpal).
* HTTP client: [`ehttp`](https://github.com/emilk/ehttp).
* Time: [`chrono`](https://github.com/chronotope/chrono).
* WebSockets: [`ewebsock`](https://github.com/rerun-io/ewebsock).
## Name

5
eframe/src/lib.rs

@ -4,9 +4,8 @@
//! and are happy with just using egui for all visuals,
//! Then `eframe` is for you!
//!
//! To get started, look at <https://github.com/emilk/eframe_template>.
//!
//! You can also take a look at [the `eframe` examples folder](https://github.com/emilk/egui/tree/master/eframe/examples).
//! To get started, see the [crate examples](https://github.com/emilk/egui/tree/master/examples) and [single-file examples](https://github.com/emilk/egui/tree/master/eframe/examples).
//! To learn how to set up `eframe` for web and native, go to <https://github.com/emilk/eframe_template/> and follow the instructions there!
//!
//! You write your application code for [`epi`] (implementing [`epi::App`]) and then
//! call from [`crate::run_native`] your `main.rs`, and/or call `eframe::start_web` from your `lib.rs`.

1
egui-winit/CHANGELOG.md

@ -7,6 +7,7 @@ All notable changes to the `egui-winit` integration will be noted in this file.
* Renamed the feature `convert_bytemuck` to `bytemuck` ([#1467](https://github.com/emilk/egui/pull/1467)).
* Renamed the feature `serialize` to `serde` ([#1467](https://github.com/emilk/egui/pull/1467)).
* MSRV (Minimum Supported Rust Version) is now `1.60.0` ([#1467](https://github.com/emilk/egui/pull/1467)).
* Added new feature `puffin` to add [`puffin profiler`](https://github.com/EmbarkStudios/puffin) scopes ([#1483](https://github.com/emilk/egui/pull/1483)).
## 0.17.0 - 2022-02-22

4
egui-winit/Cargo.toml

@ -36,6 +36,9 @@ epi_backend = ["epi", "glow"]
# enable opening links in a browser when an egui hyperlink is clicked.
links = ["webbrowser"]
# Enable profiling with the puffin crate: https://github.com/EmbarkStudios/puffin
puffin = ["dep:puffin"]
# experimental support for a screen reader
screen_reader = ["tts"]
@ -57,6 +60,7 @@ epi = { version = "0.17.0", path = "../epi", optional = true }
arboard = { version = "2.1", optional = true, default-features = false }
dark-light = { version = "0.2.1", optional = true }
glow = { version = "0.11", optional = true }
puffin = { version = "0.13", optional = true }
serde = { version = "1.0", optional = true, features = ["derive"] }
webbrowser = { version = "0.6", optional = true }

13
egui-winit/src/epi.rs

@ -191,6 +191,7 @@ impl EpiIntegration {
}
pub fn warm_up(&mut self, app: &mut dyn epi::App, window: &winit::window::Window) {
crate::profile_function!();
let saved_memory: egui::Memory = self.egui_ctx.memory().clone();
self.egui_ctx.memory().set_everything_is_visible(true);
let full_output = self.update(app, window);
@ -230,6 +231,7 @@ impl EpiIntegration {
let raw_input = self.egui_winit.take_egui_input(window);
let full_output = self.egui_ctx.run(raw_input, |egui_ctx| {
crate::profile_scope!("App::update");
app.update(egui_ctx, &mut self.frame);
});
self.pending_full_output.append(full_output);
@ -274,7 +276,10 @@ impl EpiIntegration {
pub fn save(&mut self, _app: &mut dyn epi::App, _window: &winit::window::Window) {
#[cfg(feature = "persistence")]
if let Some(storage) = self.frame.storage_mut() {
crate::profile_function!();
if _app.persist_native_window() {
crate::profile_scope!("native_window");
epi::set_value(
storage,
STORAGE_WINDOW_KEY,
@ -282,9 +287,15 @@ impl EpiIntegration {
);
}
if _app.persist_egui_memory() {
crate::profile_scope!("egui_memory");
epi::set_value(storage, STORAGE_EGUI_MEMORY_KEY, &*self.egui_ctx.memory());
}
_app.save(storage);
{
crate::profile_scope!("App::save");
_app.save(storage);
}
crate::profile_scope!("Storage::flush");
storage.flush();
}
}

22
egui-winit/src/lib.rs

@ -646,3 +646,25 @@ fn translate_cursor(cursor_icon: egui::CursorIcon) -> Option<winit::window::Curs
egui::CursorIcon::ZoomOut => Some(winit::window::CursorIcon::ZoomOut),
}
}
// ---------------------------------------------------------------------------
/// Profiling macro for feature "puffin"
#[doc(hidden)]
#[macro_export]
macro_rules! profile_function {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
puffin::profile_function!($($arg)*);
};
}
/// Profiling macro for feature "puffin"
#[doc(hidden)]
#[macro_export]
macro_rules! profile_scope {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
puffin::profile_scope!($($arg)*);
};
}

3
egui/examples/README.md

@ -2,4 +2,5 @@ There are no stand-alone egui examples, because egui is not stand-alone!
There are plenty of examples in [the online demo](https://www.egui.rs/#demo). You can find the source code for it at <https://github.com/emilk/egui/tree/master/egui_demo_lib>.
If you are using `eframe`, check out [the `eframe` examples](https://github.com/emilk/egui/tree/master/eframe/examples) and [the `eframe` template repository](https://github.com/emilk/eframe_template/).
If you are using `eframe`, the [crate examples](https://github.com/emilk/egui/tree/master/examples) and [single-file examples](https://github.com/emilk/egui/tree/master/eframe/examples).
To learn how to set up `eframe` for web and native, go to <https://github.com/emilk/eframe_template/> and follow the instructions there!

1
egui_glow/CHANGELOG.md

@ -9,6 +9,7 @@ All notable changes to the `egui_glow` integration will be noted in this file.
* Fixed potential scale bug when DPI scaling changes (e.g. when dragging a window between different displays) ([#1441](https://github.com/emilk/egui/pull/1441)).
* MSRV (Minimum Supported Rust Version) is now `1.60.0` ([#1467](https://github.com/emilk/egui/pull/1467)).
* `clipboard`, `links`, `persistence`, `winit` are now all opt-in features ([#1467](https://github.com/emilk/egui/pull/1467)).
* Added new feature `puffin` to add [`puffin profiler`](https://github.com/EmbarkStudios/puffin) scopes ([#1483](https://github.com/emilk/egui/pull/1483)).
## 0.17.0 - 2022-02-22

4
egui_glow/Cargo.toml

@ -47,6 +47,9 @@ persistence = [
"epi?/persistence",
]
# Enable profiling with the puffin crate: https://github.com/EmbarkStudios/puffin
puffin = ["dep:puffin", "egui-winit?/puffin"]
# experimental support for a screen reader
screen_reader = ["egui-winit?/screen_reader"]
@ -72,6 +75,7 @@ egui-winit = { version = "0.17.0", path = "../egui-winit", optional = true, defa
"epi_backend",
] }
glutin = { version = "0.28.0", optional = true }
puffin = { version = "0.13", optional = true }
# Web:
[target.'cfg(target_arch = "wasm32")'.dependencies]

16
egui_glow/src/epi_backend.rs

@ -11,6 +11,7 @@ fn create_display(
glutin::WindowedContext<glutin::PossiblyCurrent>,
glow::Context,
) {
crate::profile_function!();
let gl_window = unsafe {
glutin::ContextBuilder::new()
.with_depth_buffer(native_options.depth_buffer)
@ -77,15 +78,20 @@ pub fn run(app_name: &str, native_options: &epi::NativeOptions, app_creator: epi
event_loop.run(move |event, _, control_flow| {
let mut redraw = || {
#[cfg(feature = "puffin")]
puffin::GlobalProfiler::lock().new_frame();
if !is_focused {
// On Mac, a minimized Window uses up all CPU: https://github.com/emilk/egui/issues/325
// We can't know if we are minimized: https://github.com/rust-windowing/winit/issues/208
// But we know if we are focused (in foreground). When minimized, we are not focused.
// However, a user may want an egui with an animation in the background,
// so we still need to repaint quite fast.
crate::profile_scope!("bg_sleep");
std::thread::sleep(std::time::Duration::from_millis(10));
}
crate::profile_scope!("frame");
let screen_size_in_pixels: [u32; 2] = gl_window.window().inner_size().into();
crate::painter::clear(&gl, screen_size_in_pixels, app.clear_color());
@ -99,7 +105,10 @@ pub fn run(app_name: &str, native_options: &epi::NativeOptions, app_creator: epi
integration.handle_platform_output(gl_window.window(), platform_output);
let clipped_primitives = integration.egui_ctx.tessellate(shapes);
let clipped_primitives = {
crate::profile_scope!("tessellate");
integration.egui_ctx.tessellate(shapes)
};
painter.paint_and_update_textures(
screen_size_in_pixels,
@ -108,7 +117,10 @@ pub fn run(app_name: &str, native_options: &epi::NativeOptions, app_creator: epi
&textures_delta,
);
gl_window.swap_buffers().unwrap();
{
crate::profile_scope!("swap_buffers");
gl_window.swap_buffers().unwrap();
}
{
*control_flow = if integration.should_quit() {

22
egui_glow/src/lib.rs

@ -85,3 +85,25 @@ pub fn check_for_gl_error_impl(gl: &glow::Context, file: &str, line: u32, contex
}
}
}
// ---------------------------------------------------------------------------
/// Profiling macro for feature "puffin"
#[doc(hidden)]
#[macro_export]
macro_rules! profile_function {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
puffin::profile_function!($($arg)*);
};
}
/// Profiling macro for feature "puffin"
#[doc(hidden)]
#[macro_export]
macro_rules! profile_scope {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
puffin::profile_scope!($($arg)*);
};
}

6
egui_glow/src/painter.rs

@ -96,6 +96,7 @@ impl Painter {
pp_fb_extent: Option<[i32; 2]>,
shader_prefix: &str,
) -> Result<Painter, String> {
crate::profile_function!();
check_for_gl_error!(&gl, "before Painter::new");
let max_texture_side = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as usize;
@ -297,6 +298,7 @@ impl Painter {
clipped_primitives: &[egui::ClippedPrimitive],
textures_delta: &egui::TexturesDelta,
) {
crate::profile_function!();
for (id, image_delta) in &textures_delta.set {
self.set_texture(*id, image_delta);
}
@ -333,6 +335,7 @@ impl Painter {
pixels_per_point: f32,
clipped_primitives: &[egui::ClippedPrimitive],
) {
crate::profile_function!();
self.assert_not_destroyed();
if let Some(ref mut post_process) = self.post_process {
@ -355,6 +358,7 @@ impl Painter {
}
Primitive::Callback(callback) => {
if callback.rect.is_positive() {
crate::profile_scope!("callback");
// Transform callback rect to physical pixels:
let rect_min_x = pixels_per_point * callback.rect.min.x;
let rect_min_y = pixels_per_point * callback.rect.min.y;
@ -456,6 +460,8 @@ impl Painter {
// ------------------------------------------------------------------------
pub fn set_texture(&mut self, tex_id: egui::TextureId, delta: &egui::epaint::ImageDelta) {
crate::profile_function!();
self.assert_not_destroyed();
let glow_texture = *self

1
examples/README.md

@ -0,0 +1 @@
Examples of how to use [`eframe`](https://github.com/emilk/egui/tree/master/eframe) and [`egui`](https://github.com/emilk/egui/).

14
examples/puffin_profiler/Cargo.toml

@ -0,0 +1,14 @@
[package]
name = "puffin_profiler"
version = "0.1.0"
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
license = "MIT OR Apache-2.0"
edition = "2021"
rust-version = "1.60"
publish = false
[dependencies]
eframe = { path = "../../eframe", features = ["puffin"] }
puffin = "0.13"
puffin_http = "0.10"

9
examples/puffin_profiler/README.md

@ -0,0 +1,9 @@
Example how to use the [puffin profiler](https://github.com/EmbarkStudios/puffin) with an `eframe` app.
```sh
cargo run -p puffin_profiler &
cargo install puffin_viewer
puffin_viewer --url 127.0.0.1:8585
```

68
examples/puffin_profiler/src/main.rs

@ -0,0 +1,68 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
use eframe::egui;
fn main() {
start_puffin_server(); // NOTE: you may only want to call this if the users specifies some flag or clicks a button!
let options = eframe::NativeOptions::default();
eframe::run_native(
"My egui App",
options,
Box::new(|_cc| Box::new(MyApp::default())),
);
}
#[derive(Default)]
struct MyApp {}
impl eframe::App for MyApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("Example of how to use the puffin profiler with egui");
ui.separator();
let cmd = "cargo install puffin_viewer && puffin_viewer --url 127.0.0.1:8585";
ui.label("To connect, run this:");
ui.horizontal(|ui| {
ui.monospace(cmd);
if ui.small_button("📋").clicked() {
ui.output().copied_text = cmd.into();
}
});
ui.separator();
ui.label("Note that this app runs in 'reactive' mode, so you must interact with the app for new profile events to be sent. Waving the mouse over this window is enough.");
if ui
.button(
"Click to sleep a bit. That should be visible as a spike in the profiler view!",
)
.clicked()
{
puffin::profile_scope!("sleep");
std::thread::sleep(std::time::Duration::from_millis(50));
}
});
}
}
fn start_puffin_server() {
puffin::set_scopes_on(true); // tell puffin to collect data
match puffin_http::Server::new("0.0.0.0:8585") {
Ok(puffin_server) => {
eprintln!("Run: cargo install puffin_viewer && puffin_viewer --url 127.0.0.1:8585");
// We can store the server if we want, but in this case we just want
// it to keep running. Dropping it closes the server, so let's not drop it!
#[allow(clippy::mem_forget)]
std::mem::forget(puffin_server);
}
Err(err) => {
eprintln!("Failed to start puffin server: {}", err);
}
};
}
Loading…
Cancel
Save