Add more puffin profile scopes to `eframe` (#3330)
* Add puffin profile scopes to the startup and running of eframe * puffin_profiler example: start puffin right away * cargo format let-else statements * More profile scopes * Add some `#[inline]` * Standardize puffin profile scope definitions * standardize again * Silence warning when puffin is disabled
This commit is contained in:
parent
2bc6814acc
commit
fc3bddd0cf
|
|
@ -1158,6 +1158,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);
|
||||||
storage
|
storage
|
||||||
.get_string(key)
|
.get_string(key)
|
||||||
.and_then(|value| match ron::from_str(&value) {
|
.and_then(|value| match ron::from_str(&value) {
|
||||||
|
|
@ -1172,6 +1173,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);
|
||||||
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),
|
||||||
|
|
|
||||||
|
|
@ -324,7 +324,6 @@ pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
mod profiling_scopes {
|
mod profiling_scopes {
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
|
|
@ -333,6 +332,7 @@ mod profiling_scopes {
|
||||||
macro_rules! profile_function {
|
macro_rules! profile_function {
|
||||||
($($arg: tt)*) => {
|
($($arg: tt)*) => {
|
||||||
#[cfg(feature = "puffin")]
|
#[cfg(feature = "puffin")]
|
||||||
|
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
|
||||||
puffin::profile_function!($($arg)*);
|
puffin::profile_function!($($arg)*);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -342,11 +342,12 @@ mod profiling_scopes {
|
||||||
macro_rules! profile_scope {
|
macro_rules! profile_scope {
|
||||||
($($arg: tt)*) => {
|
($($arg: tt)*) => {
|
||||||
#[cfg(feature = "puffin")]
|
#[cfg(feature = "puffin")]
|
||||||
|
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
|
||||||
puffin::profile_scope!($($arg)*);
|
puffin::profile_scope!($($arg)*);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub(crate) use profile_scope;
|
pub(crate) use profile_scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[allow(unused_imports)]
|
||||||
pub(crate) use profiling_scopes::*;
|
pub(crate) use profiling_scopes::*;
|
||||||
|
|
|
||||||
|
|
@ -622,6 +622,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!();
|
||||||
#[cfg(feature = "persistence")]
|
#[cfg(feature = "persistence")]
|
||||||
{
|
{
|
||||||
epi::get_value(_storage?, STORAGE_WINDOW_KEY)
|
epi::get_value(_storage?, STORAGE_WINDOW_KEY)
|
||||||
|
|
@ -631,6 +632,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!();
|
||||||
#[cfg(feature = "persistence")]
|
#[cfg(feature = "persistence")]
|
||||||
{
|
{
|
||||||
epi::get_value(_storage?, STORAGE_EGUI_MEMORY_KEY)
|
epi::get_value(_storage?, STORAGE_EGUI_MEMORY_KEY)
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,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");
|
||||||
join_handle.join().ok();
|
join_handle.join().ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -39,6 +40,7 @@ impl Drop for FileStorage {
|
||||||
impl FileStorage {
|
impl FileStorage {
|
||||||
/// Store the state in this .ron file.
|
/// Store the state in this .ron file.
|
||||||
fn from_ron_filepath(ron_filepath: impl Into<PathBuf>) -> Self {
|
fn from_ron_filepath(ron_filepath: impl Into<PathBuf>) -> Self {
|
||||||
|
crate::profile_function!();
|
||||||
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 {
|
||||||
|
|
@ -51,6 +53,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);
|
||||||
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!(
|
||||||
|
|
@ -83,6 +86,7 @@ impl crate::Storage for FileStorage {
|
||||||
|
|
||||||
fn flush(&mut self) {
|
fn flush(&mut self) {
|
||||||
if self.dirty {
|
if self.dirty {
|
||||||
|
crate::profile_function!();
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
|
|
||||||
let file_path = self.ron_filepath.clone();
|
let file_path = self.ron_filepath.clone();
|
||||||
|
|
@ -142,6 +146,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!();
|
||||||
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);
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ trait WinitApp {
|
||||||
fn create_event_loop_builder(
|
fn create_event_loop_builder(
|
||||||
native_options: &mut epi::NativeOptions,
|
native_options: &mut epi::NativeOptions,
|
||||||
) -> EventLoopBuilder<UserEvent> {
|
) -> EventLoopBuilder<UserEvent> {
|
||||||
|
crate::profile_function!();
|
||||||
let mut event_loop_builder = winit::event_loop::EventLoopBuilder::with_user_event();
|
let mut event_loop_builder = winit::event_loop::EventLoopBuilder::with_user_event();
|
||||||
|
|
||||||
if let Some(hook) = std::mem::take(&mut native_options.event_loop_builder) {
|
if let Some(hook) = std::mem::take(&mut native_options.event_loop_builder) {
|
||||||
|
|
@ -101,6 +102,14 @@ fn create_event_loop_builder(
|
||||||
event_loop_builder
|
event_loop_builder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_event_loop(native_options: &mut epi::NativeOptions) -> EventLoop<UserEvent> {
|
||||||
|
crate::profile_function!();
|
||||||
|
let mut builder = create_event_loop_builder(native_options);
|
||||||
|
|
||||||
|
crate::profile_scope!("EventLoopBuilder::build");
|
||||||
|
builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
/// Access a thread-local event loop.
|
/// Access a thread-local event loop.
|
||||||
///
|
///
|
||||||
/// We reuse the event-loop so we can support closing and opening an eframe window
|
/// We reuse the event-loop so we can support closing and opening an eframe window
|
||||||
|
|
@ -117,8 +126,7 @@ fn with_event_loop<R>(
|
||||||
// do that as part of the lazy thread local storage initialization and so we instead
|
// do that as part of the lazy thread local storage initialization and so we instead
|
||||||
// create the event loop lazily here
|
// create the event loop lazily here
|
||||||
let mut event_loop = event_loop.borrow_mut();
|
let mut event_loop = event_loop.borrow_mut();
|
||||||
let event_loop = event_loop
|
let event_loop = event_loop.get_or_insert_with(|| create_event_loop(&mut native_options));
|
||||||
.get_or_insert_with(|| create_event_loop_builder(&mut native_options).build());
|
|
||||||
f(event_loop, native_options)
|
f(event_loop, native_options)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -137,6 +145,8 @@ fn run_and_return(
|
||||||
let mut returned_result = Ok(());
|
let mut returned_result = Ok(());
|
||||||
|
|
||||||
event_loop.run_return(|event, event_loop, control_flow| {
|
event_loop.run_return(|event, event_loop, control_flow| {
|
||||||
|
crate::profile_scope!("winit_event", short_event_description(&event));
|
||||||
|
|
||||||
let event_result = match &event {
|
let event_result = match &event {
|
||||||
winit::event::Event::LoopDestroyed => {
|
winit::event::Event::LoopDestroyed => {
|
||||||
// On Mac, Cmd-Q we get here and then `run_return` doesn't return (despite its name),
|
// On Mac, Cmd-Q we get here and then `run_return` doesn't return (despite its name),
|
||||||
|
|
@ -266,6 +276,8 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||||
let mut next_repaint_time = Instant::now();
|
let mut next_repaint_time = Instant::now();
|
||||||
|
|
||||||
event_loop.run(move |event, event_loop, control_flow| {
|
event_loop.run(move |event, event_loop, control_flow| {
|
||||||
|
crate::profile_scope!("winit_event", short_event_description(&event));
|
||||||
|
|
||||||
let event_result = match event {
|
let event_result = match event {
|
||||||
winit::event::Event::LoopDestroyed => {
|
winit::event::Event::LoopDestroyed => {
|
||||||
log::debug!("Received Event::LoopDestroyed");
|
log::debug!("Received Event::LoopDestroyed");
|
||||||
|
|
@ -420,6 +432,8 @@ mod glow_integration {
|
||||||
native_options: &epi::NativeOptions,
|
native_options: &epi::NativeOptions,
|
||||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
|
crate::profile_function!();
|
||||||
|
|
||||||
use glutin::prelude::*;
|
use glutin::prelude::*;
|
||||||
// convert native options to glutin options
|
// convert native options to glutin options
|
||||||
let hardware_acceleration = match native_options.hardware_acceleration {
|
let hardware_acceleration = match native_options.hardware_acceleration {
|
||||||
|
|
@ -460,26 +474,35 @@ mod glow_integration {
|
||||||
"trying to create glutin Display with config: {:?}",
|
"trying to create glutin Display with config: {:?}",
|
||||||
&config_template_builder
|
&config_template_builder
|
||||||
);
|
);
|
||||||
// create gl display. this may probably create a window too on most platforms. definitely on `MS windows`. never on android.
|
|
||||||
let (window, gl_config) = glutin_winit::DisplayBuilder::new()
|
// Create GL display. This may probably create a window too on most platforms. Definitely on `MS windows`. Never on Android.
|
||||||
|
let display_builder = glutin_winit::DisplayBuilder::new()
|
||||||
// we might want to expose this option to users in the future. maybe using an env var or using native_options.
|
// we might want to expose this option to users in the future. maybe using an env var or using native_options.
|
||||||
.with_preference(glutin_winit::ApiPrefence::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150
|
.with_preference(glutin_winit::ApiPrefence::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150
|
||||||
.with_window_builder(Some(winit_window_builder.clone()))
|
.with_window_builder(Some(winit_window_builder.clone()));
|
||||||
.build(
|
|
||||||
event_loop,
|
let (window, gl_config) = {
|
||||||
config_template_builder.clone(),
|
crate::profile_scope!("DisplayBuilder::build");
|
||||||
|mut config_iterator| {
|
|
||||||
let config = config_iterator.next().expect(
|
display_builder
|
||||||
|
.build(
|
||||||
|
event_loop,
|
||||||
|
config_template_builder.clone(),
|
||||||
|
|mut config_iterator| {
|
||||||
|
let config = config_iterator.next().expect(
|
||||||
"failed to find a matching configuration for creating glutin config",
|
"failed to find a matching configuration for creating glutin config",
|
||||||
);
|
);
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"using the first config from config picker closure. config: {:?}",
|
"using the first config from config picker closure. config: {:?}",
|
||||||
&config
|
&config
|
||||||
);
|
);
|
||||||
config
|
config
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.map_err(|e| crate::Error::NoGlutinConfigs(config_template_builder.build(), e))?;
|
.map_err(|e| {
|
||||||
|
crate::Error::NoGlutinConfigs(config_template_builder.build(), e)
|
||||||
|
})?
|
||||||
|
};
|
||||||
|
|
||||||
let gl_display = gl_config.display();
|
let gl_display = gl_config.display();
|
||||||
log::debug!(
|
log::debug!(
|
||||||
|
|
@ -499,10 +522,15 @@ mod glow_integration {
|
||||||
let fallback_context_attributes = glutin::context::ContextAttributesBuilder::new()
|
let fallback_context_attributes = glutin::context::ContextAttributesBuilder::new()
|
||||||
.with_context_api(glutin::context::ContextApi::Gles(None))
|
.with_context_api(glutin::context::ContextApi::Gles(None))
|
||||||
.build(raw_window_handle);
|
.build(raw_window_handle);
|
||||||
let gl_context = match gl_config
|
|
||||||
.display()
|
let gl_context_result = {
|
||||||
.create_context(&gl_config, &context_attributes)
|
crate::profile_scope!("create_context");
|
||||||
{
|
gl_config
|
||||||
|
.display()
|
||||||
|
.create_context(&gl_config, &context_attributes)
|
||||||
|
};
|
||||||
|
|
||||||
|
let gl_context = match gl_context_result {
|
||||||
Ok(it) => it,
|
Ok(it) => it,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::warn!("failed to create context using default context attributes {context_attributes:?} due to error: {err}");
|
log::warn!("failed to create context using default context attributes {context_attributes:?} due to error: {err}");
|
||||||
|
|
@ -656,6 +684,7 @@ mod glow_integration {
|
||||||
native_options: epi::NativeOptions,
|
native_options: epi::NativeOptions,
|
||||||
app_creator: epi::AppCreator,
|
app_creator: epi::AppCreator,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
crate::profile_function!();
|
||||||
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(),
|
||||||
|
|
@ -693,6 +722,7 @@ mod glow_integration {
|
||||||
}
|
}
|
||||||
|
|
||||||
let gl = unsafe {
|
let gl = unsafe {
|
||||||
|
crate::profile_scope!("glow::Context::from_loader_function");
|
||||||
glow::Context::from_loader_function(|s| {
|
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");
|
||||||
|
|
@ -705,6 +735,7 @@ mod glow_integration {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_run_state(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) -> Result<()> {
|
fn init_run_state(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) -> Result<()> {
|
||||||
|
crate::profile_function!();
|
||||||
let storage = epi_integration::create_storage(
|
let storage = epi_integration::create_storage(
|
||||||
self.native_options
|
self.native_options
|
||||||
.app_id
|
.app_id
|
||||||
|
|
@ -814,6 +845,7 @@ mod glow_integration {
|
||||||
|
|
||||||
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!();
|
||||||
running
|
running
|
||||||
.integration
|
.integration
|
||||||
.save(running.app.as_mut(), running.gl_window.window.as_ref());
|
.save(running.app.as_mut(), running.gl_window.window.as_ref());
|
||||||
|
|
@ -823,125 +855,125 @@ mod glow_integration {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_ui_and_paint(&mut self) -> EventResult {
|
fn run_ui_and_paint(&mut self) -> EventResult {
|
||||||
if let Some(running) = &mut self.running {
|
let Some(running) = &mut self.running else {
|
||||||
if running.gl_window.window.is_none() {
|
return EventResult::Wait;
|
||||||
return EventResult::Wait;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "puffin")]
|
if running.gl_window.window.is_none() {
|
||||||
puffin::GlobalProfiler::lock().new_frame();
|
return EventResult::Wait;
|
||||||
crate::profile_scope!("frame");
|
}
|
||||||
|
|
||||||
let GlowWinitRunning {
|
#[cfg(feature = "puffin")]
|
||||||
gl_window,
|
puffin::GlobalProfiler::lock().new_frame();
|
||||||
gl,
|
crate::profile_scope!("frame");
|
||||||
app,
|
|
||||||
integration,
|
|
||||||
painter,
|
|
||||||
} = running;
|
|
||||||
|
|
||||||
let window = gl_window.window();
|
let GlowWinitRunning {
|
||||||
|
gl_window,
|
||||||
|
gl,
|
||||||
|
app,
|
||||||
|
integration,
|
||||||
|
painter,
|
||||||
|
} = running;
|
||||||
|
|
||||||
let screen_size_in_pixels: [u32; 2] = window.inner_size().into();
|
let window = gl_window.window();
|
||||||
|
|
||||||
egui_glow::painter::clear(
|
let screen_size_in_pixels: [u32; 2] = window.inner_size().into();
|
||||||
gl,
|
|
||||||
screen_size_in_pixels,
|
|
||||||
app.clear_color(&integration.egui_ctx.style().visuals),
|
|
||||||
);
|
|
||||||
|
|
||||||
let egui::FullOutput {
|
egui_glow::painter::clear(
|
||||||
platform_output,
|
gl,
|
||||||
repaint_after,
|
screen_size_in_pixels,
|
||||||
textures_delta,
|
app.clear_color(&integration.egui_ctx.style().visuals),
|
||||||
shapes,
|
);
|
||||||
} = integration.update(app.as_mut(), window);
|
|
||||||
|
|
||||||
integration.handle_platform_output(window, platform_output);
|
let egui::FullOutput {
|
||||||
|
platform_output,
|
||||||
|
repaint_after,
|
||||||
|
textures_delta,
|
||||||
|
shapes,
|
||||||
|
} = integration.update(app.as_mut(), window);
|
||||||
|
|
||||||
let clipped_primitives = {
|
integration.handle_platform_output(window, platform_output);
|
||||||
crate::profile_scope!("tessellate");
|
|
||||||
integration.egui_ctx.tessellate(shapes)
|
|
||||||
};
|
|
||||||
|
|
||||||
painter.paint_and_update_textures(
|
let clipped_primitives = {
|
||||||
screen_size_in_pixels,
|
crate::profile_scope!("tessellate");
|
||||||
integration.egui_ctx.pixels_per_point(),
|
integration.egui_ctx.tessellate(shapes)
|
||||||
&clipped_primitives,
|
};
|
||||||
&textures_delta,
|
|
||||||
);
|
|
||||||
|
|
||||||
let screenshot_requested = &mut integration.frame.output.screenshot_requested;
|
painter.paint_and_update_textures(
|
||||||
|
screen_size_in_pixels,
|
||||||
|
integration.egui_ctx.pixels_per_point(),
|
||||||
|
&clipped_primitives,
|
||||||
|
&textures_delta,
|
||||||
|
);
|
||||||
|
|
||||||
if *screenshot_requested {
|
let screenshot_requested = &mut integration.frame.output.screenshot_requested;
|
||||||
*screenshot_requested = false;
|
|
||||||
|
if *screenshot_requested {
|
||||||
|
*screenshot_requested = false;
|
||||||
|
let screenshot = painter.read_screen_rgba(screen_size_in_pixels);
|
||||||
|
integration.frame.screenshot.set(Some(screenshot));
|
||||||
|
}
|
||||||
|
|
||||||
|
integration.post_rendering(app.as_mut(), window);
|
||||||
|
|
||||||
|
{
|
||||||
|
crate::profile_scope!("swap_buffers");
|
||||||
|
gl_window.swap_buffers().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
integration.post_present(window);
|
||||||
|
|
||||||
|
#[cfg(feature = "__screenshot")]
|
||||||
|
// give it time to settle:
|
||||||
|
if integration.egui_ctx.frame_nr() == 2 {
|
||||||
|
if let Ok(path) = std::env::var("EFRAME_SCREENSHOT_TO") {
|
||||||
|
assert!(
|
||||||
|
path.ends_with(".png"),
|
||||||
|
"Expected EFRAME_SCREENSHOT_TO to end with '.png', got {path:?}"
|
||||||
|
);
|
||||||
let screenshot = painter.read_screen_rgba(screen_size_in_pixels);
|
let screenshot = painter.read_screen_rgba(screen_size_in_pixels);
|
||||||
integration.frame.screenshot.set(Some(screenshot));
|
image::save_buffer(
|
||||||
|
&path,
|
||||||
|
screenshot.as_raw(),
|
||||||
|
screenshot.width() as u32,
|
||||||
|
screenshot.height() as u32,
|
||||||
|
image::ColorType::Rgba8,
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|err| {
|
||||||
|
panic!("Failed to save screenshot to {path:?}: {err}");
|
||||||
|
});
|
||||||
|
eprintln!("Screenshot saved to {path:?}.");
|
||||||
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
integration.post_rendering(app.as_mut(), window);
|
let control_flow = if integration.should_close() {
|
||||||
|
EventResult::Exit
|
||||||
{
|
} else if repaint_after.is_zero() {
|
||||||
crate::profile_scope!("swap_buffers");
|
EventResult::RepaintNext
|
||||||
gl_window.swap_buffers().unwrap();
|
} else if let Some(repaint_after_instant) =
|
||||||
}
|
std::time::Instant::now().checked_add(repaint_after)
|
||||||
|
{
|
||||||
integration.post_present(window);
|
// if repaint_after is something huge and can't be added to Instant,
|
||||||
|
// we will use `ControlFlow::Wait` instead.
|
||||||
#[cfg(feature = "__screenshot")]
|
// technically, this might lead to some weird corner cases where the user *WANTS*
|
||||||
// give it time to settle:
|
// winit to use `WaitUntil(MAX_INSTANT)` explicitly. they can roll their own
|
||||||
if integration.egui_ctx.frame_nr() == 2 {
|
// egui backend impl i guess.
|
||||||
if let Ok(path) = std::env::var("EFRAME_SCREENSHOT_TO") {
|
EventResult::RepaintAt(repaint_after_instant)
|
||||||
assert!(
|
|
||||||
path.ends_with(".png"),
|
|
||||||
"Expected EFRAME_SCREENSHOT_TO to end with '.png', got {path:?}"
|
|
||||||
);
|
|
||||||
let screenshot = painter.read_screen_rgba(screen_size_in_pixels);
|
|
||||||
image::save_buffer(
|
|
||||||
&path,
|
|
||||||
screenshot.as_raw(),
|
|
||||||
screenshot.width() as u32,
|
|
||||||
screenshot.height() as u32,
|
|
||||||
image::ColorType::Rgba8,
|
|
||||||
)
|
|
||||||
.unwrap_or_else(|err| {
|
|
||||||
panic!("Failed to save screenshot to {path:?}: {err}");
|
|
||||||
});
|
|
||||||
eprintln!("Screenshot saved to {path:?}.");
|
|
||||||
std::process::exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let control_flow = if integration.should_close() {
|
|
||||||
EventResult::Exit
|
|
||||||
} else if repaint_after.is_zero() {
|
|
||||||
EventResult::RepaintNext
|
|
||||||
} else if let Some(repaint_after_instant) =
|
|
||||||
std::time::Instant::now().checked_add(repaint_after)
|
|
||||||
{
|
|
||||||
// if repaint_after is something huge and can't be added to Instant,
|
|
||||||
// we will use `ControlFlow::Wait` instead.
|
|
||||||
// technically, this might lead to some weird corner cases where the user *WANTS*
|
|
||||||
// winit to use `WaitUntil(MAX_INSTANT)` explicitly. they can roll their own
|
|
||||||
// egui backend impl i guess.
|
|
||||||
EventResult::RepaintAt(repaint_after_instant)
|
|
||||||
} else {
|
|
||||||
EventResult::Wait
|
|
||||||
};
|
|
||||||
|
|
||||||
integration.maybe_autosave(app.as_mut(), window);
|
|
||||||
|
|
||||||
if window.is_minimized() == Some(true) {
|
|
||||||
// On Mac, a minimized Window uses up all CPU:
|
|
||||||
// https://github.com/emilk/egui/issues/325
|
|
||||||
crate::profile_scope!("bg_sleep");
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
|
||||||
}
|
|
||||||
|
|
||||||
control_flow
|
|
||||||
} else {
|
} else {
|
||||||
EventResult::Wait
|
EventResult::Wait
|
||||||
|
};
|
||||||
|
|
||||||
|
integration.maybe_autosave(app.as_mut(), window);
|
||||||
|
|
||||||
|
if window.is_minimized() == Some(true) {
|
||||||
|
// On Mac, a minimized Window uses up all CPU:
|
||||||
|
// https://github.com/emilk/egui/issues/325
|
||||||
|
crate::profile_scope!("bg_sleep");
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
control_flow
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
fn on_event(
|
||||||
|
|
@ -1070,14 +1102,14 @@ mod glow_integration {
|
||||||
run_and_return(event_loop, glow_eframe)
|
run_and_return(event_loop, glow_eframe)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
let event_loop = create_event_loop_builder(&mut native_options).build();
|
let event_loop = create_event_loop(&mut native_options);
|
||||||
let glow_eframe = GlowWinitApp::new(&event_loop, app_name, native_options, app_creator);
|
let glow_eframe = GlowWinitApp::new(&event_loop, app_name, native_options, app_creator);
|
||||||
run_and_exit(event_loop, glow_eframe);
|
run_and_exit(event_loop, glow_eframe);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "ios")]
|
#[cfg(target_os = "ios")]
|
||||||
{
|
{
|
||||||
let event_loop = create_event_loop_builder(&mut native_options).build();
|
let event_loop = create_event_loop(&mut native_options);
|
||||||
let glow_eframe = GlowWinitApp::new(&event_loop, app_name, native_options, app_creator);
|
let glow_eframe = GlowWinitApp::new(&event_loop, app_name, native_options, app_creator);
|
||||||
run_and_exit(event_loop, glow_eframe);
|
run_and_exit(event_loop, glow_eframe);
|
||||||
}
|
}
|
||||||
|
|
@ -1125,6 +1157,7 @@ mod wgpu_integration {
|
||||||
native_options: epi::NativeOptions,
|
native_options: epi::NativeOptions,
|
||||||
app_creator: epi::AppCreator,
|
app_creator: epi::AppCreator,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
crate::profile_function!();
|
||||||
#[cfg(feature = "__screenshot")]
|
#[cfg(feature = "__screenshot")]
|
||||||
assert!(
|
assert!(
|
||||||
std::env::var("EFRAME_SCREENSHOT_TO").is_err(),
|
std::env::var("EFRAME_SCREENSHOT_TO").is_err(),
|
||||||
|
|
@ -1148,6 +1181,7 @@ mod wgpu_integration {
|
||||||
title: &str,
|
title: &str,
|
||||||
native_options: &NativeOptions,
|
native_options: &NativeOptions,
|
||||||
) -> std::result::Result<winit::window::Window, winit::error::OsError> {
|
) -> std::result::Result<winit::window::Window, winit::error::OsError> {
|
||||||
|
crate::profile_function!();
|
||||||
let window_settings = epi_integration::load_window_settings(storage);
|
let window_settings = epi_integration::load_window_settings(storage);
|
||||||
let window_builder =
|
let window_builder =
|
||||||
epi_integration::window_builder(event_loop, title, native_options, window_settings);
|
epi_integration::window_builder(event_loop, title, native_options, window_settings);
|
||||||
|
|
@ -1167,6 +1201,7 @@ mod wgpu_integration {
|
||||||
) -> std::result::Result<(), egui_wgpu::WgpuError> {
|
) -> std::result::Result<(), egui_wgpu::WgpuError> {
|
||||||
self.window = Some(window);
|
self.window = Some(window);
|
||||||
if let Some(running) = &mut self.running {
|
if let Some(running) = &mut self.running {
|
||||||
|
crate::profile_function!();
|
||||||
pollster::block_on(running.painter.set_window(self.window.as_ref()))?;
|
pollster::block_on(running.painter.set_window(self.window.as_ref()))?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -1188,6 +1223,8 @@ mod wgpu_integration {
|
||||||
storage: Option<Box<dyn epi::Storage>>,
|
storage: Option<Box<dyn epi::Storage>>,
|
||||||
window: winit::window::Window,
|
window: winit::window::Window,
|
||||||
) -> std::result::Result<(), egui_wgpu::WgpuError> {
|
) -> std::result::Result<(), egui_wgpu::WgpuError> {
|
||||||
|
crate::profile_function!();
|
||||||
|
|
||||||
#[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(
|
||||||
self.native_options.wgpu_options.clone(),
|
self.native_options.wgpu_options.clone(),
|
||||||
|
|
@ -1288,6 +1325,7 @@ mod wgpu_integration {
|
||||||
|
|
||||||
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!();
|
||||||
running
|
running
|
||||||
.integration
|
.integration
|
||||||
.save(running.app.as_mut(), self.window.as_ref());
|
.save(running.app.as_mut(), self.window.as_ref());
|
||||||
|
|
@ -1303,76 +1341,76 @@ mod wgpu_integration {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_ui_and_paint(&mut self) -> EventResult {
|
fn run_ui_and_paint(&mut self) -> EventResult {
|
||||||
if let (Some(running), Some(window)) = (&mut self.running, &self.window) {
|
let (Some(running), Some(window)) = (&mut self.running, &self.window) else {
|
||||||
#[cfg(feature = "puffin")]
|
return EventResult::Wait;
|
||||||
puffin::GlobalProfiler::lock().new_frame();
|
};
|
||||||
crate::profile_scope!("frame");
|
|
||||||
|
|
||||||
let WgpuWinitRunning {
|
#[cfg(feature = "puffin")]
|
||||||
app,
|
puffin::GlobalProfiler::lock().new_frame();
|
||||||
integration,
|
crate::profile_scope!("frame");
|
||||||
painter,
|
|
||||||
} = running;
|
|
||||||
|
|
||||||
let egui::FullOutput {
|
let WgpuWinitRunning {
|
||||||
platform_output,
|
app,
|
||||||
repaint_after,
|
integration,
|
||||||
textures_delta,
|
painter,
|
||||||
shapes,
|
} = running;
|
||||||
} = integration.update(app.as_mut(), window);
|
|
||||||
|
|
||||||
integration.handle_platform_output(window, platform_output);
|
let egui::FullOutput {
|
||||||
|
platform_output,
|
||||||
|
repaint_after,
|
||||||
|
textures_delta,
|
||||||
|
shapes,
|
||||||
|
} = integration.update(app.as_mut(), window);
|
||||||
|
|
||||||
let clipped_primitives = {
|
integration.handle_platform_output(window, platform_output);
|
||||||
crate::profile_scope!("tessellate");
|
|
||||||
integration.egui_ctx.tessellate(shapes)
|
|
||||||
};
|
|
||||||
|
|
||||||
let screenshot_requested = &mut integration.frame.output.screenshot_requested;
|
let clipped_primitives = {
|
||||||
|
crate::profile_scope!("tessellate");
|
||||||
|
integration.egui_ctx.tessellate(shapes)
|
||||||
|
};
|
||||||
|
|
||||||
let screenshot = painter.paint_and_update_textures(
|
let screenshot_requested = &mut integration.frame.output.screenshot_requested;
|
||||||
integration.egui_ctx.pixels_per_point(),
|
|
||||||
app.clear_color(&integration.egui_ctx.style().visuals),
|
|
||||||
&clipped_primitives,
|
|
||||||
&textures_delta,
|
|
||||||
*screenshot_requested,
|
|
||||||
);
|
|
||||||
*screenshot_requested = false;
|
|
||||||
integration.frame.screenshot.set(screenshot);
|
|
||||||
|
|
||||||
integration.post_rendering(app.as_mut(), window);
|
let screenshot = painter.paint_and_update_textures(
|
||||||
integration.post_present(window);
|
integration.egui_ctx.pixels_per_point(),
|
||||||
|
app.clear_color(&integration.egui_ctx.style().visuals),
|
||||||
|
&clipped_primitives,
|
||||||
|
&textures_delta,
|
||||||
|
*screenshot_requested,
|
||||||
|
);
|
||||||
|
*screenshot_requested = false;
|
||||||
|
integration.frame.screenshot.set(screenshot);
|
||||||
|
|
||||||
let control_flow = if integration.should_close() {
|
integration.post_rendering(app.as_mut(), window);
|
||||||
EventResult::Exit
|
integration.post_present(window);
|
||||||
} else if repaint_after.is_zero() {
|
|
||||||
EventResult::RepaintNext
|
|
||||||
} else if let Some(repaint_after_instant) =
|
|
||||||
std::time::Instant::now().checked_add(repaint_after)
|
|
||||||
{
|
|
||||||
// if repaint_after is something huge and can't be added to Instant,
|
|
||||||
// we will use `ControlFlow::Wait` instead.
|
|
||||||
// technically, this might lead to some weird corner cases where the user *WANTS*
|
|
||||||
// winit to use `WaitUntil(MAX_INSTANT)` explicitly. they can roll their own
|
|
||||||
// egui backend impl i guess.
|
|
||||||
EventResult::RepaintAt(repaint_after_instant)
|
|
||||||
} else {
|
|
||||||
EventResult::Wait
|
|
||||||
};
|
|
||||||
|
|
||||||
integration.maybe_autosave(app.as_mut(), window);
|
let control_flow = if integration.should_close() {
|
||||||
|
EventResult::Exit
|
||||||
if window.is_minimized() == Some(true) {
|
} else if repaint_after.is_zero() {
|
||||||
// On Mac, a minimized Window uses up all CPU:
|
EventResult::RepaintNext
|
||||||
// https://github.com/emilk/egui/issues/325
|
} else if let Some(repaint_after_instant) =
|
||||||
crate::profile_scope!("bg_sleep");
|
std::time::Instant::now().checked_add(repaint_after)
|
||||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
{
|
||||||
}
|
// if repaint_after is something huge and can't be added to Instant,
|
||||||
|
// we will use `ControlFlow::Wait` instead.
|
||||||
control_flow
|
// technically, this might lead to some weird corner cases where the user *WANTS*
|
||||||
|
// winit to use `WaitUntil(MAX_INSTANT)` explicitly. they can roll their own
|
||||||
|
// egui backend impl i guess.
|
||||||
|
EventResult::RepaintAt(repaint_after_instant)
|
||||||
} else {
|
} else {
|
||||||
EventResult::Wait
|
EventResult::Wait
|
||||||
|
};
|
||||||
|
|
||||||
|
integration.maybe_autosave(app.as_mut(), window);
|
||||||
|
|
||||||
|
if window.is_minimized() == Some(true) {
|
||||||
|
// On Mac, a minimized Window uses up all CPU:
|
||||||
|
// https://github.com/emilk/egui/issues/325
|
||||||
|
crate::profile_scope!("bg_sleep");
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
control_flow
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
fn on_event(
|
||||||
|
|
@ -1517,14 +1555,14 @@ mod wgpu_integration {
|
||||||
run_and_return(event_loop, wgpu_eframe)
|
run_and_return(event_loop, wgpu_eframe)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
let event_loop = create_event_loop_builder(&mut native_options).build();
|
let event_loop = create_event_loop(&mut native_options);
|
||||||
let wgpu_eframe = WgpuWinitApp::new(&event_loop, app_name, native_options, app_creator);
|
let wgpu_eframe = WgpuWinitApp::new(&event_loop, app_name, native_options, app_creator);
|
||||||
run_and_exit(event_loop, wgpu_eframe);
|
run_and_exit(event_loop, wgpu_eframe);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "ios")]
|
#[cfg(target_os = "ios")]
|
||||||
{
|
{
|
||||||
let event_loop = create_event_loop_builder(&mut native_options).build();
|
let event_loop = create_event_loop(&mut native_options);
|
||||||
let wgpu_eframe = WgpuWinitApp::new(&event_loop, app_name, native_options, app_creator);
|
let wgpu_eframe = WgpuWinitApp::new(&event_loop, app_name, native_options, app_creator);
|
||||||
run_and_exit(event_loop, wgpu_eframe);
|
run_and_exit(event_loop, wgpu_eframe);
|
||||||
}
|
}
|
||||||
|
|
@ -1551,3 +1589,67 @@ fn system_theme(window: &winit::window::Window, options: &NativeOptions) -> Opti
|
||||||
fn extremely_far_future() -> std::time::Instant {
|
fn extremely_far_future() -> std::time::Instant {
|
||||||
std::time::Instant::now() + std::time::Duration::from_secs(10_000_000_000)
|
std::time::Instant::now() + std::time::Duration::from_secs(10_000_000_000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For the puffin profiler!
|
||||||
|
#[allow(dead_code)] // Only used for profiling
|
||||||
|
fn short_event_description(event: &winit::event::Event<'_, UserEvent>) -> &'static str {
|
||||||
|
use winit::event::{DeviceEvent, Event, StartCause, WindowEvent};
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Event::Suspended => "Event::Suspended",
|
||||||
|
Event::Resumed => "Event::Resumed",
|
||||||
|
Event::MainEventsCleared => "Event::MainEventsCleared",
|
||||||
|
Event::RedrawRequested(_) => "Event::RedrawRequested",
|
||||||
|
Event::RedrawEventsCleared => "Event::RedrawEventsCleared",
|
||||||
|
Event::LoopDestroyed => "Event::LoopDestroyed",
|
||||||
|
Event::UserEvent(user_event) => match user_event {
|
||||||
|
UserEvent::RequestRepaint { .. } => "UserEvent::RequestRepaint",
|
||||||
|
#[cfg(feature = "accesskit")]
|
||||||
|
UserEvent::AccessKitActionRequest(_) => "UserEvent::AccessKitActionRequest",
|
||||||
|
},
|
||||||
|
Event::DeviceEvent { event, .. } => match event {
|
||||||
|
DeviceEvent::Added { .. } => "DeviceEvent::Added",
|
||||||
|
DeviceEvent::Removed { .. } => "DeviceEvent::Removed",
|
||||||
|
DeviceEvent::MouseMotion { .. } => "DeviceEvent::MouseMotion",
|
||||||
|
DeviceEvent::MouseWheel { .. } => "DeviceEvent::MouseWheel",
|
||||||
|
DeviceEvent::Motion { .. } => "DeviceEvent::Motion",
|
||||||
|
DeviceEvent::Button { .. } => "DeviceEvent::Button",
|
||||||
|
DeviceEvent::Key { .. } => "DeviceEvent::Key",
|
||||||
|
DeviceEvent::Text { .. } => "DeviceEvent::Text",
|
||||||
|
},
|
||||||
|
Event::NewEvents(start_cause) => match start_cause {
|
||||||
|
StartCause::ResumeTimeReached { .. } => "NewEvents::ResumeTimeReached",
|
||||||
|
StartCause::WaitCancelled { .. } => "NewEvents::WaitCancelled",
|
||||||
|
StartCause::Poll => "NewEvents::Poll",
|
||||||
|
StartCause::Init => "NewEvents::Init",
|
||||||
|
},
|
||||||
|
Event::WindowEvent { event, .. } => match event {
|
||||||
|
WindowEvent::Resized { .. } => "WindowEvent::Resized",
|
||||||
|
WindowEvent::Moved { .. } => "WindowEvent::Moved",
|
||||||
|
WindowEvent::CloseRequested { .. } => "WindowEvent::CloseRequested",
|
||||||
|
WindowEvent::Destroyed { .. } => "WindowEvent::Destroyed",
|
||||||
|
WindowEvent::DroppedFile { .. } => "WindowEvent::DroppedFile",
|
||||||
|
WindowEvent::HoveredFile { .. } => "WindowEvent::HoveredFile",
|
||||||
|
WindowEvent::HoveredFileCancelled { .. } => "WindowEvent::HoveredFileCancelled",
|
||||||
|
WindowEvent::ReceivedCharacter { .. } => "WindowEvent::ReceivedCharacter",
|
||||||
|
WindowEvent::Focused { .. } => "WindowEvent::Focused",
|
||||||
|
WindowEvent::KeyboardInput { .. } => "WindowEvent::KeyboardInput",
|
||||||
|
WindowEvent::ModifiersChanged { .. } => "WindowEvent::ModifiersChanged",
|
||||||
|
WindowEvent::Ime { .. } => "WindowEvent::Ime",
|
||||||
|
WindowEvent::CursorMoved { .. } => "WindowEvent::CursorMoved",
|
||||||
|
WindowEvent::CursorEntered { .. } => "WindowEvent::CursorEntered",
|
||||||
|
WindowEvent::CursorLeft { .. } => "WindowEvent::CursorLeft",
|
||||||
|
WindowEvent::MouseWheel { .. } => "WindowEvent::MouseWheel",
|
||||||
|
WindowEvent::MouseInput { .. } => "WindowEvent::MouseInput",
|
||||||
|
WindowEvent::TouchpadMagnify { .. } => "WindowEvent::TouchpadMagnify",
|
||||||
|
WindowEvent::SmartMagnify { .. } => "WindowEvent::SmartMagnify",
|
||||||
|
WindowEvent::TouchpadRotate { .. } => "WindowEvent::TouchpadRotate",
|
||||||
|
WindowEvent::TouchpadPressure { .. } => "WindowEvent::TouchpadPressure",
|
||||||
|
WindowEvent::AxisMotion { .. } => "WindowEvent::AxisMotion",
|
||||||
|
WindowEvent::Touch { .. } => "WindowEvent::Touch",
|
||||||
|
WindowEvent::ScaleFactorChanged { .. } => "WindowEvent::ScaleFactorChanged",
|
||||||
|
WindowEvent::ThemeChanged { .. } => "WindowEvent::ThemeChanged",
|
||||||
|
WindowEvent::Occluded { .. } => "WindowEvent::Occluded",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -203,22 +203,30 @@ pub fn depth_format_from_bits(depth_buffer: u8, stencil_buffer: u8) -> Option<wg
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Profiling macro for feature "puffin"
|
mod profiling_scopes {
|
||||||
macro_rules! profile_function {
|
#![allow(unused_macros)]
|
||||||
($($arg: tt)*) => {
|
#![allow(unused_imports)]
|
||||||
#[cfg(feature = "puffin")]
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
puffin::profile_function!($($arg)*);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
pub(crate) use profile_function;
|
|
||||||
|
|
||||||
/// Profiling macro for feature "puffin"
|
/// Profiling macro for feature "puffin"
|
||||||
macro_rules! profile_scope {
|
macro_rules! profile_function {
|
||||||
($($arg: tt)*) => {
|
($($arg: tt)*) => {
|
||||||
#[cfg(feature = "puffin")]
|
#[cfg(feature = "puffin")]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
|
||||||
puffin::profile_scope!($($arg)*);
|
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;
|
||||||
}
|
}
|
||||||
pub(crate) use profile_scope;
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
pub(crate) use profiling_scopes::*;
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,7 @@ impl Painter {
|
||||||
render_state: &RenderState,
|
render_state: &RenderState,
|
||||||
present_mode: wgpu::PresentMode,
|
present_mode: wgpu::PresentMode,
|
||||||
) {
|
) {
|
||||||
|
crate::profile_function!();
|
||||||
let usage = if surface_state.supports_screenshot {
|
let usage = if surface_state.supports_screenshot {
|
||||||
wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST
|
wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -181,6 +182,7 @@ impl Painter {
|
||||||
&mut self,
|
&mut self,
|
||||||
window: Option<&winit::window::Window>,
|
window: Option<&winit::window::Window>,
|
||||||
) -> Result<(), crate::WgpuError> {
|
) -> Result<(), crate::WgpuError> {
|
||||||
|
crate::profile_function!();
|
||||||
match window {
|
match window {
|
||||||
Some(window) => {
|
Some(window) => {
|
||||||
let surface = unsafe { self.instance.create_surface(&window)? };
|
let surface = unsafe { self.instance.create_surface(&window)? };
|
||||||
|
|
@ -254,6 +256,7 @@ impl Painter {
|
||||||
width_in_pixels: u32,
|
width_in_pixels: u32,
|
||||||
height_in_pixels: u32,
|
height_in_pixels: u32,
|
||||||
) {
|
) {
|
||||||
|
crate::profile_function!();
|
||||||
let render_state = self.render_state.as_ref().unwrap();
|
let render_state = self.render_state.as_ref().unwrap();
|
||||||
let surface_state = self.surface_state.as_mut().unwrap();
|
let surface_state = self.surface_state.as_mut().unwrap();
|
||||||
|
|
||||||
|
|
@ -309,6 +312,7 @@ impl Painter {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_window_resized(&mut self, width_in_pixels: u32, height_in_pixels: u32) {
|
pub fn on_window_resized(&mut self, width_in_pixels: u32, height_in_pixels: u32) {
|
||||||
|
crate::profile_function!();
|
||||||
if self.surface_state.is_some() {
|
if self.surface_state.is_some() {
|
||||||
self.resize_and_generate_depth_texture_view_and_msaa_view(
|
self.resize_and_generate_depth_texture_view_and_msaa_view(
|
||||||
width_in_pixels,
|
width_in_pixels,
|
||||||
|
|
|
||||||
|
|
@ -894,26 +894,30 @@ fn translate_cursor(cursor_icon: egui::CursorIcon) -> Option<winit::window::Curs
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Profiling macro for feature "puffin"
|
mod profiling_scopes {
|
||||||
#[allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
macro_rules! profile_function {
|
#![allow(unused_imports)]
|
||||||
($($arg: tt)*) => {
|
|
||||||
#[cfg(feature = "puffin")]
|
/// Profiling macro for feature "puffin"
|
||||||
puffin::profile_function!($($arg)*);
|
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)]
|
#[allow(unused_imports)]
|
||||||
pub(crate) use profile_function;
|
pub(crate) use profiling_scopes::*;
|
||||||
|
|
||||||
/// Profiling macro for feature "puffin"
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
macro_rules! profile_scope {
|
|
||||||
($($arg: tt)*) => {
|
|
||||||
#[cfg(feature = "puffin")]
|
|
||||||
puffin::profile_scope!($($arg)*);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
pub(crate) use profile_scope;
|
|
||||||
|
|
|
||||||
|
|
@ -640,8 +640,8 @@ mod profiling_scopes {
|
||||||
/// Profiling macro for feature "puffin"
|
/// Profiling macro for feature "puffin"
|
||||||
macro_rules! profile_function {
|
macro_rules! profile_function {
|
||||||
($($arg: tt)*) => {
|
($($arg: tt)*) => {
|
||||||
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
|
|
||||||
#[cfg(feature = "puffin")]
|
#[cfg(feature = "puffin")]
|
||||||
|
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
|
||||||
puffin::profile_function!($($arg)*);
|
puffin::profile_function!($($arg)*);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -650,12 +650,13 @@ mod profiling_scopes {
|
||||||
/// Profiling macro for feature "puffin"
|
/// Profiling macro for feature "puffin"
|
||||||
macro_rules! profile_scope {
|
macro_rules! profile_scope {
|
||||||
($($arg: tt)*) => {
|
($($arg: tt)*) => {
|
||||||
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
|
|
||||||
#[cfg(feature = "puffin")]
|
#[cfg(feature = "puffin")]
|
||||||
|
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
|
||||||
puffin::profile_scope!($($arg)*);
|
puffin::profile_scope!($($arg)*);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub(crate) use profile_scope;
|
pub(crate) use profile_scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
pub(crate) use profiling_scopes::*;
|
pub(crate) use profiling_scopes::*;
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,9 @@ impl eframe::App for ImageViewer {
|
||||||
if !matches!(self.fit, ImageFit::Exact(_)) {
|
if !matches!(self.fit, ImageFit::Exact(_)) {
|
||||||
self.fit = ImageFit::Exact(Vec2::splat(128.0));
|
self.fit = ImageFit::Exact(Vec2::splat(128.0));
|
||||||
}
|
}
|
||||||
let ImageFit::Exact(size) = &mut self.fit else { unreachable!() };
|
let ImageFit::Exact(size) = &mut self.fit else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
ui.add(Slider::new(&mut size.x, 0.0..=2048.0).text("width"));
|
ui.add(Slider::new(&mut size.x, 0.0..=2048.0).text("width"));
|
||||||
ui.add(Slider::new(&mut size.y, 0.0..=2048.0).text("height"));
|
ui.add(Slider::new(&mut size.y, 0.0..=2048.0).text("height"));
|
||||||
}
|
}
|
||||||
|
|
@ -151,7 +153,9 @@ impl eframe::App for ImageViewer {
|
||||||
if !matches!(self.fit, ImageFit::Fraction(_)) {
|
if !matches!(self.fit, ImageFit::Fraction(_)) {
|
||||||
self.fit = ImageFit::Fraction(Vec2::splat(1.0));
|
self.fit = ImageFit::Fraction(Vec2::splat(1.0));
|
||||||
}
|
}
|
||||||
let ImageFit::Fraction(fract) = &mut self.fit else { unreachable!() };
|
let ImageFit::Fraction(fract) = &mut self.fit else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
ui.add(Slider::new(&mut fract.x, 0.0..=1.0).text("width"));
|
ui.add(Slider::new(&mut fract.x, 0.0..=1.0).text("width"));
|
||||||
ui.add(Slider::new(&mut fract.y, 0.0..=1.0).text("height"));
|
ui.add(Slider::new(&mut fract.y, 0.0..=1.0).text("height"));
|
||||||
}
|
}
|
||||||
|
|
@ -159,7 +163,9 @@ impl eframe::App for ImageViewer {
|
||||||
if !matches!(self.fit, ImageFit::Original(_)) {
|
if !matches!(self.fit, ImageFit::Original(_)) {
|
||||||
self.fit = ImageFit::Original(Some(1.0));
|
self.fit = ImageFit::Original(Some(1.0));
|
||||||
}
|
}
|
||||||
let ImageFit::Original(Some(scale)) = &mut self.fit else { unreachable!() };
|
let ImageFit::Original(Some(scale)) = &mut self.fit else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
ui.add(Slider::new(scale, 0.1..=4.0).text("scale"));
|
ui.add(Slider::new(scale, 0.1..=4.0).text("scale"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,8 @@ mod profiling_scopes {
|
||||||
/// Profiling macro for feature "puffin"
|
/// Profiling macro for feature "puffin"
|
||||||
macro_rules! profile_function {
|
macro_rules! profile_function {
|
||||||
($($arg: tt)*) => {
|
($($arg: tt)*) => {
|
||||||
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
|
|
||||||
#[cfg(feature = "puffin")]
|
#[cfg(feature = "puffin")]
|
||||||
|
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
|
||||||
puffin::profile_function!($($arg)*);
|
puffin::profile_function!($($arg)*);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -49,8 +49,8 @@ mod profiling_scopes {
|
||||||
/// Profiling macro for feature "puffin"
|
/// Profiling macro for feature "puffin"
|
||||||
macro_rules! profile_scope {
|
macro_rules! profile_scope {
|
||||||
($($arg: tt)*) => {
|
($($arg: tt)*) => {
|
||||||
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
|
|
||||||
#[cfg(feature = "puffin")]
|
#[cfg(feature = "puffin")]
|
||||||
|
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
|
||||||
puffin::profile_scope!($($arg)*);
|
puffin::profile_scope!($($arg)*);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ impl ImageCrateLoader {
|
||||||
fn is_supported_uri(uri: &str) -> bool {
|
fn is_supported_uri(uri: &str) -> bool {
|
||||||
let Some(ext) = Path::new(uri).extension().and_then(|ext| ext.to_str()) else {
|
let Some(ext) = Path::new(uri).extension().and_then(|ext| ext.to_str()) else {
|
||||||
// `true` because if there's no extension, assume that we support it
|
// `true` because if there's no extension, assume that we support it
|
||||||
return true
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
ext != "svg"
|
ext != "svg"
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,9 @@ impl SvgLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_supported(uri: &str) -> bool {
|
fn is_supported(uri: &str) -> bool {
|
||||||
let Some(ext) = Path::new(uri).extension().and_then(|ext| ext.to_str()) else { return false };
|
let Some(ext) = Path::new(uri).extension().and_then(|ext| ext.to_str()) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
ext == "svg"
|
ext == "svg"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,22 +112,30 @@ pub fn check_for_gl_error_impl(gl: &glow::Context, file: &str, line: u32, contex
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Profiling macro for feature "puffin"
|
mod profiling_scopes {
|
||||||
macro_rules! profile_function {
|
#![allow(unused_macros)]
|
||||||
($($arg: tt)*) => {
|
#![allow(unused_imports)]
|
||||||
#[cfg(feature = "puffin")]
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
puffin::profile_function!($($arg)*);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
pub(crate) use profile_function;
|
|
||||||
|
|
||||||
/// Profiling macro for feature "puffin"
|
/// Profiling macro for feature "puffin"
|
||||||
macro_rules! profile_scope {
|
macro_rules! profile_function {
|
||||||
($($arg: tt)*) => {
|
($($arg: tt)*) => {
|
||||||
#[cfg(feature = "puffin")]
|
#[cfg(feature = "puffin")]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
|
||||||
puffin::profile_scope!($($arg)*);
|
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;
|
||||||
}
|
}
|
||||||
pub(crate) use profile_scope;
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
pub(crate) use profiling_scopes::*;
|
||||||
|
|
|
||||||
|
|
@ -494,10 +494,13 @@ impl Painter {
|
||||||
"Mismatch between texture size and texel count"
|
"Mismatch between texture size and texel count"
|
||||||
);
|
);
|
||||||
|
|
||||||
let data: Vec<u8> = image
|
let data: Vec<u8> = {
|
||||||
.srgba_pixels(None)
|
crate::profile_scope!("font -> sRGBA");
|
||||||
.flat_map(|a| a.to_array())
|
image
|
||||||
.collect();
|
.srgba_pixels(None)
|
||||||
|
.flat_map(|a| a.to_array())
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
self.upload_texture_srgb(delta.pos, image.size, delta.options, &data);
|
self.upload_texture_srgb(delta.pos, image.size, delta.options, &data);
|
||||||
}
|
}
|
||||||
|
|
@ -511,6 +514,7 @@ impl Painter {
|
||||||
options: egui::TextureOptions,
|
options: egui::TextureOptions,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
) {
|
) {
|
||||||
|
crate::profile_function!();
|
||||||
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,
|
||||||
|
|
@ -561,6 +565,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");
|
||||||
self.gl.tex_sub_image_2d(
|
self.gl.tex_sub_image_2d(
|
||||||
glow::TEXTURE_2D,
|
glow::TEXTURE_2D,
|
||||||
level,
|
level,
|
||||||
|
|
@ -575,6 +580,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");
|
||||||
self.gl.tex_image_2d(
|
self.gl.tex_image_2d(
|
||||||
glow::TEXTURE_2D,
|
glow::TEXTURE_2D,
|
||||||
level,
|
level,
|
||||||
|
|
|
||||||
|
|
@ -280,6 +280,7 @@ impl FontImage {
|
||||||
/// `gamma` should normally be set to `None`.
|
/// `gamma` should normally be set to `None`.
|
||||||
///
|
///
|
||||||
/// If you are having problems with text looking skinny and pixelated, try using a low gamma, e.g. `0.4`.
|
/// If you are having problems with text looking skinny and pixelated, try using a low gamma, e.g. `0.4`.
|
||||||
|
#[inline]
|
||||||
pub fn srgba_pixels(
|
pub fn srgba_pixels(
|
||||||
&'_ self,
|
&'_ self,
|
||||||
gamma: Option<f32>,
|
gamma: Option<f32>,
|
||||||
|
|
@ -338,6 +339,7 @@ impl From<FontImage> for ImageData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn fast_round(r: f32) -> u8 {
|
fn fast_round(r: f32) -> u8 {
|
||||||
(r + 0.5).floor() as _ // rust does a saturating cast since 1.45
|
(r + 0.5).floor() as _ // rust does a saturating cast since 1.45
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,12 @@ fn start_puffin_server() {
|
||||||
Ok(puffin_server) => {
|
Ok(puffin_server) => {
|
||||||
eprintln!("Run: cargo install puffin_viewer && puffin_viewer --url 127.0.0.1:8585");
|
eprintln!("Run: cargo install puffin_viewer && puffin_viewer --url 127.0.0.1:8585");
|
||||||
|
|
||||||
|
std::process::Command::new("puffin_viewer")
|
||||||
|
.arg("--url")
|
||||||
|
.arg("127.0.0.1:8585")
|
||||||
|
.spawn()
|
||||||
|
.ok();
|
||||||
|
|
||||||
// We can store the server if we want, but in this case we just want
|
// We can store the server if we want, but in this case we just want
|
||||||
// it to keep running. Dropping it closes the server, so let's not drop it!
|
// it to keep running. Dropping it closes the server, so let's not drop it!
|
||||||
#[allow(clippy::mem_forget)]
|
#[allow(clippy::mem_forget)]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue