Add a dummy warm-up frame to the demo app to pre-cache emojis

This commit is contained in:
Emil Ernerfeldt 2021-01-02 14:42:43 +01:00
parent 4202c4b6a9
commit 14a96ca5d0
14 changed files with 144 additions and 55 deletions

View file

@ -870,36 +870,36 @@ async function init(input) {
var ret = wasm.memory; var ret = wasm.memory;
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_closure_wrapper2178 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper2332 = function(arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 536, __wbg_adapter_26); var ret = makeMutClosure(arg0, arg1, 607, __wbg_adapter_26);
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_closure_wrapper2179 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper2333 = function(arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 536, __wbg_adapter_29); var ret = makeMutClosure(arg0, arg1, 607, __wbg_adapter_29);
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_closure_wrapper2182 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper2336 = function(arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 536, __wbg_adapter_32); var ret = makeMutClosure(arg0, arg1, 607, __wbg_adapter_32);
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_closure_wrapper2184 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper2338 = function(arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 536, __wbg_adapter_35); var ret = makeMutClosure(arg0, arg1, 607, __wbg_adapter_35);
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_closure_wrapper2186 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper2340 = function(arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 536, __wbg_adapter_38); var ret = makeMutClosure(arg0, arg1, 607, __wbg_adapter_38);
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_closure_wrapper2188 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper2342 = function(arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 536, __wbg_adapter_41); var ret = makeMutClosure(arg0, arg1, 607, __wbg_adapter_41);
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_closure_wrapper2190 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper2344 = function(arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 536, __wbg_adapter_44); var ret = makeMutClosure(arg0, arg1, 607, __wbg_adapter_44);
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_closure_wrapper3036 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper3087 = function(arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 608, __wbg_adapter_47); var ret = makeMutClosure(arg0, arg1, 649, __wbg_adapter_47);
return addHeapObject(ret); return addHeapObject(ret);
}; };

Binary file not shown.

View file

