Use `profiling` crate to support more profiler backends (#5150)

Hey! I am not sure if this is something that's been considered before
and decided against (I couldn't find any PR's or issues).

This change removes the internal profiling macros in library crates and
the `puffin` feature and replaces it with similar functions in the
[profiling](https://github.com/aclysma/profiling) crate. This crate
provides a layer of abstraction over various profiler instrumentation
crates and allows library users to pick their favorite (supported)
profiler.

An additional benefit for puffin users is that dependencies of egui are
included in the instrumentation output too (mainly wgpu which uses the
profiling crate), so more details might be available when profiling.

A breaking change is that instead of using the `puffin` feature on egui,
users that want to profile the crate with puffin instead have to enable
the `profile-with-puffin` feature on the profiling crate. Similarly they
could instead choose to use `profile-with-tracy` etc.

I tried to add a 'tracy' feature to egui_demo_app in order to showcase ,
however the /scripts/check.sh currently breaks on mutually exclusive
features (which this introduces), so I decided against including it for
the initial PR. I'm happy to iterate more on this if there is interest
in taking this PR though.

Screenshot showing the additional info for wgpu now available when using
puffin

![image](https://github.com/user-attachments/assets/49fc0e7e-8f88-40cb-a69e-74ca2e3f90f3)
This commit is contained in:
Ted de Munnik 2024-12-16 09:15:54 +01:00 committed by GitHub
parent 9aae14cdf4
commit 3af907919b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
46 changed files with 272 additions and 482 deletions

View File

@ -1235,7 +1235,7 @@ dependencies = [
"parking_lot", "parking_lot",
"percent-encoding", "percent-encoding",
"pollster 0.4.0", "pollster 0.4.0",
"puffin", "profiling",
"raw-window-handle 0.6.2", "raw-window-handle 0.6.2",
"ron", "ron",
"serde", "serde",
@ -1263,7 +1263,7 @@ dependencies = [
"epaint", "epaint",
"log", "log",
"nohash-hasher", "nohash-hasher",
"puffin", "profiling",
"ron", "ron",
"serde", "serde",
] ]
@ -1278,7 +1278,7 @@ dependencies = [
"egui", "egui",
"epaint", "epaint",
"log", "log",
"puffin", "profiling",
"thiserror", "thiserror",
"type-map", "type-map",
"web-time", "web-time",
@ -1296,7 +1296,7 @@ dependencies = [
"document-features", "document-features",
"egui", "egui",
"log", "log",
"puffin", "profiling",
"raw-window-handle 0.6.2", "raw-window-handle 0.6.2",
"serde", "serde",
"smithay-clipboard", "smithay-clipboard",
@ -1321,6 +1321,7 @@ dependencies = [
"image", "image",
"log", "log",
"poll-promise", "poll-promise",
"profiling",
"puffin", "puffin",
"puffin_http", "puffin_http",
"rfd", "rfd",
@ -1360,7 +1361,7 @@ dependencies = [
"image", "image",
"log", "log",
"mime_guess2", "mime_guess2",
"puffin", "profiling",
"resvg", "resvg",
"serde", "serde",
"syntect", "syntect",
@ -1380,7 +1381,7 @@ dependencies = [
"glutin-winit", "glutin-winit",
"log", "log",
"memoffset", "memoffset",
"puffin", "profiling",
"wasm-bindgen", "wasm-bindgen",
"web-sys", "web-sys",
"winit", "winit",
@ -1528,7 +1529,7 @@ dependencies = [
"log", "log",
"nohash-hasher", "nohash-hasher",
"parking_lot", "parking_lot",
"puffin", "profiling",
"rayon", "rayon",
"serde", "serde",
] ]
@ -3109,6 +3110,20 @@ name = "profiling"
version = "1.0.16" version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d"
dependencies = [
"profiling-procmacros",
"puffin",
]
[[package]]
name = "profiling-procmacros"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30"
dependencies = [
"quote",
"syn",
]
[[package]] [[package]]
name = "puffin" name = "puffin"
@ -3147,6 +3162,7 @@ dependencies = [
"eframe", "eframe",
"env_logger", "env_logger",
"log", "log",
"profiling",
"puffin", "puffin",
"puffin_http", "puffin_http",
] ]

View File

@ -87,6 +87,7 @@ log = { version = "0.4", features = ["std"] }
nohash-hasher = "0.2" nohash-hasher = "0.2"
parking_lot = "0.12" parking_lot = "0.12"
pollster = "0.4" pollster = "0.4"
profiling = {version = "1.0", default-features = false }
puffin = "0.19" puffin = "0.19"
puffin_http = "0.16" puffin_http = "0.16"
raw-window-handle = "0.6.0" raw-window-handle = "0.6.0"

View File

@ -71,19 +71,6 @@ persistence = [
"serde", "serde",
] ]
## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate.
##
## `eframe` will call `puffin::GlobalProfiler::lock().new_frame()` for you
##
## Only enabled on native, because of the low resolution (1ms) of clocks in browsers.
puffin = [
"dep:puffin",
"egui/puffin",
"egui_glow?/puffin",
"egui-wgpu?/puffin",
"egui-winit/puffin",
]
## Enables wayland support and fixes clipboard issue. ## Enables wayland support and fixes clipboard issue.
wayland = ["egui-winit/wayland", "egui-wgpu?/wayland", "egui_glow?/wayland", "glutin?/wayland", "glutin-winit?/wayland"] wayland = ["egui-winit/wayland", "egui-wgpu?/wayland", "egui_glow?/wayland", "glutin?/wayland", "glutin-winit?/wayland"]
@ -127,6 +114,7 @@ ahash.workspace = true
document-features.workspace = true document-features.workspace = true
log.workspace = true log.workspace = true
parking_lot.workspace = true parking_lot.workspace = true
profiling.workspace = true
raw-window-handle.workspace = true raw-window-handle.workspace = true
static_assertions = "1.1.0" static_assertions = "1.1.0"
web-time.workspace = true web-time.workspace = true
@ -157,7 +145,6 @@ pollster = { workspace = true, optional = true } # needed for wgpu
glutin = { workspace = true, optional = true, default-features = false, features = ["egl", "wgl"] } glutin = { workspace = true, optional = true, default-features = false, features = ["egl", "wgl"] }
glutin-winit = { workspace = true, optional = true, default-features = false, features = ["egl", "wgl"] } glutin-winit = { workspace = true, optional = true, default-features = false, features = ["egl", "wgl"] }
home = { workspace = true, optional = true } home = { workspace = true, optional = true }
puffin = { workspace = true, optional = true }
wgpu = { workspace = true, optional = true, features = [ wgpu = { workspace = true, optional = true, features = [
# Let's enable some backends so that users can use `eframe` out-of-the-box # Let's enable some backends so that users can use `eframe` out-of-the-box
# without having to explicitly opt-in to backends # without having to explicitly opt-in to backends

View File

@ -788,8 +788,7 @@ pub struct IntegrationInfo {
/// ///
/// This includes [`App::update`] as well as rendering (except for vsync waiting). /// This includes [`App::update`] as well as rendering (except for vsync waiting).
/// ///
/// For a more detailed view of cpu usage, use the [`puffin`](https://crates.io/crates/puffin) /// For a more detailed view of cpu usage, connect your preferred profiler by enabling it's feature in [`profiling`](https://crates.io/crates/profiling).
/// profiler together with the `puffin` feature of `eframe`.
/// ///
/// `None` if this is the first frame. /// `None` if this is the first frame.
pub cpu_usage: Option<f32>, pub cpu_usage: Option<f32>,
@ -831,7 +830,7 @@ impl Storage for DummyStorage {
/// Get and deserialize the [RON](https://github.com/ron-rs/ron) stored at the given key. /// Get and deserialize the [RON](https://github.com/ron-rs/ron) stored at the given key.
#[cfg(feature = "ron")] #[cfg(feature = "ron")]
pub fn get_value<T: serde::de::DeserializeOwned>(storage: &dyn Storage, key: &str) -> Option<T> { pub fn get_value<T: serde::de::DeserializeOwned>(storage: &dyn Storage, key: &str) -> Option<T> {
crate::profile_function!(key); profiling::function_scope!(key);
storage storage
.get_string(key) .get_string(key)
.and_then(|value| match ron::from_str(&value) { .and_then(|value| match ron::from_str(&value) {
@ -847,7 +846,7 @@ pub fn get_value<T: serde::de::DeserializeOwned>(storage: &dyn Storage, key: &st
/// Serialize the given value as [RON](https://github.com/ron-rs/ron) and store with the given key. /// Serialize the given value as [RON](https://github.com/ron-rs/ron) and store with the given key.
#[cfg(feature = "ron")] #[cfg(feature = "ron")]
pub fn set_value<T: serde::Serialize>(storage: &mut dyn Storage, key: &str, value: &T) { pub fn set_value<T: serde::Serialize>(storage: &mut dyn Storage, key: &str, value: &T) {
crate::profile_function!(key); profiling::function_scope!(key);
match ron::ser::to_string(value) { match ron::ser::to_string(value) {
Ok(string) => storage.set_string(key, string), Ok(string) => storage.set_string(key, string),
Err(err) => log::error!("eframe failed to encode data using ron: {}", err), Err(err) => log::error!("eframe failed to encode data using ron: {}", err),

View File

@ -22,7 +22,7 @@ pub trait IconDataExt {
/// # Errors /// # Errors
/// If this is not a valid png. /// If this is not a valid png.
pub fn from_png_bytes(png_bytes: &[u8]) -> Result<IconData, image::ImageError> { pub fn from_png_bytes(png_bytes: &[u8]) -> Result<IconData, image::ImageError> {
crate::profile_function!(); profiling::function_scope!();
let image = image::load_from_memory(png_bytes)?; let image = image::load_from_memory(png_bytes)?;
Ok(from_image(image)) Ok(from_image(image))
} }
@ -38,7 +38,7 @@ fn from_image(image: image::DynamicImage) -> IconData {
impl IconDataExt for IconData { impl IconDataExt for IconData {
fn to_image(&self) -> Result<image::RgbaImage, String> { fn to_image(&self) -> Result<image::RgbaImage, String> {
crate::profile_function!(); profiling::function_scope!();
let Self { let Self {
rgba, rgba,
width, width,
@ -48,7 +48,7 @@ impl IconDataExt for IconData {
} }
fn to_png_bytes(&self) -> Result<Vec<u8>, String> { fn to_png_bytes(&self) -> Result<Vec<u8>, String> {
crate::profile_function!(); profiling::function_scope!();
let image = self.to_image()?; let image = self.to_image()?;
let mut png_bytes: Vec<u8> = Vec::new(); let mut png_bytes: Vec<u8> = Vec::new();
image image

View File

@ -129,6 +129,17 @@
//! ## Feature flags //! ## Feature flags
#![doc = document_features::document_features!()] #![doc = document_features::document_features!()]
//! //!
//! ## Instrumentation
//! This crate supports using the [profiling](https://crates.io/crates/profiling) crate for instrumentation.
//! You can enable features on the profiling crates in your application to add instrumentation for all
//! crates that support it, including egui. See the profiling crate docs for more information.
//! ```toml
//! [dependencies]
//! profiling = "1.0"
//! [features]
//! profile-with-puffin = ["profiling/profile-with-puffin"]
//! ```
//!
#![warn(missing_docs)] // let's keep eframe well-documented #![warn(missing_docs)] // let's keep eframe well-documented
#![allow(clippy::needless_doctest_main)] #![allow(clippy::needless_doctest_main)]
@ -445,33 +456,3 @@ impl std::fmt::Display for Error {
/// Short for `Result<T, eframe::Error>`. /// Short for `Result<T, eframe::Error>`.
pub type Result<T = (), E = Error> = std::result::Result<T, E>; pub type Result<T = (), E = Error> = std::result::Result<T, E>;
// ---------------------------------------------------------------------------
mod profiling_scopes {
#![allow(unused_macros)]
#![allow(unused_imports)]
/// Profiling macro for feature "puffin"
macro_rules! profile_function {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
puffin::profile_function!($($arg)*);
};
}
pub(crate) use profile_function;
/// Profiling macro for feature "puffin"
macro_rules! profile_scope {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
puffin::profile_scope!($($arg)*);
};
}
pub(crate) use profile_scope;
}
#[allow(unused_imports)]
pub(crate) use profiling_scopes::{profile_function, profile_scope};

View File

@ -59,7 +59,7 @@ enum AppIconStatus {
/// Since window creation can be lazy, call this every frame until it's either successfully or gave up. /// Since window creation can be lazy, call this every frame until it's either successfully or gave up.
/// (See [`AppIconStatus`]) /// (See [`AppIconStatus`])
fn set_title_and_icon(_title: &str, _icon_data: Option<&IconData>) -> AppIconStatus { fn set_title_and_icon(_title: &str, _icon_data: Option<&IconData>) -> AppIconStatus {
crate::profile_function!(); profiling::function_scope!();
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
@ -201,7 +201,7 @@ fn set_app_icon_windows(icon_data: &IconData) -> AppIconStatus {
#[allow(unsafe_code)] #[allow(unsafe_code)]
fn set_title_and_icon_mac(title: &str, icon_data: Option<&IconData>) -> AppIconStatus { fn set_title_and_icon_mac(title: &str, icon_data: Option<&IconData>) -> AppIconStatus {
use crate::icon_data::IconDataExt as _; use crate::icon_data::IconDataExt as _;
crate::profile_function!(); profiling::function_scope!();
use objc2::ClassType; use objc2::ClassType;
use objc2_app_kit::{NSApplication, NSImage}; use objc2_app_kit::{NSApplication, NSImage};
@ -237,7 +237,7 @@ fn set_title_and_icon_mac(title: &str, icon_data: Option<&IconData>) -> AppIconS
log::trace!("NSImage::initWithData…"); log::trace!("NSImage::initWithData…");
let app_icon = NSImage::initWithData(NSImage::alloc(), &data); let app_icon = NSImage::initWithData(NSImage::alloc(), &data);
crate::profile_scope!("setApplicationIconImage_"); profiling::scope!("setApplicationIconImage_");
log::trace!("setApplicationIconImage…"); log::trace!("setApplicationIconImage…");
app.setApplicationIconImage(app_icon.as_deref()); app.setApplicationIconImage(app_icon.as_deref());
} }
@ -246,7 +246,7 @@ fn set_title_and_icon_mac(title: &str, icon_data: Option<&IconData>) -> AppIconS
if let Some(main_menu) = app.mainMenu() { if let Some(main_menu) = app.mainMenu() {
if let Some(item) = main_menu.itemAtIndex(0) { if let Some(item) = main_menu.itemAtIndex(0) {
if let Some(app_menu) = item.submenu() { if let Some(app_menu) = item.submenu() {
crate::profile_scope!("setTitle_"); profiling::scope!("setTitle_");
app_menu.setTitle(&NSString::from_str(title)); app_menu.setTitle(&NSString::from_str(title));
} }
} }

View File

@ -19,7 +19,7 @@ pub fn viewport_builder(
native_options: &mut epi::NativeOptions, native_options: &mut epi::NativeOptions,
window_settings: Option<WindowSettings>, window_settings: Option<WindowSettings>,
) -> ViewportBuilder { ) -> ViewportBuilder {
crate::profile_function!(); profiling::function_scope!();
let mut viewport_builder = native_options.viewport.clone(); let mut viewport_builder = native_options.viewport.clone();
@ -67,7 +67,7 @@ pub fn viewport_builder(
#[cfg(not(target_os = "ios"))] #[cfg(not(target_os = "ios"))]
if native_options.centered { if native_options.centered {
crate::profile_scope!("center"); profiling::scope!("center");
if let Some(monitor) = event_loop if let Some(monitor) = event_loop
.primary_monitor() .primary_monitor()
.or_else(|| event_loop.available_monitors().next()) .or_else(|| event_loop.available_monitors().next())
@ -94,8 +94,7 @@ pub fn apply_window_settings(
window: &winit::window::Window, window: &winit::window::Window,
window_settings: Option<WindowSettings>, window_settings: Option<WindowSettings>,
) { ) {
crate::profile_function!(); profiling::function_scope!();
if let Some(window_settings) = window_settings { if let Some(window_settings) = window_settings {
window_settings.initialize_window(window); window_settings.initialize_window(window);
} }
@ -103,12 +102,11 @@ pub fn apply_window_settings(
#[cfg(not(target_os = "ios"))] #[cfg(not(target_os = "ios"))]
fn largest_monitor_point_size(egui_zoom_factor: f32, event_loop: &ActiveEventLoop) -> egui::Vec2 { fn largest_monitor_point_size(egui_zoom_factor: f32, event_loop: &ActiveEventLoop) -> egui::Vec2 {
crate::profile_function!(); profiling::function_scope!();
let mut max_size = egui::Vec2::ZERO; let mut max_size = egui::Vec2::ZERO;
let available_monitors = { let available_monitors = {
crate::profile_scope!("available_monitors"); profiling::scope!("available_monitors");
event_loop.available_monitors() event_loop.available_monitors()
}; };
@ -238,7 +236,7 @@ impl EpiIntegration {
egui_winit: &mut egui_winit::State, egui_winit: &mut egui_winit::State,
event: &winit::event::WindowEvent, event: &winit::event::WindowEvent,
) -> EventResponse { ) -> EventResponse {
crate::profile_function!(egui_winit::short_window_event_description(event)); profiling::function_scope!(egui_winit::short_window_event_description(event));
use winit::event::{ElementState, MouseButton, WindowEvent}; use winit::event::{ElementState, MouseButton, WindowEvent};
@ -276,10 +274,10 @@ impl EpiIntegration {
let full_output = self.egui_ctx.run(raw_input, |egui_ctx| { let full_output = self.egui_ctx.run(raw_input, |egui_ctx| {
if let Some(viewport_ui_cb) = viewport_ui_cb { if let Some(viewport_ui_cb) = viewport_ui_cb {
// Child viewport // Child viewport
crate::profile_scope!("viewport_callback"); profiling::scope!("viewport_callback");
viewport_ui_cb(egui_ctx); viewport_ui_cb(egui_ctx);
} else { } else {
crate::profile_scope!("App::update"); profiling::scope!("App::update");
app.update(egui_ctx, &mut self.frame); app.update(egui_ctx, &mut self.frame);
} }
}); });
@ -306,7 +304,7 @@ impl EpiIntegration {
} }
pub fn post_rendering(&mut self, window: &winit::window::Window) { pub fn post_rendering(&mut self, window: &winit::window::Window) {
crate::profile_function!(); profiling::function_scope!();
if std::mem::take(&mut self.is_first_frame) { if std::mem::take(&mut self.is_first_frame) {
// We keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279 // We keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279
window.set_visible(true); window.set_visible(true);
@ -332,11 +330,11 @@ impl EpiIntegration {
pub fn save(&mut self, _app: &mut dyn epi::App, _window: Option<&winit::window::Window>) { pub fn save(&mut self, _app: &mut dyn epi::App, _window: Option<&winit::window::Window>) {
#[cfg(feature = "persistence")] #[cfg(feature = "persistence")]
if let Some(storage) = self.frame.storage_mut() { if let Some(storage) = self.frame.storage_mut() {
crate::profile_function!(); profiling::function_scope!();
if let Some(window) = _window { if let Some(window) = _window {
if self.persist_window { if self.persist_window {
crate::profile_scope!("native_window"); profiling::scope!("native_window");
epi::set_value( epi::set_value(
storage, storage,
STORAGE_WINDOW_KEY, STORAGE_WINDOW_KEY,
@ -345,23 +343,23 @@ impl EpiIntegration {
} }
} }
if _app.persist_egui_memory() { if _app.persist_egui_memory() {
crate::profile_scope!("egui_memory"); profiling::scope!("egui_memory");
self.egui_ctx self.egui_ctx
.memory(|mem| epi::set_value(storage, STORAGE_EGUI_MEMORY_KEY, mem)); .memory(|mem| epi::set_value(storage, STORAGE_EGUI_MEMORY_KEY, mem));
} }
{ {
crate::profile_scope!("App::save"); profiling::scope!("App::save");
_app.save(storage); _app.save(storage);
} }
crate::profile_scope!("Storage::flush"); profiling::scope!("Storage::flush");
storage.flush(); storage.flush();
} }
} }
} }
fn load_default_egui_icon() -> egui::IconData { fn load_default_egui_icon() -> egui::IconData {
crate::profile_function!(); profiling::function_scope!();
crate::icon_data::from_png_bytes(&include_bytes!("../../data/icon.png")[..]).unwrap() crate::icon_data::from_png_bytes(&include_bytes!("../../data/icon.png")[..]).unwrap()
} }
@ -372,7 +370,7 @@ const STORAGE_EGUI_MEMORY_KEY: &str = "egui";
const STORAGE_WINDOW_KEY: &str = "window"; const STORAGE_WINDOW_KEY: &str = "window";
pub fn load_window_settings(_storage: Option<&dyn epi::Storage>) -> Option<WindowSettings> { pub fn load_window_settings(_storage: Option<&dyn epi::Storage>) -> Option<WindowSettings> {
crate::profile_function!(); profiling::function_scope!();
#[cfg(feature = "persistence")] #[cfg(feature = "persistence")]
{ {
epi::get_value(_storage?, STORAGE_WINDOW_KEY) epi::get_value(_storage?, STORAGE_WINDOW_KEY)
@ -382,7 +380,7 @@ pub fn load_window_settings(_storage: Option<&dyn epi::Storage>) -> Option<Windo
} }
pub fn load_egui_memory(_storage: Option<&dyn epi::Storage>) -> Option<egui::Memory> { pub fn load_egui_memory(_storage: Option<&dyn epi::Storage>) -> Option<egui::Memory> {
crate::profile_function!(); profiling::function_scope!();
#[cfg(feature = "persistence")] #[cfg(feature = "persistence")]
{ {
epi::get_value(_storage?, STORAGE_EGUI_MEMORY_KEY) epi::get_value(_storage?, STORAGE_EGUI_MEMORY_KEY)

View File

@ -100,7 +100,7 @@ pub struct FileStorage {
impl Drop for FileStorage { impl Drop for FileStorage {
fn drop(&mut self) { fn drop(&mut self) {
if let Some(join_handle) = self.last_save_join_handle.take() { if let Some(join_handle) = self.last_save_join_handle.take() {
crate::profile_scope!("wait_for_save"); profiling::scope!("wait_for_save");
join_handle.join().ok(); join_handle.join().ok();
} }
} }
@ -109,7 +109,7 @@ impl Drop for FileStorage {
impl FileStorage { impl FileStorage {
/// Store the state in this .ron file. /// Store the state in this .ron file.
pub(crate) fn from_ron_filepath(ron_filepath: impl Into<PathBuf>) -> Self { pub(crate) fn from_ron_filepath(ron_filepath: impl Into<PathBuf>) -> Self {
crate::profile_function!(); profiling::function_scope!();
let ron_filepath: PathBuf = ron_filepath.into(); let ron_filepath: PathBuf = ron_filepath.into();
log::debug!("Loading app state from {:?}…", ron_filepath); log::debug!("Loading app state from {:?}…", ron_filepath);
Self { Self {
@ -122,7 +122,7 @@ impl FileStorage {
/// Find a good place to put the files that the OS likes. /// Find a good place to put the files that the OS likes.
pub fn from_app_id(app_id: &str) -> Option<Self> { pub fn from_app_id(app_id: &str) -> Option<Self> {
crate::profile_function!(app_id); profiling::function_scope!();
if let Some(data_dir) = storage_dir(app_id) { if let Some(data_dir) = storage_dir(app_id) {
if let Err(err) = std::fs::create_dir_all(&data_dir) { if let Err(err) = std::fs::create_dir_all(&data_dir) {
log::warn!( log::warn!(
@ -155,7 +155,7 @@ impl crate::Storage for FileStorage {
fn flush(&mut self) { fn flush(&mut self) {
if self.dirty { if self.dirty {
crate::profile_function!(); profiling::scope!("FileStorage::flush");
self.dirty = false; self.dirty = false;
let file_path = self.ron_filepath.clone(); let file_path = self.ron_filepath.clone();
@ -184,7 +184,7 @@ impl crate::Storage for FileStorage {
} }
fn save_to_disk(file_path: &PathBuf, kv: &HashMap<String, String>) { fn save_to_disk(file_path: &PathBuf, kv: &HashMap<String, String>) {
crate::profile_function!(); profiling::function_scope!();
if let Some(parent_dir) = file_path.parent() { if let Some(parent_dir) = file_path.parent() {
if !parent_dir.exists() { if !parent_dir.exists() {
@ -199,7 +199,7 @@ fn save_to_disk(file_path: &PathBuf, kv: &HashMap<String, String>) {
let mut writer = std::io::BufWriter::new(file); let mut writer = std::io::BufWriter::new(file);
let config = Default::default(); let config = Default::default();
crate::profile_scope!("ron::serialize"); profiling::scope!("ron::serialize");
if let Err(err) = ron::ser::to_writer_pretty(&mut writer, &kv, config) if let Err(err) = ron::ser::to_writer_pretty(&mut writer, &kv, config)
.and_then(|_| writer.flush().map_err(|err| err.into())) .and_then(|_| writer.flush().map_err(|err| err.into()))
{ {
@ -220,7 +220,7 @@ fn read_ron<T>(ron_path: impl AsRef<Path>) -> Option<T>
where where
T: serde::de::DeserializeOwned, T: serde::de::DeserializeOwned,
{ {
crate::profile_function!(); profiling::function_scope!();
match std::fs::File::open(ron_path) { match std::fs::File::open(ron_path) {
Ok(file) => { Ok(file) => {
let reader = std::io::BufReader::new(file); let reader = std::io::BufReader::new(file);

View File

@ -129,7 +129,7 @@ impl<'app> GlowWinitApp<'app> {
native_options: NativeOptions, native_options: NativeOptions,
app_creator: AppCreator<'app>, app_creator: AppCreator<'app>,
) -> Self { ) -> Self {
crate::profile_function!(); profiling::function_scope!();
Self { Self {
repaint_proxy: Arc::new(egui::mutex::Mutex::new(event_loop.create_proxy())), repaint_proxy: Arc::new(egui::mutex::Mutex::new(event_loop.create_proxy())),
app_name: app_name.to_owned(), app_name: app_name.to_owned(),
@ -146,8 +146,7 @@ impl<'app> GlowWinitApp<'app> {
storage: Option<&dyn Storage>, storage: Option<&dyn Storage>,
native_options: &mut NativeOptions, native_options: &mut NativeOptions,
) -> Result<(GlutinWindowContext, egui_glow::Painter)> { ) -> Result<(GlutinWindowContext, egui_glow::Painter)> {
crate::profile_function!(); profiling::function_scope!();
let window_settings = epi_integration::load_window_settings(storage); let window_settings = epi_integration::load_window_settings(storage);
let winit_window_builder = epi_integration::viewport_builder( let winit_window_builder = epi_integration::viewport_builder(
@ -172,7 +171,7 @@ impl<'app> GlowWinitApp<'app> {
} }
let gl = unsafe { let gl = unsafe {
crate::profile_scope!("glow::Context::from_loader_function"); profiling::scope!("glow::Context::from_loader_function");
Arc::new(glow::Context::from_loader_function(|s| { Arc::new(glow::Context::from_loader_function(|s| {
let s = std::ffi::CString::new(s) let s = std::ffi::CString::new(s)
.expect("failed to construct C string from string for gl proc address"); .expect("failed to construct C string from string for gl proc address");
@ -195,7 +194,7 @@ impl<'app> GlowWinitApp<'app> {
&mut self, &mut self,
event_loop: &ActiveEventLoop, event_loop: &ActiveEventLoop,
) -> Result<&mut GlowWinitRunning<'app>> { ) -> Result<&mut GlowWinitRunning<'app>> {
crate::profile_function!(); profiling::function_scope!();
let storage = if let Some(file) = &self.native_options.persistence_path { let storage = if let Some(file) = &self.native_options.persistence_path {
epi_integration::create_storage_with_file(file) epi_integration::create_storage_with_file(file)
@ -308,7 +307,7 @@ impl<'app> GlowWinitApp<'app> {
raw_display_handle: window.display_handle().map(|h| h.as_raw()), raw_display_handle: window.display_handle().map(|h| h.as_raw()),
raw_window_handle: window.window_handle().map(|h| h.as_raw()), raw_window_handle: window.window_handle().map(|h| h.as_raw()),
}; };
crate::profile_scope!("app_creator"); profiling::scope!("app_creator");
app_creator(&cc).map_err(crate::Error::AppCreation)? app_creator(&cc).map_err(crate::Error::AppCreation)?
}; };
@ -369,7 +368,7 @@ impl<'app> WinitApp for GlowWinitApp<'app> {
fn save_and_destroy(&mut self) { fn save_and_destroy(&mut self) {
if let Some(mut running) = self.running.take() { if let Some(mut running) = self.running.take() {
crate::profile_function!(); profiling::function_scope!();
running.integration.save( running.integration.save(
running.app.as_mut(), running.app.as_mut(),
@ -486,7 +485,7 @@ impl<'app> GlowWinitRunning<'app> {
event_loop: &ActiveEventLoop, event_loop: &ActiveEventLoop,
window_id: WindowId, window_id: WindowId,
) -> Result<EventResult> { ) -> Result<EventResult> {
crate::profile_function!(); profiling::function_scope!();
let Some(viewport_id) = self let Some(viewport_id) = self
.glutin .glutin
@ -498,8 +497,7 @@ impl<'app> GlowWinitRunning<'app> {
return Ok(EventResult::Wait); return Ok(EventResult::Wait);
}; };
#[cfg(feature = "puffin")] profiling::finish_frame!();
puffin::GlobalProfiler::lock().new_frame();
let mut frame_timer = crate::stopwatch::Stopwatch::new(); let mut frame_timer = crate::stopwatch::Stopwatch::new();
frame_timer.start(); frame_timer.start();
@ -698,7 +696,7 @@ impl<'app> GlowWinitRunning<'app> {
{ {
// vsync - don't count as frame-time: // vsync - don't count as frame-time:
frame_timer.pause(); frame_timer.pause();
crate::profile_scope!("swap_buffers"); profiling::scope!("swap_buffers");
let context = current_gl_context let context = current_gl_context
.as_ref() .as_ref()
.ok_or(egui_glow::PainterError::from( .ok_or(egui_glow::PainterError::from(
@ -726,7 +724,7 @@ impl<'app> GlowWinitRunning<'app> {
if window.is_minimized() == Some(true) { if window.is_minimized() == Some(true) {
// On Mac, a minimized Window uses up all CPU: // On Mac, a minimized Window uses up all CPU:
// https://github.com/emilk/egui/issues/325 // https://github.com/emilk/egui/issues/325
crate::profile_scope!("minimized_sleep"); profiling::scope!("minimized_sleep");
std::thread::sleep(std::time::Duration::from_millis(10)); std::thread::sleep(std::time::Duration::from_millis(10));
} }
@ -857,7 +855,7 @@ fn change_gl_context(
not_current_gl_context: &mut Option<glutin::context::NotCurrentContext>, not_current_gl_context: &mut Option<glutin::context::NotCurrentContext>,
gl_surface: &glutin::surface::Surface<glutin::surface::WindowSurface>, gl_surface: &glutin::surface::Surface<glutin::surface::WindowSurface>,
) { ) {
crate::profile_function!(); profiling::function_scope!();
if !cfg!(target_os = "windows") { if !cfg!(target_os = "windows") {
// According to https://github.com/emilk/egui/issues/4289 // According to https://github.com/emilk/egui/issues/4289
@ -866,7 +864,7 @@ fn change_gl_context(
// See https://github.com/emilk/egui/issues/4173 // See https://github.com/emilk/egui/issues/4173
if let Some(current_gl_context) = current_gl_context { if let Some(current_gl_context) = current_gl_context {
crate::profile_scope!("is_current"); profiling::scope!("is_current");
if gl_surface.is_current(current_gl_context) { if gl_surface.is_current(current_gl_context) {
return; // Early-out to save a lot of time. return; // Early-out to save a lot of time.
} }
@ -876,7 +874,7 @@ fn change_gl_context(
let not_current = if let Some(not_current_context) = not_current_gl_context.take() { let not_current = if let Some(not_current_context) = not_current_gl_context.take() {
not_current_context not_current_context
} else { } else {
crate::profile_scope!("make_not_current"); profiling::scope!("make_not_current");
current_gl_context current_gl_context
.take() .take()
.unwrap() .unwrap()
@ -884,7 +882,7 @@ fn change_gl_context(
.unwrap() .unwrap()
}; };
crate::profile_scope!("make_current"); profiling::scope!("make_current");
*current_gl_context = Some(not_current.make_current(gl_surface).unwrap()); *current_gl_context = Some(not_current.make_current(gl_surface).unwrap());
} }
@ -896,7 +894,7 @@ impl GlutinWindowContext {
native_options: &NativeOptions, native_options: &NativeOptions,
event_loop: &ActiveEventLoop, event_loop: &ActiveEventLoop,
) -> Result<Self> { ) -> Result<Self> {
crate::profile_function!(); profiling::function_scope!();
// There is a lot of complexity with opengl creation, // There is a lot of complexity with opengl creation,
// so prefer extensive logging to get all the help we can to debug issues. // so prefer extensive logging to get all the help we can to debug issues.
@ -952,7 +950,7 @@ impl GlutinWindowContext {
))); )));
let (window, gl_config) = { let (window, gl_config) = {
crate::profile_scope!("DisplayBuilder::build"); profiling::scope!("DisplayBuilder::build");
display_builder display_builder
.build( .build(
@ -995,7 +993,7 @@ impl GlutinWindowContext {
.build(glutin_raw_window_handle); .build(glutin_raw_window_handle);
let gl_context_result = unsafe { let gl_context_result = unsafe {
crate::profile_scope!("create_context"); profiling::scope!("create_context");
gl_config gl_config
.display() .display()
.create_context(&gl_config, &context_attributes) .create_context(&gl_config, &context_attributes)
@ -1070,7 +1068,7 @@ impl GlutinWindowContext {
/// ///
/// Errors will be logged. /// Errors will be logged.
fn initialize_all_windows(&mut self, event_loop: &ActiveEventLoop) { fn initialize_all_windows(&mut self, event_loop: &ActiveEventLoop) {
crate::profile_function!(); profiling::function_scope!();
let viewports: Vec<ViewportId> = self.viewports.keys().copied().collect(); let viewports: Vec<ViewportId> = self.viewports.keys().copied().collect();
@ -1088,7 +1086,7 @@ impl GlutinWindowContext {
viewport_id: ViewportId, viewport_id: ViewportId,
event_loop: &ActiveEventLoop, event_loop: &ActiveEventLoop,
) -> Result { ) -> Result {
crate::profile_function!(); profiling::function_scope!();
let viewport = self let viewport = self
.viewports .viewports
@ -1268,7 +1266,7 @@ impl GlutinWindowContext {
egui_ctx: &egui::Context, egui_ctx: &egui::Context,
viewport_output: &ViewportIdMap<ViewportOutput>, viewport_output: &ViewportIdMap<ViewportOutput>,
) { ) {
crate::profile_function!(); profiling::function_scope!();
for ( for (
viewport_id, viewport_id,
@ -1329,7 +1327,7 @@ fn initialize_or_update_viewport(
mut builder: ViewportBuilder, mut builder: ViewportBuilder,
viewport_ui_cb: Option<Arc<dyn Fn(&egui::Context) + Send + Sync>>, viewport_ui_cb: Option<Arc<dyn Fn(&egui::Context) + Send + Sync>>,
) -> &mut Viewport { ) -> &mut Viewport {
crate::profile_function!(); profiling::function_scope!();
if builder.icon.is_none() { if builder.icon.is_none() {
// Inherit icon from parent // Inherit icon from parent
@ -1393,7 +1391,7 @@ fn render_immediate_viewport(
beginning: Instant, beginning: Instant,
immediate_viewport: ImmediateViewport<'_>, immediate_viewport: ImmediateViewport<'_>,
) { ) {
crate::profile_function!(); profiling::function_scope!();
let ImmediateViewport { let ImmediateViewport {
ids, ids,
@ -1516,7 +1514,7 @@ fn render_immediate_viewport(
); );
{ {
crate::profile_scope!("swap_buffers"); profiling::scope!("swap_buffers");
if let Err(err) = gl_surface.swap_buffers(current_gl_context) { if let Err(err) = gl_surface.swap_buffers(current_gl_context) {
log::error!("swap_buffers failed: {err}"); log::error!("swap_buffers failed: {err}");
} }

View File

@ -20,7 +20,7 @@ fn create_event_loop(native_options: &mut epi::NativeOptions) -> Result<EventLoo
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
use winit::platform::android::EventLoopBuilderExtAndroid as _; use winit::platform::android::EventLoopBuilderExtAndroid as _;
crate::profile_function!(); profiling::function_scope!();
let mut builder = winit::event_loop::EventLoop::with_user_event(); let mut builder = winit::event_loop::EventLoop::with_user_event();
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
@ -35,7 +35,7 @@ fn create_event_loop(native_options: &mut epi::NativeOptions) -> Result<EventLoo
hook(&mut builder); hook(&mut builder);
} }
crate::profile_scope!("EventLoopBuilder::build"); profiling::scope!("EventLoopBuilder::build");
Ok(builder.build()?) Ok(builder.build()?)
} }
@ -186,7 +186,7 @@ impl<T: WinitApp> WinitAppWrapper<T> {
impl<T: WinitApp> ApplicationHandler<UserEvent> for WinitAppWrapper<T> { impl<T: WinitApp> ApplicationHandler<UserEvent> for WinitAppWrapper<T> {
fn suspended(&mut self, event_loop: &ActiveEventLoop) { fn suspended(&mut self, event_loop: &ActiveEventLoop) {
crate::profile_function!("Event::Suspended"); profiling::scope!("Event::Suspended");
event_loop_context::with_event_loop_context(event_loop, move || { event_loop_context::with_event_loop_context(event_loop, move || {
let event_result = self.winit_app.suspended(event_loop); let event_result = self.winit_app.suspended(event_loop);
@ -195,7 +195,7 @@ impl<T: WinitApp> ApplicationHandler<UserEvent> for WinitAppWrapper<T> {
} }
fn resumed(&mut self, event_loop: &ActiveEventLoop) { fn resumed(&mut self, event_loop: &ActiveEventLoop) {
crate::profile_function!("Event::Resumed"); profiling::scope!("Event::Resumed");
// Nb: Make sure this guard is dropped after this function returns. // Nb: Make sure this guard is dropped after this function returns.
event_loop_context::with_event_loop_context(event_loop, move || { event_loop_context::with_event_loop_context(event_loop, move || {
@ -219,7 +219,7 @@ impl<T: WinitApp> ApplicationHandler<UserEvent> for WinitAppWrapper<T> {
device_id: winit::event::DeviceId, device_id: winit::event::DeviceId,
event: winit::event::DeviceEvent, event: winit::event::DeviceEvent,
) { ) {
crate::profile_function!(egui_winit::short_device_event_description(&event)); profiling::function_scope!(egui_winit::short_device_event_description(&event));
// Nb: Make sure this guard is dropped after this function returns. // Nb: Make sure this guard is dropped after this function returns.
event_loop_context::with_event_loop_context(event_loop, move || { event_loop_context::with_event_loop_context(event_loop, move || {
@ -229,7 +229,7 @@ impl<T: WinitApp> ApplicationHandler<UserEvent> for WinitAppWrapper<T> {
} }
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) { fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) {
crate::profile_function!(match &event { profiling::function_scope!(match &event {
UserEvent::RequestRepaint { .. } => "UserEvent::RequestRepaint", UserEvent::RequestRepaint { .. } => "UserEvent::RequestRepaint",
#[cfg(feature = "accesskit")] #[cfg(feature = "accesskit")]
UserEvent::AccessKitActionRequest(_) => "UserEvent::AccessKitActionRequest", UserEvent::AccessKitActionRequest(_) => "UserEvent::AccessKitActionRequest",
@ -285,7 +285,7 @@ impl<T: WinitApp> ApplicationHandler<UserEvent> for WinitAppWrapper<T> {
window_id: WindowId, window_id: WindowId,
event: winit::event::WindowEvent, event: winit::event::WindowEvent,
) { ) {
crate::profile_function!(egui_winit::short_window_event_description(&event)); profiling::function_scope!(egui_winit::short_window_event_description(&event));
// Nb: Make sure this guard is dropped after this function returns. // Nb: Make sure this guard is dropped after this function returns.
event_loop_context::with_event_loop_context(event_loop, move || { event_loop_context::with_event_loop_context(event_loop, move || {

View File

@ -102,7 +102,7 @@ impl<'app> WgpuWinitApp<'app> {
native_options: NativeOptions, native_options: NativeOptions,
app_creator: AppCreator<'app>, app_creator: AppCreator<'app>,
) -> Self { ) -> Self {
crate::profile_function!(); profiling::function_scope!();
#[cfg(feature = "__screenshot")] #[cfg(feature = "__screenshot")]
assert!( assert!(
@ -181,8 +181,7 @@ impl<'app> WgpuWinitApp<'app> {
window: Window, window: Window,
builder: ViewportBuilder, builder: ViewportBuilder,
) -> crate::Result<&mut WgpuWinitRunning<'app>> { ) -> crate::Result<&mut WgpuWinitRunning<'app>> {
crate::profile_function!(); profiling::function_scope!();
#[allow(unsafe_code, unused_mut, unused_unsafe)] #[allow(unsafe_code, unused_mut, unused_unsafe)]
let mut painter = egui_wgpu::winit::Painter::new( let mut painter = egui_wgpu::winit::Painter::new(
egui_ctx.clone(), egui_ctx.clone(),
@ -199,7 +198,7 @@ impl<'app> WgpuWinitApp<'app> {
let window = Arc::new(window); let window = Arc::new(window);
{ {
crate::profile_scope!("set_window"); profiling::scope!("set_window");
pollster::block_on(painter.set_window(ViewportId::ROOT, Some(window.clone())))?; pollster::block_on(painter.set_window(ViewportId::ROOT, Some(window.clone())))?;
} }
@ -268,7 +267,7 @@ impl<'app> WgpuWinitApp<'app> {
raw_window_handle: window.window_handle().map(|h| h.as_raw()), raw_window_handle: window.window_handle().map(|h| h.as_raw()),
}; };
let app = { let app = {
crate::profile_scope!("user_app_creator"); profiling::scope!("user_app_creator");
app_creator(&cc).map_err(crate::Error::AppCreation)? app_creator(&cc).map_err(crate::Error::AppCreation)?
}; };
@ -490,7 +489,7 @@ impl<'app> WinitApp for WgpuWinitApp<'app> {
impl<'app> WgpuWinitRunning<'app> { impl<'app> WgpuWinitRunning<'app> {
fn save_and_destroy(&mut self) { fn save_and_destroy(&mut self) {
crate::profile_function!(); profiling::function_scope!();
let mut shared = self.shared.borrow_mut(); let mut shared = self.shared.borrow_mut();
if let Some(Viewport { window, .. }) = shared.viewports.get(&ViewportId::ROOT) { if let Some(Viewport { window, .. }) = shared.viewports.get(&ViewportId::ROOT) {
@ -508,7 +507,7 @@ impl<'app> WgpuWinitRunning<'app> {
/// This is called both for the root viewport, and all deferred viewports /// This is called both for the root viewport, and all deferred viewports
fn run_ui_and_paint(&mut self, window_id: WindowId) -> Result<EventResult> { fn run_ui_and_paint(&mut self, window_id: WindowId) -> Result<EventResult> {
crate::profile_function!(); profiling::function_scope!();
let Some(viewport_id) = self let Some(viewport_id) = self
.shared .shared
@ -520,8 +519,7 @@ impl<'app> WgpuWinitRunning<'app> {
return Ok(EventResult::Wait); return Ok(EventResult::Wait);
}; };
#[cfg(feature = "puffin")] profiling::finish_frame!();
puffin::GlobalProfiler::lock().new_frame();
let Self { let Self {
app, app,
@ -533,7 +531,7 @@ impl<'app> WgpuWinitRunning<'app> {
frame_timer.start(); frame_timer.start();
let (viewport_ui_cb, raw_input) = { let (viewport_ui_cb, raw_input) = {
crate::profile_scope!("Prepare"); profiling::scope!("Prepare");
let mut shared_lock = shared.borrow_mut(); let mut shared_lock = shared.borrow_mut();
let SharedState { let SharedState {
@ -577,7 +575,7 @@ impl<'app> WgpuWinitRunning<'app> {
egui_winit::update_viewport_info(info, &integration.egui_ctx, window, false); egui_winit::update_viewport_info(info, &integration.egui_ctx, window, false);
{ {
crate::profile_scope!("set_window"); profiling::scope!("set_window");
pollster::block_on(painter.set_window(viewport_id, Some(window.clone())))?; pollster::block_on(painter.set_window(viewport_id, Some(window.clone())))?;
} }
@ -719,7 +717,7 @@ impl<'app> WgpuWinitRunning<'app> {
if window.is_minimized() == Some(true) { if window.is_minimized() == Some(true) {
// On Mac, a minimized Window uses up all CPU: // On Mac, a minimized Window uses up all CPU:
// https://github.com/emilk/egui/issues/325 // https://github.com/emilk/egui/issues/325
crate::profile_scope!("minimized_sleep"); profiling::scope!("minimized_sleep");
std::thread::sleep(std::time::Duration::from_millis(10)); std::thread::sleep(std::time::Duration::from_millis(10));
} }
} }
@ -846,7 +844,7 @@ impl Viewport {
return; // we already have one return; // we already have one
} }
crate::profile_function!(); profiling::function_scope!();
let viewport_id = self.ids.this; let viewport_id = self.ids.this;
@ -887,7 +885,7 @@ fn create_window(
storage: Option<&dyn Storage>, storage: Option<&dyn Storage>,
native_options: &mut NativeOptions, native_options: &mut NativeOptions,
) -> Result<(Window, ViewportBuilder), winit::error::OsError> { ) -> Result<(Window, ViewportBuilder), winit::error::OsError> {
crate::profile_function!(); profiling::function_scope!();
let window_settings = epi_integration::load_window_settings(storage); let window_settings = epi_integration::load_window_settings(storage);
let viewport_builder = epi_integration::viewport_builder( let viewport_builder = epi_integration::viewport_builder(
@ -908,7 +906,7 @@ fn render_immediate_viewport(
shared: &RefCell<SharedState>, shared: &RefCell<SharedState>,
immediate_viewport: ImmediateViewport<'_>, immediate_viewport: ImmediateViewport<'_>,
) { ) {
crate::profile_function!(); profiling::function_scope!();
let ImmediateViewport { let ImmediateViewport {
ids, ids,
@ -988,7 +986,7 @@ fn render_immediate_viewport(
}; };
{ {
crate::profile_scope!("set_window"); profiling::scope!("set_window");
if let Err(err) = pollster::block_on(painter.set_window(ids.this, Some(window.clone()))) { if let Err(err) = pollster::block_on(painter.set_window(ids.this, Some(window.clone()))) {
log::error!( log::error!(
"when rendering viewport_id={:?}, set_window Error {err}", "when rendering viewport_id={:?}, set_window Error {err}",
@ -1096,7 +1094,7 @@ fn initialize_or_update_viewport<'a>(
viewport_ui_cb: Option<Arc<dyn Fn(&egui::Context) + Send + Sync>>, viewport_ui_cb: Option<Arc<dyn Fn(&egui::Context) + Send + Sync>>,
painter: &mut egui_wgpu::winit::Painter, painter: &mut egui_wgpu::winit::Painter,
) -> &'a mut Viewport { ) -> &'a mut Viewport {
crate::profile_function!(); profiling::function_scope!();
if builder.icon.is_none() { if builder.icon.is_none() {
// Inherit icon from parent // Inherit icon from parent

View File

@ -11,7 +11,7 @@ use egui_winit::accesskit_winit;
/// Create an egui context, restoring it from storage if possible. /// Create an egui context, restoring it from storage if possible.
pub fn create_egui_context(storage: Option<&dyn crate::Storage>) -> egui::Context { pub fn create_egui_context(storage: Option<&dyn crate::Storage>) -> egui::Context {
crate::profile_function!(); profiling::function_scope!();
pub const IS_DESKTOP: bool = cfg!(any( pub const IS_DESKTOP: bool = cfg!(any(
target_os = "freebsd", target_os = "freebsd",

View File

@ -33,9 +33,6 @@ rustdoc-args = ["--generate-link-to-definition"]
[features] [features]
default = ["fragile-send-sync-non-atomic-wasm"] default = ["fragile-send-sync-non-atomic-wasm"]
## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate.
puffin = ["dep:puffin"]
## Enable [`winit`](https://docs.rs/winit) integration. On Linux, requires either `wayland` or `x11` ## Enable [`winit`](https://docs.rs/winit) integration. On Linux, requires either `wayland` or `x11`
winit = ["dep:winit", "winit/rwh_06"] winit = ["dep:winit", "winit/rwh_06"]
@ -60,6 +57,7 @@ ahash.workspace = true
bytemuck.workspace = true bytemuck.workspace = true
document-features.workspace = true document-features.workspace = true
log.workspace = true log.workspace = true
profiling.workspace = true
thiserror.workspace = true thiserror.workspace = true
type-map.workspace = true type-map.workspace = true
web-time.workspace = true web-time.workspace = true
@ -68,7 +66,3 @@ wgpu = { workspace = true, features = ["wgsl"] }
# Optional dependencies: # Optional dependencies:
winit = { workspace = true, optional = true, default-features = false } winit = { workspace = true, optional = true, default-features = false }
# Native:
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
puffin = { workspace = true, optional = true }

View File

@ -96,7 +96,7 @@ impl RenderState {
msaa_samples: u32, msaa_samples: u32,
dithering: bool, dithering: bool,
) -> Result<Self, WgpuError> { ) -> Result<Self, WgpuError> {
crate::profile_scope!("RenderState::create"); // async yield give bad names using `profile_function` profiling::scope!("RenderState::create"); // async yield give bad names using `profile_function`
// This is always an empty list on web. // This is always an empty list on web.
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -109,7 +109,7 @@ impl RenderState {
device_descriptor, device_descriptor,
} => { } => {
let adapter = { let adapter = {
crate::profile_scope!("request_adapter"); profiling::scope!("request_adapter");
instance instance
.request_adapter(&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference, power_preference,
@ -164,7 +164,7 @@ impl RenderState {
let trace_path = std::env::var("WGPU_TRACE"); let trace_path = std::env::var("WGPU_TRACE");
let (device, queue) = { let (device, queue) = {
crate::profile_scope!("request_device"); profiling::scope!("request_device");
adapter adapter
.request_device( .request_device(
&(*device_descriptor)(&adapter), &(*device_descriptor)(&adapter),
@ -187,7 +187,7 @@ impl RenderState {
}; };
let capabilities = { let capabilities = {
crate::profile_scope!("get_capabilities"); profiling::scope!("get_capabilities");
surface.get_capabilities(&adapter).formats surface.get_capabilities(&adapter).formats
}; };
let target_format = crate::preferred_framebuffer_format(&capabilities)?; let target_format = crate::preferred_framebuffer_format(&capabilities)?;
@ -474,33 +474,3 @@ pub fn adapter_info_summary(info: &wgpu::AdapterInfo) -> String {
summary summary
} }
// ---------------------------------------------------------------------------
mod profiling_scopes {
#![allow(unused_macros)]
#![allow(unused_imports)]
/// Profiling macro for feature "puffin"
macro_rules! profile_function {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
puffin::profile_function!($($arg)*);
};
}
pub(crate) use profile_function;
/// Profiling macro for feature "puffin"
macro_rules! profile_scope {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
puffin::profile_scope!($($arg)*);
};
}
pub(crate) use profile_scope;
}
#[allow(unused_imports)]
pub(crate) use profiling_scopes::{profile_function, profile_scope};

View File

@ -214,14 +214,14 @@ impl Renderer {
msaa_samples: u32, msaa_samples: u32,
dithering: bool, dithering: bool,
) -> Self { ) -> Self {
crate::profile_function!(); profiling::function_scope!();
let shader = wgpu::ShaderModuleDescriptor { let shader = wgpu::ShaderModuleDescriptor {
label: Some("egui"), label: Some("egui"),
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("egui.wgsl"))), source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("egui.wgsl"))),
}; };
let module = { let module = {
crate::profile_scope!("create_shader_module"); profiling::scope!("create_shader_module");
device.create_shader_module(shader) device.create_shader_module(shader)
}; };
@ -236,7 +236,7 @@ impl Renderer {
}); });
let uniform_bind_group_layout = { let uniform_bind_group_layout = {
crate::profile_scope!("create_bind_group_layout"); profiling::scope!("create_bind_group_layout");
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("egui_uniform_bind_group_layout"), label: Some("egui_uniform_bind_group_layout"),
entries: &[wgpu::BindGroupLayoutEntry { entries: &[wgpu::BindGroupLayoutEntry {
@ -253,7 +253,7 @@ impl Renderer {
}; };
let uniform_bind_group = { let uniform_bind_group = {
crate::profile_scope!("create_bind_group"); profiling::scope!("create_bind_group");
device.create_bind_group(&wgpu::BindGroupDescriptor { device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("egui_uniform_bind_group"), label: Some("egui_uniform_bind_group"),
layout: &uniform_bind_group_layout, layout: &uniform_bind_group_layout,
@ -269,7 +269,7 @@ impl Renderer {
}; };
let texture_bind_group_layout = { let texture_bind_group_layout = {
crate::profile_scope!("create_bind_group_layout"); profiling::scope!("create_bind_group_layout");
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("egui_texture_bind_group_layout"), label: Some("egui_texture_bind_group_layout"),
entries: &[ entries: &[
@ -308,7 +308,7 @@ impl Renderer {
}); });
let pipeline = { let pipeline = {
crate::profile_scope!("create_render_pipeline"); profiling::scope!("create_render_pipeline");
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("egui_pipeline"), label: Some("egui_pipeline"),
layout: Some(&pipeline_layout), layout: Some(&pipeline_layout),
@ -420,7 +420,7 @@ impl Renderer {
paint_jobs: &[epaint::ClippedPrimitive], paint_jobs: &[epaint::ClippedPrimitive],
screen_descriptor: &ScreenDescriptor, screen_descriptor: &ScreenDescriptor,
) { ) {
crate::profile_function!(); profiling::function_scope!();
let pixels_per_point = screen_descriptor.pixels_per_point; let pixels_per_point = screen_descriptor.pixels_per_point;
let size_in_pixels = screen_descriptor.size_in_pixels; let size_in_pixels = screen_descriptor.size_in_pixels;
@ -506,7 +506,7 @@ impl Renderer {
let viewport_px = info.viewport_in_pixels(); let viewport_px = info.viewport_in_pixels();
if viewport_px.width_px > 0 && viewport_px.height_px > 0 { if viewport_px.width_px > 0 && viewport_px.height_px > 0 {
crate::profile_scope!("callback"); profiling::scope!("callback");
needs_reset = true; needs_reset = true;
@ -544,7 +544,7 @@ impl Renderer {
id: epaint::TextureId, id: epaint::TextureId,
image_delta: &epaint::ImageDelta, image_delta: &epaint::ImageDelta,
) { ) {
crate::profile_function!(); profiling::function_scope!();
let width = image_delta.image.width() as u32; let width = image_delta.image.width() as u32;
let height = image_delta.image.height() as u32; let height = image_delta.image.height() as u32;
@ -570,14 +570,14 @@ impl Renderer {
image.pixels.len(), image.pixels.len(),
"Mismatch between texture size and texel count" "Mismatch between texture size and texel count"
); );
crate::profile_scope!("font -> sRGBA"); profiling::scope!("font -> sRGBA");
Cow::Owned(image.srgba_pixels(None).collect::<Vec<epaint::Color32>>()) Cow::Owned(image.srgba_pixels(None).collect::<Vec<epaint::Color32>>())
} }
}; };
let data_bytes: &[u8] = bytemuck::cast_slice(data_color32.as_slice()); let data_bytes: &[u8] = bytemuck::cast_slice(data_color32.as_slice());
let queue_write_data_to_texture = |texture, origin| { let queue_write_data_to_texture = |texture, origin| {
crate::profile_scope!("write_texture"); profiling::scope!("write_texture");
queue.write_texture( queue.write_texture(
wgpu::ImageCopyTexture { wgpu::ImageCopyTexture {
texture, texture,
@ -631,7 +631,7 @@ impl Renderer {
} else { } else {
// allocate a new texture // allocate a new texture
let texture = { let texture = {
crate::profile_scope!("create_texture"); profiling::scope!("create_texture");
device.create_texture(&wgpu::TextureDescriptor { device.create_texture(&wgpu::TextureDescriptor {
label, label,
size, size,
@ -756,7 +756,7 @@ impl Renderer {
texture: &wgpu::TextureView, texture: &wgpu::TextureView,
sampler_descriptor: wgpu::SamplerDescriptor<'_>, sampler_descriptor: wgpu::SamplerDescriptor<'_>,
) -> epaint::TextureId { ) -> epaint::TextureId {
crate::profile_function!(); profiling::function_scope!();
let sampler = device.create_sampler(&wgpu::SamplerDescriptor { let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
compare: None, compare: None,
@ -804,7 +804,7 @@ impl Renderer {
sampler_descriptor: wgpu::SamplerDescriptor<'_>, sampler_descriptor: wgpu::SamplerDescriptor<'_>,
id: epaint::TextureId, id: epaint::TextureId,
) { ) {
crate::profile_function!(); profiling::function_scope!();
let Texture { let Texture {
bind_group: user_texture_binding, bind_group: user_texture_binding,
@ -849,7 +849,7 @@ impl Renderer {
paint_jobs: &[epaint::ClippedPrimitive], paint_jobs: &[epaint::ClippedPrimitive],
screen_descriptor: &ScreenDescriptor, screen_descriptor: &ScreenDescriptor,
) -> Vec<wgpu::CommandBuffer> { ) -> Vec<wgpu::CommandBuffer> {
crate::profile_function!(); profiling::function_scope!();
let screen_size_in_points = screen_descriptor.screen_size_in_points(); let screen_size_in_points = screen_descriptor.screen_size_in_points();
@ -859,7 +859,7 @@ impl Renderer {
_padding: Default::default(), _padding: Default::default(),
}; };
if uniform_buffer_content != self.previous_uniform_buffer_content { if uniform_buffer_content != self.previous_uniform_buffer_content {
crate::profile_scope!("update uniforms"); profiling::scope!("update uniforms");
queue.write_buffer( queue.write_buffer(
&self.uniform_buffer, &self.uniform_buffer,
0, 0,
@ -871,7 +871,7 @@ impl Renderer {
// Determine how many vertices & indices need to be rendered, and gather prepare callbacks // Determine how many vertices & indices need to be rendered, and gather prepare callbacks
let mut callbacks = Vec::new(); let mut callbacks = Vec::new();
let (vertex_count, index_count) = { let (vertex_count, index_count) = {
crate::profile_scope!("count_vertices_indices"); profiling::scope!("count_vertices_indices");
paint_jobs.iter().fold((0, 0), |acc, clipped_primitive| { paint_jobs.iter().fold((0, 0), |acc, clipped_primitive| {
match &clipped_primitive.primitive { match &clipped_primitive.primitive {
Primitive::Mesh(mesh) => { Primitive::Mesh(mesh) => {
@ -890,7 +890,7 @@ impl Renderer {
}; };
if index_count > 0 { if index_count > 0 {
crate::profile_scope!("indices", index_count.to_string()); profiling::scope!("indices", index_count.to_string().as_str());
self.index_buffer.slices.clear(); self.index_buffer.slices.clear();
@ -928,7 +928,7 @@ impl Renderer {
} }
} }
if vertex_count > 0 { if vertex_count > 0 {
crate::profile_scope!("vertices", vertex_count.to_string()); profiling::scope!("vertices", vertex_count.to_string().as_str());
self.vertex_buffer.slices.clear(); self.vertex_buffer.slices.clear();
@ -969,7 +969,7 @@ impl Renderer {
let mut user_cmd_bufs = Vec::new(); let mut user_cmd_bufs = Vec::new();
{ {
crate::profile_scope!("prepare callbacks"); profiling::scope!("prepare callbacks");
for callback in &callbacks { for callback in &callbacks {
user_cmd_bufs.extend(callback.prepare( user_cmd_bufs.extend(callback.prepare(
device, device,
@ -981,7 +981,7 @@ impl Renderer {
} }
} }
{ {
crate::profile_scope!("finish prepare callbacks"); profiling::scope!("finish prepare callbacks");
for callback in &callbacks { for callback in &callbacks {
user_cmd_bufs.extend(callback.finish_prepare( user_cmd_bufs.extend(callback.finish_prepare(
device, device,
@ -1026,7 +1026,7 @@ fn create_sampler(
} }
fn create_vertex_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buffer { fn create_vertex_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buffer {
crate::profile_function!(); profiling::function_scope!();
device.create_buffer(&wgpu::BufferDescriptor { device.create_buffer(&wgpu::BufferDescriptor {
label: Some("egui_vertex_buffer"), label: Some("egui_vertex_buffer"),
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
@ -1036,7 +1036,7 @@ fn create_vertex_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buffer {
} }
fn create_index_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buffer { fn create_index_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buffer {
crate::profile_function!(); profiling::function_scope!();
device.create_buffer(&wgpu::BufferDescriptor { device.create_buffer(&wgpu::BufferDescriptor {
label: Some("egui_index_buffer"), label: Some("egui_index_buffer"),
usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST, usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,

View File

@ -104,7 +104,7 @@ impl Painter {
render_state: &RenderState, render_state: &RenderState,
config: &WgpuConfiguration, config: &WgpuConfiguration,
) { ) {
crate::profile_function!(); profiling::function_scope!();
let width = surface_state.width; let width = surface_state.width;
let height = surface_state.height; let height = surface_state.height;
@ -156,7 +156,7 @@ impl Painter {
viewport_id: ViewportId, viewport_id: ViewportId,
window: Option<Arc<winit::window::Window>>, window: Option<Arc<winit::window::Window>>,
) -> Result<(), crate::WgpuError> { ) -> Result<(), crate::WgpuError> {
crate::profile_scope!("Painter::set_window"); // profile_function gives bad names for async functions profiling::scope!("Painter::set_window"); // profile_function gives bad names for async functions
if let Some(window) = window { if let Some(window) = window {
let size = window.inner_size(); let size = window.inner_size();
@ -182,7 +182,7 @@ impl Painter {
viewport_id: ViewportId, viewport_id: ViewportId,
window: Option<&winit::window::Window>, window: Option<&winit::window::Window>,
) -> Result<(), crate::WgpuError> { ) -> Result<(), crate::WgpuError> {
crate::profile_scope!("Painter::set_window_unsafe"); // profile_function gives bad names for async functions profiling::scope!("Painter::set_window_unsafe"); // profile_function gives bad names for async functions
if let Some(window) = window { if let Some(window) = window {
let size = window.inner_size(); let size = window.inner_size();
@ -273,7 +273,7 @@ impl Painter {
width_in_pixels: NonZeroU32, width_in_pixels: NonZeroU32,
height_in_pixels: NonZeroU32, height_in_pixels: NonZeroU32,
) { ) {
crate::profile_function!(); profiling::function_scope!();
let width = width_in_pixels.get(); let width = width_in_pixels.get();
let height = height_in_pixels.get(); let height = height_in_pixels.get();
@ -344,7 +344,7 @@ impl Painter {
width_in_pixels: NonZeroU32, width_in_pixels: NonZeroU32,
height_in_pixels: NonZeroU32, height_in_pixels: NonZeroU32,
) { ) {
crate::profile_function!(); profiling::function_scope!();
if self.surfaces.contains_key(&viewport_id) { if self.surfaces.contains_key(&viewport_id) {
self.resize_and_generate_depth_texture_view_and_msaa_view( self.resize_and_generate_depth_texture_view_and_msaa_view(
@ -372,7 +372,7 @@ impl Painter {
textures_delta: &epaint::textures::TexturesDelta, textures_delta: &epaint::textures::TexturesDelta,
capture_data: Vec<UserData>, capture_data: Vec<UserData>,
) -> f32 { ) -> f32 {
crate::profile_function!(); profiling::function_scope!();
let capture = !capture_data.is_empty(); let capture = !capture_data.is_empty();
let mut vsync_sec = 0.0; let mut vsync_sec = 0.0;
@ -418,7 +418,7 @@ impl Painter {
}; };
let output_frame = { let output_frame = {
crate::profile_scope!("get_current_texture"); profiling::scope!("get_current_texture");
// This is what vsync-waiting happens on my Mac. // This is what vsync-waiting happens on my Mac.
let start = web_time::Instant::now(); let start = web_time::Instant::now();
let output_frame = surface_state.surface.get_current_texture(); let output_frame = surface_state.surface.get_current_texture();
@ -514,13 +514,13 @@ impl Painter {
} }
let encoded = { let encoded = {
crate::profile_scope!("CommandEncoder::finish"); profiling::scope!("CommandEncoder::finish");
encoder.finish() encoder.finish()
}; };
// Submit the commands: both the main buffer and user-defined ones. // Submit the commands: both the main buffer and user-defined ones.
{ {
crate::profile_scope!("Queue::submit"); profiling::scope!("Queue::submit");
// wgpu doesn't document where vsync can happen. Maybe here? // wgpu doesn't document where vsync can happen. Maybe here?
let start = web_time::Instant::now(); let start = web_time::Instant::now();
render_state render_state
@ -552,7 +552,7 @@ impl Painter {
} }
{ {
crate::profile_scope!("present"); profiling::scope!("present");
// wgpu doesn't document where vsync can happen. Maybe here? // wgpu doesn't document where vsync can happen. Maybe here?
let start = web_time::Instant::now(); let start = web_time::Instant::now();
output_frame.present(); output_frame.present();

View File

@ -45,9 +45,6 @@ clipboard = ["arboard", "smithay-clipboard"]
## Enable opening links in a browser when an egui hyperlink is clicked. ## Enable opening links in a browser when an egui hyperlink is clicked.
links = ["webbrowser"] links = ["webbrowser"]
## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate.
puffin = ["dep:puffin", "egui/puffin"]
## Allow serialization of [`WindowSettings`] using [`serde`](https://docs.rs/serde). ## Allow serialization of [`WindowSettings`] using [`serde`](https://docs.rs/serde).
serde = ["egui/serde", "dep:serde"] serde = ["egui/serde", "dep:serde"]
@ -62,6 +59,7 @@ egui = { workspace = true, default-features = false, features = ["log"] }
ahash.workspace = true ahash.workspace = true
log.workspace = true log.workspace = true
profiling.workspace = true
raw-window-handle.workspace = true raw-window-handle.workspace = true
web-time.workspace = true web-time.workspace = true
winit = { workspace = true, default-features = false } winit = { workspace = true, default-features = false }
@ -74,7 +72,6 @@ accesskit_winit = { version = "0.23", optional = true }
## Enable this when generating docs. ## Enable this when generating docs.
document-features = { workspace = true, optional = true } document-features = { workspace = true, optional = true }
puffin = { workspace = true, optional = true }
serde = { workspace = true, optional = true } serde = { workspace = true, optional = true }
webbrowser = { version = "1.0.0", optional = true } webbrowser = { version = "1.0.0", optional = true }

View File

@ -112,7 +112,7 @@ impl Clipboard {
#[cfg(all(feature = "arboard", not(target_os = "android")))] #[cfg(all(feature = "arboard", not(target_os = "android")))]
fn init_arboard() -> Option<arboard::Clipboard> { fn init_arboard() -> Option<arboard::Clipboard> {
crate::profile_function!(); profiling::function_scope!();
log::trace!("Initializing arboard clipboard…"); log::trace!("Initializing arboard clipboard…");
match arboard::Clipboard::new() { match arboard::Clipboard::new() {
@ -139,7 +139,7 @@ fn init_smithay_clipboard(
) -> Option<smithay_clipboard::Clipboard> { ) -> Option<smithay_clipboard::Clipboard> {
#![allow(clippy::undocumented_unsafe_blocks)] #![allow(clippy::undocumented_unsafe_blocks)]
crate::profile_function!(); profiling::function_scope!();
if let Some(RawDisplayHandle::Wayland(display)) = raw_display_handle { if let Some(RawDisplayHandle::Wayland(display)) = raw_display_handle {
log::trace!("Initializing smithay clipboard…"); log::trace!("Initializing smithay clipboard…");

View File

@ -25,9 +25,6 @@ pub use window_settings::WindowSettings;
use ahash::HashSet; use ahash::HashSet;
use raw_window_handle::HasDisplayHandle; use raw_window_handle::HasDisplayHandle;
#[allow(unused_imports)]
pub(crate) use profiling_scopes::{profile_function, profile_scope};
use winit::{ use winit::{
dpi::{PhysicalPosition, PhysicalSize}, dpi::{PhysicalPosition, PhysicalSize},
event::ElementState, event::ElementState,
@ -121,7 +118,7 @@ impl State {
theme: Option<winit::window::Theme>, theme: Option<winit::window::Theme>,
max_texture_side: Option<usize>, max_texture_side: Option<usize>,
) -> Self { ) -> Self {
crate::profile_function!(); profiling::function_scope!();
let egui_input = egui::RawInput { let egui_input = egui::RawInput {
focused: false, // winit will tell us when we have focus focused: false, // winit will tell us when we have focus
@ -172,7 +169,7 @@ impl State {
window: &Window, window: &Window,
event_loop_proxy: winit::event_loop::EventLoopProxy<T>, event_loop_proxy: winit::event_loop::EventLoopProxy<T>,
) { ) {
crate::profile_function!(); profiling::function_scope!();
self.accesskit = Some(accesskit_winit::Adapter::with_event_loop_proxy( self.accesskit = Some(accesskit_winit::Adapter::with_event_loop_proxy(
window, window,
@ -233,7 +230,7 @@ impl State {
/// Use [`update_viewport_info`] to update the info for each /// Use [`update_viewport_info`] to update the info for each
/// viewport. /// viewport.
pub fn take_egui_input(&mut self, window: &Window) -> egui::RawInput { pub fn take_egui_input(&mut self, window: &Window) -> egui::RawInput {
crate::profile_function!(); profiling::function_scope!();
self.egui_input.time = Some(self.start_time.elapsed().as_secs_f64()); self.egui_input.time = Some(self.start_time.elapsed().as_secs_f64());
@ -268,7 +265,7 @@ impl State {
window: &Window, window: &Window,
event: &winit::event::WindowEvent, event: &winit::event::WindowEvent,
) -> EventResponse { ) -> EventResponse {
crate::profile_function!(short_window_event_description(event)); profiling::function_scope!(short_window_event_description(event));
#[cfg(feature = "accesskit")] #[cfg(feature = "accesskit")]
if let Some(accesskit) = self.accesskit.as_mut() { if let Some(accesskit) = self.accesskit.as_mut() {
@ -823,7 +820,7 @@ impl State {
window: &Window, window: &Window,
platform_output: egui::PlatformOutput, platform_output: egui::PlatformOutput,
) { ) {
crate::profile_function!(); profiling::function_scope!();
let egui::PlatformOutput { let egui::PlatformOutput {
cursor_icon, cursor_icon,
@ -851,7 +848,7 @@ impl State {
let allow_ime = ime.is_some(); let allow_ime = ime.is_some();
if self.allow_ime != allow_ime { if self.allow_ime != allow_ime {
self.allow_ime = allow_ime; self.allow_ime = allow_ime;
crate::profile_scope!("set_ime_allowed"); profiling::scope!("set_ime_allowed");
window.set_ime_allowed(allow_ime); window.set_ime_allowed(allow_ime);
} }
@ -862,7 +859,7 @@ impl State {
|| self.egui_ctx.input(|i| !i.events.is_empty()) || self.egui_ctx.input(|i| !i.events.is_empty())
{ {
self.ime_rect_px = Some(ime_rect_px); self.ime_rect_px = Some(ime_rect_px);
crate::profile_scope!("set_ime_cursor_area"); profiling::scope!("set_ime_cursor_area");
window.set_ime_cursor_area( window.set_ime_cursor_area(
winit::dpi::PhysicalPosition { winit::dpi::PhysicalPosition {
x: ime_rect_px.min.x, x: ime_rect_px.min.x,
@ -881,7 +878,7 @@ impl State {
#[cfg(feature = "accesskit")] #[cfg(feature = "accesskit")]
if let Some(accesskit) = self.accesskit.as_mut() { if let Some(accesskit) = self.accesskit.as_mut() {
if let Some(update) = accesskit_update { if let Some(update) = accesskit_update {
crate::profile_scope!("accesskit"); profiling::scope!("accesskit");
accesskit.update_if_active(|| update); accesskit.update_if_active(|| update);
} }
} }
@ -953,8 +950,7 @@ pub fn update_viewport_info(
window: &Window, window: &Window,
is_init: bool, is_init: bool,
) { ) {
crate::profile_function!(); profiling::function_scope!();
let pixels_per_point = pixels_per_point(egui_ctx, window); let pixels_per_point = pixels_per_point(egui_ctx, window);
let has_a_position = match window.is_minimized() { let has_a_position = match window.is_minimized() {
@ -975,7 +971,7 @@ pub fn update_viewport_info(
}; };
let monitor_size = { let monitor_size = {
crate::profile_scope!("monitor_size"); profiling::scope!("monitor_size");
if let Some(monitor) = window.current_monitor() { if let Some(monitor) = window.current_monitor() {
let size = monitor.size().to_logical::<f32>(pixels_per_point.into()); let size = monitor.size().to_logical::<f32>(pixels_per_point.into());
Some(egui::vec2(size.width, size.height)) Some(egui::vec2(size.width, size.height))
@ -1326,7 +1322,7 @@ fn process_viewport_command(
info: &mut ViewportInfo, info: &mut ViewportInfo,
actions_requested: &mut HashSet<ActionRequested>, actions_requested: &mut HashSet<ActionRequested>,
) { ) {
crate::profile_function!(); profiling::function_scope!();
use winit::window::ResizeDirection; use winit::window::ResizeDirection;
@ -1542,7 +1538,7 @@ pub fn create_window(
event_loop: &ActiveEventLoop, event_loop: &ActiveEventLoop,
viewport_builder: &ViewportBuilder, viewport_builder: &ViewportBuilder,
) -> Result<Window, winit::error::OsError> { ) -> Result<Window, winit::error::OsError> {
crate::profile_function!(); profiling::function_scope!();
let window_attributes = let window_attributes =
create_winit_window_attributes(egui_ctx, event_loop, viewport_builder.clone()); create_winit_window_attributes(egui_ctx, event_loop, viewport_builder.clone());
@ -1556,7 +1552,7 @@ pub fn create_winit_window_attributes(
event_loop: &ActiveEventLoop, event_loop: &ActiveEventLoop,
viewport_builder: ViewportBuilder, viewport_builder: ViewportBuilder,
) -> winit::window::WindowAttributes { ) -> winit::window::WindowAttributes {
crate::profile_function!(); profiling::function_scope!();
// We set sizes and positions in egui:s own ui points, which depends on the egui // We set sizes and positions in egui:s own ui points, which depends on the egui
// zoom_factor and the native pixels per point, so we need to know that here. // zoom_factor and the native pixels per point, so we need to know that here.
@ -1752,7 +1748,7 @@ fn to_winit_icon(icon: &egui::IconData) -> Option<winit::window::Icon> {
if icon.is_empty() { if icon.is_empty() {
None None
} else { } else {
crate::profile_function!(); profiling::function_scope!();
match winit::window::Icon::from_rgba(icon.rgba.clone(), icon.width, icon.height) { match winit::window::Icon::from_rgba(icon.rgba.clone(), icon.width, icon.height) {
Ok(winit_icon) => Some(winit_icon), Ok(winit_icon) => Some(winit_icon),
Err(err) => { Err(err) => {
@ -1867,30 +1863,3 @@ pub fn short_window_event_description(event: &winit::event::WindowEvent) -> &'st
WindowEvent::PanGesture { .. } => "WindowEvent::PanGesture", WindowEvent::PanGesture { .. } => "WindowEvent::PanGesture",
} }
} }
// ---------------------------------------------------------------------------
mod profiling_scopes {
#![allow(unused_macros)]
#![allow(unused_imports)]
/// Profiling macro for feature "puffin"
macro_rules! profile_function {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
puffin::profile_function!($($arg)*);
};
}
pub(crate) use profile_function;
/// Profiling macro for feature "puffin"
macro_rules! profile_scope {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
puffin::profile_scope!($($arg)*);
};
}
pub(crate) use profile_scope;
}

View File

@ -56,7 +56,7 @@ impl WindowSettings {
event_loop: &winit::event_loop::ActiveEventLoop, event_loop: &winit::event_loop::ActiveEventLoop,
mut viewport_builder: ViewportBuilder, mut viewport_builder: ViewportBuilder,
) -> ViewportBuilder { ) -> ViewportBuilder {
crate::profile_function!(); profiling::function_scope!();
// `WindowBuilder::with_position` expects inner position in Macos, and outer position elsewhere // `WindowBuilder::with_position` expects inner position in Macos, and outer position elsewhere
// See [`winit::window::WindowBuilder::with_position`] for details. // See [`winit::window::WindowBuilder::with_position`] for details.
@ -143,8 +143,7 @@ fn find_active_monitor(
window_size_pts: egui::Vec2, window_size_pts: egui::Vec2,
position_px: &egui::Pos2, position_px: &egui::Pos2,
) -> Option<winit::monitor::MonitorHandle> { ) -> Option<winit::monitor::MonitorHandle> {
crate::profile_function!(); profiling::function_scope!();
let monitors = event_loop.available_monitors(); let monitors = event_loop.available_monitors();
// default to primary monitor, in case the correct monitor was disconnected. // default to primary monitor, in case the correct monitor was disconnected.
@ -178,7 +177,7 @@ fn clamp_pos_to_monitors(
window_size_pts: egui::Vec2, window_size_pts: egui::Vec2,
position_px: &mut egui::Pos2, position_px: &mut egui::Pos2,
) { ) {
crate::profile_function!(); profiling::function_scope!();
let Some(active_monitor) = let Some(active_monitor) =
find_active_monitor(egui_zoom_factor, event_loop, window_size_pts, position_px) find_active_monitor(egui_zoom_factor, event_loop, window_size_pts, position_px)

View File

@ -62,10 +62,6 @@ mint = ["epaint/mint"]
## Enable persistence of memory (window positions etc). ## Enable persistence of memory (window positions etc).
persistence = ["serde", "epaint/serde", "ron"] persistence = ["serde", "epaint/serde", "ron"]
## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate.
##
## Only enabled on native, because of the low resolution (1ms) of clocks in browsers.
puffin = ["dep:puffin", "epaint/puffin"]
## Enable parallel tessellation using [`rayon`](https://docs.rs/rayon). ## Enable parallel tessellation using [`rayon`](https://docs.rs/rayon).
## ##
@ -85,6 +81,7 @@ epaint = { workspace = true, default-features = false }
ahash.workspace = true ahash.workspace = true
nohash-hasher.workspace = true nohash-hasher.workspace = true
profiling.workspace = true
#! ### Optional dependencies #! ### Optional dependencies
accesskit = { version = "0.17.0", optional = true } accesskit = { version = "0.17.0", optional = true }
@ -95,7 +92,6 @@ backtrace = { workspace = true, optional = true }
document-features = { workspace = true, optional = true } document-features = { workspace = true, optional = true }
log = { workspace = true, optional = true } log = { workspace = true, optional = true }
puffin = { workspace = true, optional = true }
ron = { workspace = true, optional = true } ron = { workspace = true, optional = true }
serde = { workspace = true, optional = true, features = ["derive", "rc"] } serde = { workspace = true, optional = true, features = ["derive", "rc"] }

View File

@ -109,13 +109,13 @@ struct Plugins {
impl Plugins { impl Plugins {
fn call(ctx: &Context, _cb_name: &str, callbacks: &[NamedContextCallback]) { fn call(ctx: &Context, _cb_name: &str, callbacks: &[NamedContextCallback]) {
crate::profile_scope!("plugins", _cb_name); profiling::scope!("plugins", _cb_name);
for NamedContextCallback { for NamedContextCallback {
debug_name: _name, debug_name: _name,
callback, callback,
} in callbacks } in callbacks
{ {
crate::profile_scope!("plugin", _name); profiling::scope!("plugin", _name);
(callback)(ctx); (callback)(ctx);
} }
} }
@ -549,7 +549,7 @@ impl ContextImpl {
#[cfg(feature = "accesskit")] #[cfg(feature = "accesskit")]
if self.is_accesskit_enabled { if self.is_accesskit_enabled {
crate::profile_scope!("accesskit"); profiling::scope!("accesskit");
use crate::pass_state::AccessKitPassState; use crate::pass_state::AccessKitPassState;
let id = crate::accesskit_root_id(); let id = crate::accesskit_root_id();
let mut root_node = accesskit::Node::new(accesskit::Role::Window); let mut root_node = accesskit::Node::new(accesskit::Role::Window);
@ -568,8 +568,7 @@ impl ContextImpl {
/// Load fonts unless already loaded. /// Load fonts unless already loaded.
fn update_fonts_mut(&mut self) { fn update_fonts_mut(&mut self) {
crate::profile_function!(); profiling::function_scope!();
let input = &self.viewport().input; let input = &self.viewport().input;
let pixels_per_point = input.pixels_per_point(); let pixels_per_point = input.pixels_per_point();
let max_texture_side = input.max_texture_side; let max_texture_side = input.max_texture_side;
@ -616,7 +615,7 @@ impl ContextImpl {
log::trace!("Creating new Fonts for pixels_per_point={pixels_per_point}"); log::trace!("Creating new Fonts for pixels_per_point={pixels_per_point}");
is_new = true; is_new = true;
crate::profile_scope!("Fonts::new"); profiling::scope!("Fonts::new");
Fonts::new( Fonts::new(
pixels_per_point, pixels_per_point,
max_texture_side, max_texture_side,
@ -625,12 +624,12 @@ impl ContextImpl {
}); });
{ {
crate::profile_scope!("Fonts::begin_pass"); profiling::scope!("Fonts::begin_pass");
fonts.begin_pass(pixels_per_point, max_texture_side); fonts.begin_pass(pixels_per_point, max_texture_side);
} }
if is_new && self.memory.options.preload_font_glyphs { if is_new && self.memory.options.preload_font_glyphs {
crate::profile_scope!("preload_font_glyphs"); profiling::scope!("preload_font_glyphs");
// Preload the most common characters for the most common fonts. // Preload the most common characters for the most common fonts.
// This is not very important to do, but may save a few GPU operations. // This is not very important to do, but may save a few GPU operations.
for font_id in self.memory.options.style().text_styles.values() { for font_id in self.memory.options.style().text_styles.values() {
@ -812,8 +811,7 @@ impl Context {
/// ``` /// ```
#[must_use] #[must_use]
pub fn run(&self, mut new_input: RawInput, mut run_ui: impl FnMut(&Self)) -> FullOutput { pub fn run(&self, mut new_input: RawInput, mut run_ui: impl FnMut(&Self)) -> FullOutput {
crate::profile_function!(); profiling::function_scope!();
let viewport_id = new_input.viewport_id; let viewport_id = new_input.viewport_id;
let max_passes = self.write(|ctx| ctx.memory.options.max_passes.get()); let max_passes = self.write(|ctx| ctx.memory.options.max_passes.get());
@ -821,9 +819,13 @@ impl Context {
debug_assert_eq!(output.platform_output.num_completed_passes, 0); debug_assert_eq!(output.platform_output.num_completed_passes, 0);
loop { loop {
crate::profile_scope!( profiling::scope!(
"pass", "pass",
output.platform_output.num_completed_passes.to_string() output
.platform_output
.num_completed_passes
.to_string()
.as_str()
); );
// We must move the `num_passes` (back) to the viewport output so that [`Self::will_discard`] // We must move the `num_passes` (back) to the viewport output so that [`Self::will_discard`]
@ -886,7 +888,7 @@ impl Context {
/// // handle full_output /// // handle full_output
/// ``` /// ```
pub fn begin_pass(&self, new_input: RawInput) { pub fn begin_pass(&self, new_input: RawInput) {
crate::profile_function!(); profiling::function_scope!();
self.write(|ctx| ctx.begin_pass(new_input)); self.write(|ctx| ctx.begin_pass(new_input));
@ -1760,7 +1762,7 @@ impl Context {
/// The new fonts will become active at the start of the next pass. /// The new fonts will become active at the start of the next pass.
/// This will overwrite the existing fonts. /// This will overwrite the existing fonts.
pub fn set_fonts(&self, font_definitions: FontDefinitions) { pub fn set_fonts(&self, font_definitions: FontDefinitions) {
crate::profile_function!(); profiling::function_scope!();
let pixels_per_point = self.pixels_per_point(); let pixels_per_point = self.pixels_per_point();
@ -1788,7 +1790,7 @@ impl Context {
/// The new font will become active at the start of the next pass. /// The new font will become active at the start of the next pass.
/// This will keep the existing fonts. /// This will keep the existing fonts.
pub fn add_font(&self, new_font: FontInsert) { pub fn add_font(&self, new_font: FontInsert) {
crate::profile_function!(); profiling::function_scope!();
let pixels_per_point = self.pixels_per_point(); let pixels_per_point = self.pixels_per_point();
@ -2152,7 +2154,7 @@ impl Context {
/// Call at the end of each frame if you called [`Context::begin_pass`]. /// Call at the end of each frame if you called [`Context::begin_pass`].
#[must_use] #[must_use]
pub fn end_pass(&self) -> FullOutput { pub fn end_pass(&self) -> FullOutput {
crate::profile_function!(); profiling::function_scope!();
if self.options(|o| o.zoom_with_keyboard) { if self.options(|o| o.zoom_with_keyboard) {
crate::gui_zoom::zoom_with_keyboard(self); crate::gui_zoom::zoom_with_keyboard(self);
@ -2342,7 +2344,7 @@ impl ContextImpl {
// https://github.com/emilk/egui/issues/3664 // https://github.com/emilk/egui/issues/3664
// at the cost of a lot of performance. // at the cost of a lot of performance.
// (This will override any smaller delta that was uploaded above.) // (This will override any smaller delta that was uploaded above.)
crate::profile_scope!("full_font_atlas_update"); profiling::scope!("full_font_atlas_update");
let full_delta = ImageDelta::full(fonts.image(), TextureAtlas::texture_options()); let full_delta = ImageDelta::full(fonts.image(), TextureAtlas::texture_options());
tex_mngr.set(TextureId::default(), full_delta); tex_mngr.set(TextureId::default(), full_delta);
} }
@ -2356,7 +2358,7 @@ impl ContextImpl {
#[cfg(feature = "accesskit")] #[cfg(feature = "accesskit")]
{ {
crate::profile_scope!("accesskit"); profiling::scope!("accesskit");
let state = viewport.this_pass.accesskit_state.take(); let state = viewport.this_pass.accesskit_state.take();
if let Some(state) = state { if let Some(state) = state {
let root_id = crate::accesskit_root_id().accesskit_id(); let root_id = crate::accesskit_root_id().accesskit_id();
@ -2386,7 +2388,7 @@ impl ContextImpl {
let mut repaint_needed = false; let mut repaint_needed = false;
if self.memory.options.repaint_on_widget_change { if self.memory.options.repaint_on_widget_change {
crate::profile_function!("compare-widget-rects"); profiling::scope!("compare-widget-rects");
if viewport.prev_pass.widgets != viewport.this_pass.widgets { if viewport.prev_pass.widgets != viewport.this_pass.widgets {
repaint_needed = true; // Some widget has moved repaint_needed = true; // Some widget has moved
} }
@ -2525,7 +2527,7 @@ impl Context {
shapes: Vec<ClippedShape>, shapes: Vec<ClippedShape>,
pixels_per_point: f32, pixels_per_point: f32,
) -> Vec<ClippedPrimitive> { ) -> Vec<ClippedPrimitive> {
crate::profile_function!(); profiling::function_scope!();
// A tempting optimization is to reuse the tessellation from last frame if the // A tempting optimization is to reuse the tessellation from last frame if the
// shapes are the same, but just comparing the shapes takes about 50% of the time // shapes are the same, but just comparing the shapes takes about 50% of the time
@ -2552,7 +2554,7 @@ impl Context {
let paint_stats = PaintStats::from_shapes(&shapes); let paint_stats = PaintStats::from_shapes(&shapes);
let clipped_primitives = { let clipped_primitives = {
crate::profile_scope!("tessellator::tessellate_shapes"); profiling::scope!("tessellator::tessellate_shapes");
tessellator::Tessellator::new( tessellator::Tessellator::new(
pixels_per_point, pixels_per_point,
tessellation_options, tessellation_options,
@ -3368,7 +3370,7 @@ impl Context {
pub fn forget_image(&self, uri: &str) { pub fn forget_image(&self, uri: &str) {
use load::BytesLoader as _; use load::BytesLoader as _;
crate::profile_function!(); profiling::function_scope!();
let loaders = self.loaders(); let loaders = self.loaders();
@ -3390,7 +3392,7 @@ impl Context {
pub fn forget_all_images(&self) { pub fn forget_all_images(&self) {
use load::BytesLoader as _; use load::BytesLoader as _;
crate::profile_function!(); profiling::function_scope!();
let loaders = self.loaders(); let loaders = self.loaders();
@ -3425,7 +3427,7 @@ impl Context {
/// [not_supported]: crate::load::LoadError::NotSupported /// [not_supported]: crate::load::LoadError::NotSupported
/// [custom]: crate::load::LoadError::Loading /// [custom]: crate::load::LoadError::Loading
pub fn try_load_bytes(&self, uri: &str) -> load::BytesLoadResult { pub fn try_load_bytes(&self, uri: &str) -> load::BytesLoadResult {
crate::profile_function!(uri); profiling::function_scope!(uri);
let loaders = self.loaders(); let loaders = self.loaders();
let bytes_loaders = loaders.bytes.lock(); let bytes_loaders = loaders.bytes.lock();
@ -3462,7 +3464,7 @@ impl Context {
/// [not_supported]: crate::load::LoadError::NotSupported /// [not_supported]: crate::load::LoadError::NotSupported
/// [custom]: crate::load::LoadError::Loading /// [custom]: crate::load::LoadError::Loading
pub fn try_load_image(&self, uri: &str, size_hint: load::SizeHint) -> load::ImageLoadResult { pub fn try_load_image(&self, uri: &str, size_hint: load::SizeHint) -> load::ImageLoadResult {
crate::profile_function!(uri); profiling::function_scope!(uri);
let loaders = self.loaders(); let loaders = self.loaders();
let image_loaders = loaders.image.lock(); let image_loaders = loaders.image.lock();
@ -3513,7 +3515,7 @@ impl Context {
texture_options: TextureOptions, texture_options: TextureOptions,
size_hint: load::SizeHint, size_hint: load::SizeHint,
) -> load::TextureLoadResult { ) -> load::TextureLoadResult {
crate::profile_function!(uri); profiling::function_scope!(uri);
let loaders = self.loaders(); let loaders = self.loaders();
let texture_loaders = loaders.texture.lock(); let texture_loaders = loaders.texture.lock();
@ -3531,7 +3533,7 @@ impl Context {
/// The loaders of bytes, images, and textures. /// The loaders of bytes, images, and textures.
pub fn loaders(&self) -> Arc<Loaders> { pub fn loaders(&self) -> Arc<Loaders> {
crate::profile_function!(); profiling::function_scope!();
self.read(|this| this.loaders.clone()) self.read(|this| this.loaders.clone())
} }
} }
@ -3663,7 +3665,7 @@ impl Context {
viewport_builder: ViewportBuilder, viewport_builder: ViewportBuilder,
viewport_ui_cb: impl Fn(&Self, ViewportClass) + Send + Sync + 'static, viewport_ui_cb: impl Fn(&Self, ViewportClass) + Send + Sync + 'static,
) { ) {
crate::profile_function!(); profiling::function_scope!();
if self.embed_viewports() { if self.embed_viewports() {
viewport_ui_cb(self, ViewportClass::Embedded); viewport_ui_cb(self, ViewportClass::Embedded);
@ -3715,7 +3717,7 @@ impl Context {
builder: ViewportBuilder, builder: ViewportBuilder,
mut viewport_ui_cb: impl FnMut(&Self, ViewportClass) -> T, mut viewport_ui_cb: impl FnMut(&Self, ViewportClass) -> T,
) -> T { ) -> T {
crate::profile_function!(); profiling::function_scope!();
if self.embed_viewports() { if self.embed_viewports() {
return viewport_ui_cb(self, ViewportClass::Embedded); return viewport_ui_cb(self, ViewportClass::Embedded);

View File

@ -39,7 +39,7 @@ pub fn hit_test(
pos: Pos2, pos: Pos2,
search_radius: f32, search_radius: f32,
) -> WidgetHits { ) -> WidgetHits {
crate::profile_function!(); profiling::function_scope!();
let search_radius_sq = search_radius * search_radius; let search_radius_sq = search_radius * search_radius;

View File

@ -269,7 +269,7 @@ impl InputState {
pixels_per_point: f32, pixels_per_point: f32,
options: &crate::Options, options: &crate::Options,
) -> Self { ) -> Self {
crate::profile_function!(); profiling::function_scope!();
let time = new.time.unwrap_or(self.time + new.predicted_dt as f64); let time = new.time.unwrap_or(self.time + new.predicted_dt as f64);
let unstable_dt = (time - self.time) as f32; let unstable_dt = (time - self.time) as f32;

View File

@ -113,7 +113,7 @@ pub(crate) fn interact(
input: &InputState, input: &InputState,
interaction: &mut InteractionState, interaction: &mut InteractionState,
) -> InteractionSnapshot { ) -> InteractionSnapshot {
crate::profile_function!(); profiling::function_scope!();
if let Some(id) = interaction.potential_click_id { if let Some(id) = interaction.potential_click_id {
if !widgets.contains(id) { if !widgets.contains(id) {

View File

@ -222,7 +222,7 @@ impl GraphicLayers {
area_order: &[LayerId], area_order: &[LayerId],
to_global: &ahash::HashMap<LayerId, TSTransform>, to_global: &ahash::HashMap<LayerId, TSTransform>,
) -> Vec<ClippedShape> { ) -> Vec<ClippedShape> {
crate::profile_function!(); profiling::function_scope!();
let mut all_shapes: Vec<_> = Default::default(); let mut all_shapes: Vec<_> = Default::default();

View File

@ -388,6 +388,18 @@
//! ## Installing additional fonts //! ## Installing additional fonts
//! The default egui fonts only support latin and cryllic characters, and some emojis. //! The default egui fonts only support latin and cryllic characters, and some emojis.
//! To use egui with e.g. asian characters you need to install your own font (`.ttf` or `.otf`) using [`Context::set_fonts`]. //! To use egui with e.g. asian characters you need to install your own font (`.ttf` or `.otf`) using [`Context::set_fonts`].
//!
//! ## Instrumentation
//! This crate supports using the [profiling](https://crates.io/crates/profiling) crate for instrumentation.
//! You can enable features on the profiling crates in your application to add instrumentation for all
//! crates that support it, including egui. See the profiling crate docs for more information.
//! ```toml
//! [dependencies]
//! profiling = "1.0"
//! [features]
//! profile-with-puffin = ["profiling/profile-with-puffin"]
//! ```
//!
#![allow(clippy::float_cmp)] #![allow(clippy::float_cmp)]
#![allow(clippy::manual_range_contains)] #![allow(clippy::manual_range_contains)]
@ -691,33 +703,3 @@ pub fn __run_test_ui(add_contents: impl Fn(&mut Ui)) {
pub fn accesskit_root_id() -> Id { pub fn accesskit_root_id() -> Id {
Id::new("accesskit_root") Id::new("accesskit_root")
} }
// ---------------------------------------------------------------------------
mod profiling_scopes {
#![allow(unused_macros)]
#![allow(unused_imports)]
/// Profiling macro for feature "puffin"
macro_rules! profile_function {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
puffin::profile_function!($($arg)*);
};
}
pub(crate) use profile_function;
/// Profiling macro for feature "puffin"
macro_rules! profile_scope {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
puffin::profile_scope!($($arg)*);
};
}
pub(crate) use profile_scope;
}
#[allow(unused_imports)]
pub(crate) use profiling_scopes::{profile_function, profile_scope};

View File

@ -779,7 +779,7 @@ impl Focus {
impl Memory { impl Memory {
pub(crate) fn begin_pass(&mut self, new_raw_input: &RawInput, viewports: &ViewportIdSet) { pub(crate) fn begin_pass(&mut self, new_raw_input: &RawInput, viewports: &ViewportIdSet) {
crate::profile_function!(); profiling::function_scope!();
self.viewport_id = new_raw_input.viewport_id; self.viewport_id = new_raw_input.viewport_id;

View File

@ -248,7 +248,7 @@ impl Default for PassState {
impl PassState { impl PassState {
pub(crate) fn begin_pass(&mut self, screen_rect: Rect) { pub(crate) fn begin_pass(&mut self, screen_rect: Rect) {
crate::profile_function!(); profiling::function_scope!();
let Self { let Self {
used_ids, used_ids,
widgets, widgets,

View File

@ -574,7 +574,7 @@ struct PersistedMap(Vec<(u64, SerializedElement)>);
#[cfg(feature = "persistence")] #[cfg(feature = "persistence")]
impl PersistedMap { impl PersistedMap {
fn from_map(map: &IdTypeMap) -> Self { fn from_map(map: &IdTypeMap) -> Self {
crate::profile_function!(); profiling::function_scope!();
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -593,7 +593,7 @@ impl PersistedMap {
let max_bytes_per_type = map.max_bytes_per_type; let max_bytes_per_type = map.max_bytes_per_type;
{ {
crate::profile_scope!("gather"); profiling::scope!("gather");
for (hash, element) in &map.map { for (hash, element) in &map.map {
if let Some(element) = element.to_serialize() { if let Some(element) = element.to_serialize() {
let stats = types_map.entry(element.type_id).or_default(); let stats = types_map.entry(element.type_id).or_default();
@ -610,7 +610,7 @@ impl PersistedMap {
let mut persisted = vec![]; let mut persisted = vec![];
{ {
crate::profile_scope!("gc"); profiling::scope!("gc");
for stats in types_map.values() { for stats in types_map.values() {
let mut bytes_written = 0; let mut bytes_written = 0;
@ -634,7 +634,7 @@ impl PersistedMap {
} }
fn into_map(self) -> IdTypeMap { fn into_map(self) -> IdTypeMap {
crate::profile_function!(); profiling::function_scope!();
let map = self let map = self
.0 .0
.into_iter() .into_iter()
@ -671,7 +671,7 @@ impl serde::Serialize for IdTypeMap {
where where
S: serde::Serializer, S: serde::Serializer,
{ {
crate::profile_scope!("IdTypeMap::serialize"); profiling::scope!("IdTypeMap::serialize");
PersistedMap::from_map(self).serialize(serializer) PersistedMap::from_map(self).serialize(serializer)
} }
} }
@ -682,7 +682,7 @@ impl<'de> serde::Deserialize<'de> for IdTypeMap {
where where
D: serde::Deserializer<'de>, D: serde::Deserializer<'de>,
{ {
crate::profile_scope!("IdTypeMap::deserialize"); profiling::scope!("IdTypeMap::deserialize");
<PersistedMap>::deserialize(deserializer).map(PersistedMap::into_map) <PersistedMap>::deserialize(deserializer).map(PersistedMap::into_map)
} }
} }

View File

@ -188,7 +188,7 @@ impl std::fmt::Debug for IconData {
impl From<IconData> for epaint::ColorImage { impl From<IconData> for epaint::ColorImage {
fn from(icon: IconData) -> Self { fn from(icon: IconData) -> Self {
crate::profile_function!(); profiling::function_scope!();
let IconData { let IconData {
rgba, rgba,
width, width,
@ -200,7 +200,7 @@ impl From<IconData> for epaint::ColorImage {
impl From<&IconData> for epaint::ColorImage { impl From<&IconData> for epaint::ColorImage {
fn from(icon: &IconData) -> Self { fn from(icon: &IconData) -> Self {
crate::profile_function!(); profiling::function_scope!();
let IconData { let IconData {
rgba, rgba,
width, width,

View File

@ -8,6 +8,9 @@ rust-version.workspace = true
publish = false publish = false
default-run = "egui_demo_app" default-run = "egui_demo_app"
[package.metadata.cargo-machete]
ignored = ["profiling"]
[lints] [lints]
workspace = true workspace = true
@ -33,7 +36,7 @@ persistence = [
"egui/persistence", "egui/persistence",
"serde", "serde",
] ]
puffin = ["eframe/puffin", "dep:puffin", "dep:puffin_http"] puffin = ["dep:puffin", "dep:puffin_http", "profiling/profile-with-puffin"]
serde = ["dep:serde", "egui_demo_lib/serde", "egui/serde"] serde = ["dep:serde", "egui_demo_lib/serde", "egui/serde"]
syntect = ["egui_demo_lib/syntect"] syntect = ["egui_demo_lib/syntect"]
@ -54,6 +57,7 @@ egui = { workspace = true, features = ["callstack", "default", "log"] }
egui_demo_lib = { workspace = true, features = ["default", "chrono"] } egui_demo_lib = { workspace = true, features = ["default", "chrono"] }
egui_extras = { workspace = true, features = ["default", "image"] } egui_extras = { workspace = true, features = ["default", "image"] }
log.workspace = true log.workspace = true
profiling.workspace = true
# Optional dependencies: # Optional dependencies:

View File

@ -51,6 +51,7 @@ fn main() -> eframe::Result {
..Default::default() ..Default::default()
}; };
eframe::run_native( eframe::run_native(
"egui demo app", "egui demo app",
options, options,

View File

@ -53,11 +53,6 @@ http = ["dep:ehttp"]
## ``` ## ```
image = ["dep:image"] image = ["dep:image"]
## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate.
##
## Only enabled on native, because of the low resolution (1ms) of clocks in browsers.
puffin = ["dep:puffin", "egui/puffin"]
## Derive serde Serialize/Deserialize on stateful structs ## Derive serde Serialize/Deserialize on stateful structs
serde = ["egui/serde", "dep:serde"] serde = ["egui/serde", "dep:serde"]
@ -74,6 +69,7 @@ egui = { workspace = true, default-features = false }
ahash.workspace = true ahash.workspace = true
enum-map = { version = "2", features = ["serde"] } enum-map = { version = "2", features = ["serde"] }
log.workspace = true log.workspace = true
profiling.workspace = true
#! ### Optional dependencies #! ### Optional dependencies
@ -96,7 +92,6 @@ image = { workspace = true, optional = true }
# file feature # file feature
mime_guess2 = { version = "2", optional = true, default-features = false } mime_guess2 = { version = "2", optional = true, default-features = false }
puffin = { workspace = true, optional = true }
syntect = { version = "5", optional = true, default-features = false, features = [ syntect = { version = "5", optional = true, default-features = false, features = [
"default-fancy", "default-fancy",

View File

@ -199,7 +199,7 @@ impl RetainedImage {
/// On invalid image or unsupported image format. /// On invalid image or unsupported image format.
#[cfg(feature = "image")] #[cfg(feature = "image")]
pub fn load_image_bytes(image_bytes: &[u8]) -> Result<egui::ColorImage, egui::load::LoadError> { pub fn load_image_bytes(image_bytes: &[u8]) -> Result<egui::ColorImage, egui::load::LoadError> {
crate::profile_function!(); profiling::function_scope!();
let image = image::load_from_memory(image_bytes).map_err(|err| match err { let image = image::load_from_memory(image_bytes).map_err(|err| match err {
image::ImageError::Unsupported(err) => match err.kind() { image::ImageError::Unsupported(err) => match err.kind() {
image::error::UnsupportedErrorKind::Format(format) => { image::error::UnsupportedErrorKind::Format(format) => {
@ -245,7 +245,8 @@ pub fn load_svg_bytes_with_size(
use resvg::tiny_skia::{IntSize, Pixmap}; use resvg::tiny_skia::{IntSize, Pixmap};
use resvg::usvg::{Options, Tree, TreeParsing}; use resvg::usvg::{Options, Tree, TreeParsing};
crate::profile_function!(); profiling::function_scope!();
let opt = Options::default(); let opt = Options::default();
let mut rtree = Tree::from_data(svg_bytes, &opt).map_err(|err| err.to_string())?; let mut rtree = Tree::from_data(svg_bytes, &opt).map_err(|err| err.to_string())?;

View File

@ -37,36 +37,6 @@ pub use loaders::install_image_loaders;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
mod profiling_scopes {
#![allow(unused_macros)]
#![allow(unused_imports)]
/// Profiling macro for feature "puffin"
macro_rules! profile_function {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
puffin::profile_function!($($arg)*);
};
}
pub(crate) use profile_function;
/// Profiling macro for feature "puffin"
macro_rules! profile_scope {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
puffin::profile_scope!($($arg)*);
};
}
pub(crate) use profile_scope;
}
#[allow(unused_imports)]
pub(crate) use profiling_scopes::profile_function;
// ---------------------------------------------------------------------------
/// Panic in debug builds, log otherwise. /// Panic in debug builds, log otherwise.
macro_rules! log_or_panic { macro_rules! log_or_panic {
($fmt: literal) => {$crate::log_or_panic!($fmt,)}; ($fmt: literal) => {$crate::log_or_panic!($fmt,)};

View File

@ -403,7 +403,7 @@ struct Highlighter {
#[cfg(feature = "syntect")] #[cfg(feature = "syntect")]
impl Default for Highlighter { impl Default for Highlighter {
fn default() -> Self { fn default() -> Self {
crate::profile_function!(); profiling::function_scope!();
Self { Self {
ps: syntect::parsing::SyntaxSet::load_defaults_newlines(), ps: syntect::parsing::SyntaxSet::load_defaults_newlines(),
ts: syntect::highlighting::ThemeSet::load_defaults(), ts: syntect::highlighting::ThemeSet::load_defaults(),
@ -437,8 +437,7 @@ impl Highlighter {
#[cfg(feature = "syntect")] #[cfg(feature = "syntect")]
fn highlight_impl(&self, theme: &CodeTheme, text: &str, language: &str) -> Option<LayoutJob> { fn highlight_impl(&self, theme: &CodeTheme, text: &str, language: &str) -> Option<LayoutJob> {
crate::profile_function!(); profiling::function_scope!();
use syntect::easy::HighlightLines; use syntect::easy::HighlightLines;
use syntect::highlighting::FontStyle; use syntect::highlighting::FontStyle;
use syntect::util::LinesWithEndings; use syntect::util::LinesWithEndings;
@ -512,7 +511,7 @@ impl Highlighter {
mut text: &str, mut text: &str,
language: &str, language: &str,
) -> Option<LayoutJob> { ) -> Option<LayoutJob> {
crate::profile_function!(); profiling::function_scope!();
let language = Language::new(language)?; let language = Language::new(language)?;

View File

@ -39,9 +39,6 @@ clipboard = ["egui-winit?/clipboard"]
## enable opening links in a browser when an egui hyperlink is clicked. ## enable opening links in a browser when an egui hyperlink is clicked.
links = ["egui-winit?/links"] links = ["egui-winit?/links"]
## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate.
puffin = ["dep:puffin", "egui-winit?/puffin", "egui/puffin"]
## Enable [`winit`](https://docs.rs/winit) integration. On Linux, requires either `wayland` or `x11` ## Enable [`winit`](https://docs.rs/winit) integration. On Linux, requires either `wayland` or `x11`
winit = ["egui-winit", "dep:winit"] winit = ["egui-winit", "dep:winit"]
@ -61,14 +58,13 @@ bytemuck.workspace = true
glow.workspace = true glow.workspace = true
log.workspace = true log.workspace = true
memoffset = "0.9" memoffset = "0.9"
profiling.workspace = true
#! ### Optional dependencies #! ### Optional dependencies
## Enable this when generating docs. ## Enable this when generating docs.
document-features = { workspace = true, optional = true } document-features = { workspace = true, optional = true }
# Native: # Native:
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
puffin = { workspace = true, optional = true }
winit = { workspace = true, optional = true, default-features = false, features = ["rwh_06"] } winit = { workspace = true, optional = true, default-features = false, features = ["rwh_06"] }
# Web: # Web:

View File

@ -110,33 +110,3 @@ pub fn check_for_gl_error_impl(gl: &glow::Context, file: &str, line: u32, contex
} }
} }
} }
// ---------------------------------------------------------------------------
mod profiling_scopes {
#![allow(unused_macros)]
#![allow(unused_imports)]
/// Profiling macro for feature "puffin"
macro_rules! profile_function {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
puffin::profile_function!($($arg)*);
};
}
pub(crate) use profile_function;
/// Profiling macro for feature "puffin"
macro_rules! profile_scope {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
puffin::profile_scope!($($arg)*);
};
}
pub(crate) use profile_scope;
}
#[allow(unused_imports)]
pub(crate) use profiling_scopes::{profile_function, profile_scope};

View File

@ -144,7 +144,7 @@ impl Painter {
shader_version: Option<ShaderVersion>, shader_version: Option<ShaderVersion>,
dithering: bool, dithering: bool,
) -> Result<Self, PainterError> { ) -> Result<Self, PainterError> {
crate::profile_function!(); profiling::function_scope!();
crate::check_for_gl_error_even_in_release!(&gl, "before Painter::new"); crate::check_for_gl_error_even_in_release!(&gl, "before Painter::new");
// some useful debug info. all three of them are present in gl 1.1. // some useful debug info. all three of them are present in gl 1.1.
@ -366,7 +366,7 @@ impl Painter {
clipped_primitives: &[egui::ClippedPrimitive], clipped_primitives: &[egui::ClippedPrimitive],
textures_delta: &egui::TexturesDelta, textures_delta: &egui::TexturesDelta,
) { ) {
crate::profile_function!(); profiling::function_scope!();
for (id, image_delta) in &textures_delta.set { for (id, image_delta) in &textures_delta.set {
self.set_texture(*id, image_delta); self.set_texture(*id, image_delta);
@ -405,7 +405,7 @@ impl Painter {
pixels_per_point: f32, pixels_per_point: f32,
clipped_primitives: &[egui::ClippedPrimitive], clipped_primitives: &[egui::ClippedPrimitive],
) { ) {
crate::profile_function!(); profiling::function_scope!();
self.assert_not_destroyed(); self.assert_not_destroyed();
unsafe { self.prepare_painting(screen_size_px, pixels_per_point) }; unsafe { self.prepare_painting(screen_size_px, pixels_per_point) };
@ -423,7 +423,7 @@ impl Painter {
} }
Primitive::Callback(callback) => { Primitive::Callback(callback) => {
if callback.rect.is_positive() { if callback.rect.is_positive() {
crate::profile_scope!("callback"); profiling::scope!("callback");
let info = egui::PaintCallbackInfo { let info = egui::PaintCallbackInfo {
viewport: callback.rect, viewport: callback.rect,
@ -508,7 +508,7 @@ impl Painter {
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
pub fn set_texture(&mut self, tex_id: egui::TextureId, delta: &egui::epaint::ImageDelta) { pub fn set_texture(&mut self, tex_id: egui::TextureId, delta: &egui::epaint::ImageDelta) {
crate::profile_function!(); profiling::function_scope!();
self.assert_not_destroyed(); self.assert_not_destroyed();
@ -540,7 +540,7 @@ impl Painter {
); );
let data: Vec<u8> = { let data: Vec<u8> = {
crate::profile_scope!("font -> sRGBA"); profiling::scope!("font -> sRGBA");
image image
.srgba_pixels(None) .srgba_pixels(None)
.flat_map(|a| a.to_array()) .flat_map(|a| a.to_array())
@ -559,7 +559,7 @@ impl Painter {
options: egui::TextureOptions, options: egui::TextureOptions,
data: &[u8], data: &[u8],
) { ) {
crate::profile_function!(); profiling::function_scope!();
assert_eq!(data.len(), w * h * 4); assert_eq!(data.len(), w * h * 4);
assert!( assert!(
w <= self.max_texture_side && h <= self.max_texture_side, w <= self.max_texture_side && h <= self.max_texture_side,
@ -610,7 +610,7 @@ impl Painter {
let level = 0; let level = 0;
if let Some([x, y]) = pos { if let Some([x, y]) = pos {
crate::profile_scope!("gl.tex_sub_image_2d"); profiling::scope!("gl.tex_sub_image_2d");
self.gl.tex_sub_image_2d( self.gl.tex_sub_image_2d(
glow::TEXTURE_2D, glow::TEXTURE_2D,
level, level,
@ -625,7 +625,7 @@ impl Painter {
check_for_gl_error!(&self.gl, "tex_sub_image_2d"); check_for_gl_error!(&self.gl, "tex_sub_image_2d");
} else { } else {
let border = 0; let border = 0;
crate::profile_scope!("gl.tex_image_2d"); profiling::scope!("gl.tex_image_2d");
self.gl.tex_image_2d( self.gl.tex_image_2d(
glow::TEXTURE_2D, glow::TEXTURE_2D,
level, level,
@ -675,7 +675,7 @@ impl Painter {
} }
pub fn read_screen_rgba(&self, [w, h]: [u32; 2]) -> egui::ColorImage { pub fn read_screen_rgba(&self, [w, h]: [u32; 2]) -> egui::ColorImage {
crate::profile_function!(); profiling::function_scope!();
let mut pixels = vec![0_u8; (w * h * 4) as usize]; let mut pixels = vec![0_u8; (w * h * 4) as usize];
unsafe { unsafe {
@ -700,8 +700,7 @@ impl Painter {
} }
pub fn read_screen_rgb(&self, [w, h]: [u32; 2]) -> Vec<u8> { pub fn read_screen_rgb(&self, [w, h]: [u32; 2]) -> Vec<u8> {
crate::profile_function!(); profiling::function_scope!();
let mut pixels = vec![0_u8; (w * h * 3) as usize]; let mut pixels = vec![0_u8; (w * h * 3) as usize];
unsafe { unsafe {
self.gl.read_pixels( self.gl.read_pixels(
@ -748,7 +747,7 @@ impl Painter {
} }
pub fn clear(gl: &glow::Context, screen_size_in_pixels: [u32; 2], clear_color: [f32; 4]) { pub fn clear(gl: &glow::Context, screen_size_in_pixels: [u32; 2], clear_color: [f32; 4]) {
crate::profile_function!(); profiling::function_scope!();
unsafe { unsafe {
gl.disable(glow::SCISSOR_TEST); gl.disable(glow::SCISSOR_TEST);

View File

@ -55,11 +55,6 @@ log = ["dep:log"]
## [`mint`](https://docs.rs/mint) enables interoperability with other math libraries such as [`glam`](https://docs.rs/glam) and [`nalgebra`](https://docs.rs/nalgebra). ## [`mint`](https://docs.rs/mint) enables interoperability with other math libraries such as [`glam`](https://docs.rs/glam) and [`nalgebra`](https://docs.rs/nalgebra).
mint = ["emath/mint"] mint = ["emath/mint"]
## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate.
##
## Only enabled on native, because of the low resolution (1ms) of clocks in browsers.
puffin = ["dep:puffin"]
## Enable parallel tessellation using [`rayon`](https://docs.rs/rayon). ## Enable parallel tessellation using [`rayon`](https://docs.rs/rayon).
## ##
## This can help performance for graphics-intense applications. ## This can help performance for graphics-intense applications.
@ -79,6 +74,7 @@ ab_glyph = "0.2.11"
ahash.workspace = true ahash.workspace = true
nohash-hasher.workspace = true nohash-hasher.workspace = true
parking_lot.workspace = true # Using parking_lot over std::sync::Mutex gives 50% speedups in some real-world scenarios. parking_lot.workspace = true # Using parking_lot over std::sync::Mutex gives 50% speedups in some real-world scenarios.
profiling = { workspace = true}
#! ### Optional dependencies #! ### Optional dependencies
bytemuck = { workspace = true, optional = true, features = ["derive"] } bytemuck = { workspace = true, optional = true, features = ["derive"] }
@ -87,7 +83,6 @@ bytemuck = { workspace = true, optional = true, features = ["derive"] }
document-features = { workspace = true, optional = true } document-features = { workspace = true, optional = true }
log = { workspace = true, optional = true } log = { workspace = true, optional = true }
puffin = { workspace = true, optional = true }
rayon = { version = "1.7", optional = true } rayon = { version = "1.7", optional = true }
## Allow serialization using [`serde`](https://docs.rs/serde) . ## Allow serialization using [`serde`](https://docs.rs/serde) .

View File

@ -143,33 +143,3 @@ pub enum Primitive {
/// Was epaint compiled with the `rayon` feature? /// Was epaint compiled with the `rayon` feature?
pub const HAS_RAYON: bool = cfg!(feature = "rayon"); pub const HAS_RAYON: bool = cfg!(feature = "rayon");
// ---------------------------------------------------------------------------
mod profiling_scopes {
#![allow(unused_macros)]
#![allow(unused_imports)]
/// Profiling macro for feature "puffin"
macro_rules! profile_function {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
puffin::profile_function!($($arg)*);
};
}
pub(crate) use profile_function;
/// Profiling macro for feature "puffin"
macro_rules! profile_scope {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
puffin::profile_scope!($($arg)*);
};
}
pub(crate) use profile_scope;
}
#[allow(unused_imports)]
pub(crate) use profiling_scopes::{profile_function, profile_scope};

View File

@ -85,7 +85,7 @@ impl Mesh {
/// Are all indices within the bounds of the contained vertices? /// Are all indices within the bounds of the contained vertices?
pub fn is_valid(&self) -> bool { pub fn is_valid(&self) -> bool {
crate::profile_function!(); profiling::function_scope!();
if let Ok(n) = u32::try_from(self.vertices.len()) { if let Ok(n) = u32::try_from(self.vertices.len()) {
self.indices.iter().all(|&i| i < n) self.indices.iter().all(|&i| i < n)
@ -111,7 +111,7 @@ impl Mesh {
/// ///
/// Panics when `other` mesh has a different texture. /// Panics when `other` mesh has a different texture.
pub fn append(&mut self, other: Self) { pub fn append(&mut self, other: Self) {
crate::profile_function!(); profiling::function_scope!();
debug_assert!(other.is_valid()); debug_assert!(other.is_valid());
if self.is_empty() { if self.is_empty() {

View File

@ -1363,7 +1363,7 @@ impl Tessellator {
self.tessellate_ellipse(ellipse, out); self.tessellate_ellipse(ellipse, out);
} }
Shape::Mesh(mesh) => { Shape::Mesh(mesh) => {
crate::profile_scope!("mesh"); profiling::scope!("mesh");
if self.options.validate_meshes && !mesh.is_valid() { if self.options.validate_meshes && !mesh.is_valid() {
debug_assert!(false, "Invalid Mesh in Shape::Mesh"); debug_assert!(false, "Invalid Mesh in Shape::Mesh");
@ -1596,7 +1596,7 @@ impl Tessellator {
return; return;
} }
crate::profile_function!(); profiling::function_scope!();
let PathShape { let PathShape {
points, points,
@ -1977,7 +1977,7 @@ impl Tessellator {
/// A list of clip rectangles with matching [`Mesh`]. /// A list of clip rectangles with matching [`Mesh`].
#[allow(unused_mut)] #[allow(unused_mut)]
pub fn tessellate_shapes(&mut self, mut shapes: Vec<ClippedShape>) -> Vec<ClippedPrimitive> { pub fn tessellate_shapes(&mut self, mut shapes: Vec<ClippedShape>) -> Vec<ClippedPrimitive> {
crate::profile_function!(); profiling::function_scope!();
#[cfg(feature = "rayon")] #[cfg(feature = "rayon")]
if self.options.parallel_tessellation { if self.options.parallel_tessellation {
@ -1987,7 +1987,7 @@ impl Tessellator {
let mut clipped_primitives: Vec<ClippedPrimitive> = Vec::default(); let mut clipped_primitives: Vec<ClippedPrimitive> = Vec::default();
{ {
crate::profile_scope!("tessellate"); profiling::scope!("tessellate");
for clipped_shape in shapes { for clipped_shape in shapes {
self.tessellate_clipped_shape(clipped_shape, &mut clipped_primitives); self.tessellate_clipped_shape(clipped_shape, &mut clipped_primitives);
} }
@ -2024,7 +2024,7 @@ impl Tessellator {
/// then replace the original shape with their tessellated meshes. /// then replace the original shape with their tessellated meshes.
#[cfg(feature = "rayon")] #[cfg(feature = "rayon")]
fn parallel_tessellation_of_large_shapes(&self, shapes: &mut [ClippedShape]) { fn parallel_tessellation_of_large_shapes(&self, shapes: &mut [ClippedShape]) {
crate::profile_function!(); profiling::function_scope!();
use rayon::prelude::*; use rayon::prelude::*;
@ -2054,7 +2054,7 @@ impl Tessellator {
.enumerate() .enumerate()
.filter(|(_, clipped_shape)| should_parallelize(&clipped_shape.shape)) .filter(|(_, clipped_shape)| should_parallelize(&clipped_shape.shape))
.map(|(index, clipped_shape)| { .map(|(index, clipped_shape)| {
crate::profile_scope!("tessellate_big_shape"); profiling::scope!("tessellate_big_shape");
// TODO(emilk): reuse tessellator in a thread local // TODO(emilk): reuse tessellator in a thread local
let mut tessellator = (*self).clone(); let mut tessellator = (*self).clone();
let mut mesh = Mesh::default(); let mut mesh = Mesh::default();
@ -2063,7 +2063,7 @@ impl Tessellator {
}) })
.collect(); .collect();
crate::profile_scope!("distribute results", tessellated.len().to_string()); profiling::scope!("distribute results", tessellated.len().to_string());
for (index, mesh) in tessellated { for (index, mesh) in tessellated {
shapes[index].shape = Shape::Mesh(mesh); shapes[index].shape = Shape::Mesh(mesh);
} }

View File

@ -7,6 +7,9 @@ edition = "2021"
rust-version = "1.80" rust-version = "1.80"
publish = false publish = false
[package.metadata.cargo-machete]
ignored = ["profiling"]
[lints] [lints]
workspace = true workspace = true
@ -18,7 +21,6 @@ wgpu = ["eframe/wgpu"]
[dependencies] [dependencies]
eframe = { workspace = true, features = [ eframe = { workspace = true, features = [
"default", "default",
"puffin",
"__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO
] } ] }
env_logger = { version = "0.10", default-features = false, features = [ env_logger = { version = "0.10", default-features = false, features = [
@ -28,3 +30,4 @@ env_logger = { version = "0.10", default-features = false, features = [
log = { workspace = true } log = { workspace = true }
puffin = "0.19" puffin = "0.19"
puffin_http = "0.16" puffin_http = "0.16"
profiling = {workspace = true, features = ["profile-with-puffin"] }