@ -35,7 +35,7 @@ impl State {
// Helper // Helper
pub fn is_open(ctx: &Context, id: Id) -> Option<bool> { pub fn is_open(ctx: &Context, id: Id) -> Option<bool> {
if ctx.memory().all_collpasing_are_open { if ctx.memory().everything_is_visible() {
Some(true) Some(true)
} else { } else {
ctx.memory() ctx.memory()
@ -52,7 +52,11 @@ impl State {
/// 0 for closed, 1 for open, with tweening /// 0 for closed, 1 for open, with tweening
pub fn openness(&self, ctx: &Context, id: Id) -> f32 { pub fn openness(&self, ctx: &Context, id: Id) -> f32 {
ctx.animate_bool(id, self.open || ctx.memory().all_collpasing_are_open) if ctx.memory().everything_is_visible() {
1.0
} else {
ctx.animate_bool(id, self.open)
}
} }
/// Show contents if we are open, with a nice animation between closed and open /// Show contents if we are open, with a nice animation between closed and open

View file

@ -25,6 +25,8 @@ pub fn show_tooltip(ctx: &CtxRef, add_contents: impl FnOnce(&mut Ui)) {
let position = position.min(ctx.input().screen_rect().right_bottom() - expected_size); let position = position.min(ctx.input().screen_rect().right_bottom() - expected_size);
let position = position.max(ctx.input().screen_rect().left_top()); let position = position.max(ctx.input().screen_rect().left_top());
position position
} else if ctx.memory().everything_is_visible() {
Pos2::default()
} else { } else {
return; // No good place for a tooltip :( return; // No good place for a tooltip :(
}; };

View file

@ -209,7 +209,7 @@ impl<'open> Window<'open> {
with_title_bar, with_title_bar,
} = self; } = self;
if matches!(open, Some(false)) && !ctx.memory().all_windows_are_open { if matches!(open, Some(false)) && !ctx.memory().everything_is_visible() {
return None; return None;
} }

View file

@ -38,6 +38,7 @@ pub(crate) struct FrameState {
/// How much space is used by panels. /// How much space is used by panels.
used_by_panels: Rect, used_by_panels: Rect,
pub(crate) scroll_delta: Vec2, pub(crate) scroll_delta: Vec2,
pub(crate) scroll_target: Option<(f32, Align)>, pub(crate) scroll_target: Option<(f32, Align)>,
// TODO: move some things from `Memory` to here // TODO: move some things from `Memory` to here
@ -56,7 +57,7 @@ impl Default for FrameState {
} }
impl FrameState { impl FrameState {
pub fn begin_frame(&mut self, input: &InputState) { fn begin_frame(&mut self, input: &InputState) {
self.available_rect = input.screen_rect(); self.available_rect = input.screen_rect();
self.unused_rect = input.screen_rect(); self.unused_rect = input.screen_rect();
self.used_by_panels = Rect::nothing(); self.used_by_panels = Rect::nothing();
@ -717,6 +718,11 @@ impl Context {
} }
animated_value animated_value
} }
/// Clear memory of any animations.
pub fn clear_animations(&self) {
*self.animation_manager.lock() = Default::default();
}
} }
impl Context { impl Context {

View file

@ -60,12 +60,8 @@ pub struct Memory {
#[cfg_attr(feature = "serde", serde(skip))] #[cfg_attr(feature = "serde", serde(skip))]
pub(crate) tooltip_rect: Option<Rect>, pub(crate) tooltip_rect: Option<Rect>,
/// Useful for debugging, benchmarking etc. #[cfg_attr(feature = "serde", serde(skip))]
pub all_collpasing_are_open: bool, everything_is_visible: bool,
/// Useful for debugging, benchmarking etc.
pub all_menus_are_open: bool,
/// Useful for debugging, benchmarking etc.
pub all_windows_are_open: bool,
} }
/// Say there is a button in a scroll area. /// Say there is a button in a scroll area.
@ -258,7 +254,7 @@ impl Memory {
/// Only one can be be open at a time. /// Only one can be be open at a time.
impl Memory { impl Memory {
pub fn is_popup_open(&mut self, popup_id: Id) -> bool { pub fn is_popup_open(&mut self, popup_id: Id) -> bool {
self.popup == Some(popup_id) self.popup == Some(popup_id) || self.everything_is_visible()
} }
pub fn open_popup(&mut self, popup_id: Id) { pub fn open_popup(&mut self, popup_id: Id) {
@ -276,6 +272,24 @@ impl Memory {
self.open_popup(popup_id); self.open_popup(popup_id);
} }
} }
/// If true, all windows, menus, tooltips etc are to be visible at once.
///
/// This is useful for testing, benchmarking, pre-caching, etc.
///
/// Experimental feature!
pub fn everything_is_visible(&self) -> bool {
self.everything_is_visible
}
/// If true, all windows, menus, tooltips etc are to be visible at once.
///
/// This is useful for testing, benchmarking, pre-caching, etc.
///
/// Experimental feature!
pub fn set_everything_is_visible(&mut self, value: bool) {
self.everything_is_visible = value;
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View file

@ -94,7 +94,7 @@ fn menu_impl<'c>(
bar_state.open_menu = Some(menu_id); bar_state.open_menu = Some(menu_id);
} }
if bar_state.open_menu == Some(menu_id) || ui.memory().all_menus_are_open { if bar_state.open_menu == Some(menu_id) || ui.ctx().memory().everything_is_visible() {
let area = Area::new(menu_id) let area = Area::new(menu_id)
.order(Order::Foreground) .order(Order::Foreground)
.fixed_pos(button_response.rect.left_bottom()); .fixed_pos(button_response.rect.left_bottom());

View file

@ -139,7 +139,7 @@ impl Response {
/// Show this UI if the item was hovered (i.e. a tooltip). /// Show this UI if the item was hovered (i.e. a tooltip).
/// If you call this multiple times the tooltips will stack underneath the previous ones. /// If you call this multiple times the tooltips will stack underneath the previous ones.
pub fn on_hover_ui(self, add_contents: impl FnOnce(&mut Ui)) -> Self { pub fn on_hover_ui(self, add_contents: impl FnOnce(&mut Ui)) -> Self {
if self.hovered { if self.hovered || self.ctx.memory().everything_is_visible() {
crate::containers::show_tooltip(&self.ctx, add_contents); crate::containers::show_tooltip(&self.ctx, add_contents);
} }
self self

View file

@ -18,7 +18,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
{ {
let mut ctx = egui::CtxRef::default(); let mut ctx = egui::CtxRef::default();
ctx.memory().all_collpasing_are_open = true; // expand the demo window with everything ctx.memory().set_everything_is_visible(true); // give us everything
let mut demo_windows = egui_demo_lib::DemoWindows::default(); let mut demo_windows = egui_demo_lib::DemoWindows::default();
c.bench_function("demo_windows_full", |b| { c.bench_function("demo_windows_full", |b| {
@ -32,7 +32,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
{ {
let mut ctx = egui::CtxRef::default(); let mut ctx = egui::CtxRef::default();
ctx.memory().all_collpasing_are_open = true; // expand the demo window with everything ctx.memory().set_everything_is_visible(true); // give us everything
let mut demo_windows = egui_demo_lib::DemoWindows::default(); let mut demo_windows = egui_demo_lib::DemoWindows::default();
ctx.begin_frame(raw_input.clone()); ctx.begin_frame(raw_input.clone());
demo_windows.ui(&ctx); demo_windows.ui(&ctx);

View file

@ -42,6 +42,10 @@ impl epi::App for WrapApp {
epi::set_value(storage, epi::APP_KEY, self); epi::set_value(storage, epi::APP_KEY, self);
} }
fn warm_up_enabled(&self) -> bool {
true // The example windows use a lot of emojis. Pre-cache them by running one frame where everything is open.
}
fn update(&mut self, ctx: &egui::CtxRef, frame: &mut epi::Frame<'_>) { fn update(&mut self, ctx: &egui::CtxRef, frame: &mut epi::Frame<'_>) {
if let Some(web_info) = frame.info().web_info.as_ref() { if let Some(web_info) = frame.info().web_info.as_ref() {
if let Some(anchor) = web_info.web_location_hash.strip_prefix("#") { if let Some(anchor) = web_info.web_location_hash.strip_prefix("#") {
@ -91,14 +95,14 @@ impl epi::App for WrapApp {
}); });
self.backend_panel.update(ctx, frame); self.backend_panel.update(ctx, frame);
if self.backend_panel.open { if self.backend_panel.open || ctx.memory().everything_is_visible() {
egui::SidePanel::left("backend_panel", 150.0).show(ctx, |ui| { egui::SidePanel::left("backend_panel", 150.0).show(ctx, |ui| {
self.backend_panel.ui(ui, frame); self.backend_panel.ui(ui, frame);
}); });
} }
for (anchor, app) in self.apps.iter_mut() { for (anchor, app) in self.apps.iter_mut() {
if anchor == self.selected_anchor { if anchor == self.selected_anchor || ctx.memory().everything_is_visible() {
app.update(ctx, frame); app.update(ctx, frame);
} }
} }
@ -192,19 +196,6 @@ impl BackendPanel {
fn ui(&mut self, ui: &mut egui::Ui, frame: &mut epi::Frame<'_>) { fn ui(&mut self, ui: &mut egui::Ui, frame: &mut epi::Frame<'_>) {
ui.heading("💻 Backend"); ui.heading("💻 Backend");
if frame.is_web() {
ui.label("Egui is an immediate mode GUI written in Rust, compiled to WebAssembly, rendered with WebGL.");
ui.label(
"Everything you see is rendered as textured triangles. There is no DOM. There are no HTML elements. \
This is not JavaScript. This is Rust, running at 60 FPS. This is the web page, reinvented with game tech.");
ui.label("This is also work in progress, and not ready for production... yet :)");
ui.horizontal(|ui| {
ui.label("Project home page:");
ui.hyperlink("https://github.com/emilk/egui");
});
ui.separator();
}
self.run_mode_ui(ui); self.run_mode_ui(ui);
ui.separator(); ui.separator();
@ -219,8 +210,19 @@ impl BackendPanel {
} }
} }
if !frame.is_web() { ui.separator();
ui.separator();
if frame.is_web() {
ui.label("Egui is an immediate mode GUI written in Rust, compiled to WebAssembly, rendered with WebGL.");
ui.label(
"Everything you see is rendered as textured triangles. There is no DOM. There are no HTML elements. \
This is not JavaScript. This is Rust, running at 60 FPS. This is the web page, reinvented with game tech.");
ui.label("This is also work in progress, and not ready for production... yet :)");
ui.horizontal_wrapped_for_text(egui::TextStyle::Body, |ui| {
ui.label("Project home page:");
ui.hyperlink("https://github.com/emilk/egui");
});
} else {
if ui if ui
.button("📱 Phone Size") .button("📱 Phone Size")
.on_hover_text("Resize the window to be small like a phone.") .on_hover_text("Resize the window to be small like a phone.")

View file

@ -90,6 +90,18 @@ fn create_storage(app_name: &str) -> Option<Box<dyn epi::Storage>> {
} }
} }
fn integration_info(
display: &glium::Display,
previous_frame_time: Option<f32>,
) -> epi::IntegrationInfo {
epi::IntegrationInfo {
web_info: None,
cpu_usage: previous_frame_time,
seconds_since_midnight: Some(seconds_since_midnight()),
native_pixels_per_point: Some(native_pixels_per_point(&display)),
}
}
/// Run an egui app /// Run an egui app
pub fn run(mut app: Box<dyn epi::App>) -> ! { pub fn run(mut app: Box<dyn epi::App>) -> ! {
let mut storage = create_storage(app.name()); let mut storage = create_storage(app.name());
@ -113,6 +125,7 @@ pub fn run(mut app: Box<dyn epi::App>) -> ! {
.as_mut() .as_mut()
.and_then(|storage| epi::get_value(storage.as_ref(), EGUI_MEMORY_KEY)) .and_then(|storage| epi::get_value(storage.as_ref(), EGUI_MEMORY_KEY))
.unwrap_or_default(); .unwrap_or_default();
app.setup(&ctx); app.setup(&ctx);
let mut input_state = GliumInputState::from_pixels_per_point(native_pixels_per_point(&display)); let mut input_state = GliumInputState::from_pixels_per_point(native_pixels_per_point(&display));
@ -126,6 +139,36 @@ pub fn run(mut app: Box<dyn epi::App>) -> ! {
let http = std::sync::Arc::new(crate::http::GliumHttp {}); let http = std::sync::Arc::new(crate::http::GliumHttp {});
if app.warm_up_enabled() {
// let warm_up_start = Instant::now();
input_state.raw.time = Some(0.0);
input_state.raw.screen_rect = Some(Rect::from_min_size(
Default::default(),
screen_size_in_pixels(&display) / input_state.raw.pixels_per_point.unwrap(),
));
ctx.begin_frame(input_state.raw.take());
let mut app_output = epi::backend::AppOutput::default();
let mut frame = epi::backend::FrameBuilder {
info: integration_info(&display, None),
tex_allocator: Some(&mut painter),
http: http.clone(),
output: &mut app_output,
repaint_signal: repaint_signal.clone(),
}
.build();
let saved_memory = ctx.memory().clone();
ctx.memory().set_everything_is_visible(true);
app.update(&ctx, &mut frame);
*ctx.memory() = saved_memory; // We don't want to remember that windows were huge.
ctx.clear_animations();
let (egui_output, _paint_commands) = ctx.end_frame();
handle_output(egui_output, &display, clipboard.as_mut());
// TODO: handle app_output
// eprintln!("Warmed up in {} ms", warm_up_start.elapsed().as_millis())
}
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
let mut redraw = || { let mut redraw = || {
let frame_start = Instant::now(); let frame_start = Instant::now();
@ -138,12 +181,7 @@ pub fn run(mut app: Box<dyn epi::App>) -> ! {
ctx.begin_frame(input_state.raw.take()); ctx.begin_frame(input_state.raw.take());
let mut app_output = epi::backend::AppOutput::default(); let mut app_output = epi::backend::AppOutput::default();
let mut frame = epi::backend::FrameBuilder { let mut frame = epi::backend::FrameBuilder {
info: epi::IntegrationInfo { info: integration_info(&display, previous_frame_time),
web_info: None,
cpu_usage: previous_frame_time,
seconds_since_midnight: Some(seconds_since_midnight()),
native_pixels_per_point: Some(native_pixels_per_point(&display)),
},
tex_allocator: Some(&mut painter), tex_allocator: Some(&mut painter),
http: http.clone(), http: http.clone(),
output: &mut app_output, output: &mut app_output,

View file

@ -179,6 +179,20 @@ impl AppRunner {
self.web_backend.canvas_id() self.web_backend.canvas_id()
} }
pub fn warm_up(&mut self) -> Result<(), JsValue> {
if self.app.warm_up_enabled() {
let saved_memory = self.web_backend.ctx.memory().clone();
self.web_backend
.ctx
.memory()
.set_everything_is_visible(true);
self.logic()?;
*self.web_backend.ctx.memory() = saved_memory; // We don't want to remember that windows were huge.
self.web_backend.ctx.clear_animations();
}
Ok(())
}
pub fn logic(&mut self) -> Result<(egui::Output, egui::PaintJobs), JsValue> { pub fn logic(&mut self) -> Result<(egui::Output, egui::PaintJobs), JsValue> {
resize_canvas_to_screen_size(self.web_backend.canvas_id()); resize_canvas_to_screen_size(self.web_backend.canvas_id());
let canvas_size = canvas_size_in_points(self.web_backend.canvas_id()); let canvas_size = canvas_size_in_points(self.web_backend.canvas_id());
@ -227,7 +241,8 @@ impl AppRunner {
/// and start running the given app. /// and start running the given app.
pub fn start(canvas_id: &str, app: Box<dyn epi::App>) -> Result<AppRunnerRef, JsValue> { pub fn start(canvas_id: &str, app: Box<dyn epi::App>) -> Result<AppRunnerRef, JsValue> {
let backend = WebBackend::new(canvas_id)?; let backend = WebBackend::new(canvas_id)?;
let runner = AppRunner::new(backend, app)?; let mut runner = AppRunner::new(backend, app)?;
runner.warm_up()?;
start_runner(runner) start_runner(runner)
} }

View file

@ -64,6 +64,14 @@ pub trait App {
/// Optional. /// Optional.
fn setup(&mut self, _ctx: &egui::CtxRef) {} fn setup(&mut self, _ctx: &egui::CtxRef) {}
/// If `true` a warm-up call to [`Self::update`] will be issued where
/// `ctx.memory().everything_is_visible()` will be set to `true`.
///
/// In this warm-up call, all paint commands will be ignored.
fn warm_up_enabled(&self) -> bool {
false
}
/// Called once on start. Allows you to restore state. /// Called once on start. Allows you to restore state.
fn load(&mut self, _storage: &dyn Storage) {} fn load(&mut self, _storage: &dyn Storage) {}