Skip to content

Commit

Permalink
Make epi::Frame cloneable so you can allocate textures in other threa…
Browse files Browse the repository at this point in the history
…ds (emilk#999)

Closes emilk#673

Also adds `epi::Image`
  • Loading branch information
emilk authored Dec 26, 2021
1 parent 647e020 commit b7441ee
Show file tree
Hide file tree
Showing 28 changed files with 548 additions and 824 deletions.
3 changes: 3 additions & 0 deletions eframe/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ NOTE: [`egui_web`](egui_web/CHANGELOG.md), [`egui-winit`](egui-winit/CHANGELOG.m


## Unreleased
* `Frame` can now be cloned, saved, and passed to background threads ([#999](https://github.com/emilk/egui/pull/999)).
* Added `Frame::request_repaint` to replace `repaint_signal` ([#999](https://github.com/emilk/egui/pull/999)).
* Added `Frame::alloc_texture/free_texture` to replace `tex_allocator` ([#999](https://github.com/emilk/egui/pull/999)).


## 0.15.0 - 2021-10-24
Expand Down
2 changes: 1 addition & 1 deletion eframe/examples/file_dialog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ impl epi::App for MyApp {
"Native file dialogs and drag-and-drop files"
}

fn update(&mut self, ctx: &egui::CtxRef, _frame: &mut epi::Frame<'_>) {
fn update(&mut self, ctx: &egui::CtxRef, _frame: &epi::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.label("Drag-and-drop files onto the window!");

Expand Down
2 changes: 1 addition & 1 deletion eframe/examples/hello_world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ impl epi::App for MyApp {
"My egui App"
}

fn update(&mut self, ctx: &egui::CtxRef, frame: &mut epi::Frame<'_>) {
fn update(&mut self, ctx: &egui::CtxRef, frame: &epi::Frame) {
let Self { name, age } = self;

egui::CentralPanel::default().show(ctx, |ui| {
Expand Down
16 changes: 5 additions & 11 deletions eframe/examples/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,20 @@ impl epi::App for MyApp {
"Show an image with eframe/egui"
}

fn update(&mut self, ctx: &egui::CtxRef, frame: &mut epi::Frame<'_>) {
fn update(&mut self, ctx: &egui::CtxRef, frame: &epi::Frame) {
if self.texture.is_none() {
// Load the image:
let image_data = include_bytes!("rust-logo-256x256.png");
use image::GenericImageView;
let image = image::load_from_memory(image_data).expect("Failed to load image");
let image_buffer = image.to_rgba8();
let size = (image.width() as usize, image.height() as usize);
let size = [image.width() as usize, image.height() as usize];
let pixels = image_buffer.into_vec();
assert_eq!(size.0 * size.1 * 4, pixels.len());
let pixels: Vec<_> = pixels
.chunks_exact(4)
.map(|p| egui::Color32::from_rgba_unmultiplied(p[0], p[1], p[2], p[3]))
.collect();
let image = epi::Image::from_rgba_unmultiplied(size, &pixels);

// Allocate a texture:
let texture = frame
.tex_allocator()
.alloc_srgba_premultiplied(size, &pixels);
let size = egui::Vec2::new(size.0 as f32, size.1 as f32);
let texture = frame.alloc_texture(image);
let size = egui::Vec2::new(size[0] as f32, size[1] as f32);
self.texture = Some((size, texture));
}

Expand Down
4 changes: 2 additions & 2 deletions eframe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
//! "My egui App"
//! }
//!
//! fn update(&mut self, ctx: &egui::CtxRef, frame: &mut epi::Frame<'_>) {
//! fn update(&mut self, ctx: &egui::CtxRef, frame: &epi::Frame) {
//! egui::CentralPanel::default().show(ctx, |ui| {
//! ui.heading("Hello World!");
//! });
Expand Down Expand Up @@ -127,7 +127,7 @@ pub fn start_web(canvas_id: &str, app: Box<dyn epi::App>) -> Result<(), wasm_bin
/// "My egui App"
/// }
///
/// fn update(&mut self, ctx: &egui::CtxRef, frame: &mut epi::Frame<'_>) {
/// fn update(&mut self, ctx: &egui::CtxRef, frame: &epi::Frame) {
/// egui::CentralPanel::default().show(ctx, |ui| {
/// ui.heading("Hello World!");
/// });
Expand Down
1 change: 1 addition & 0 deletions egui-winit/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ All notable changes to the `egui-winit` integration will be noted in this file.
* Remove `State::is_quit_event` and `State::is_quit_shortcut` ([#881](https://github.com/emilk/egui/pull/881)).
* Updated `winit` to 0.26 ([#930](https://github.com/emilk/egui/pull/930)).


## 0.15.0 - 2021-10-24
First stand-alone release. Previously part of `egui_glium`.
106 changes: 41 additions & 65 deletions egui-winit/src/epi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ pub fn handle_app_output(
window: &winit::window::Window,
current_pixels_per_point: f32,
app_output: epi::backend::AppOutput,
) {
) -> epi::backend::TexAllocationData {
let epi::backend::AppOutput {
quit: _,
tex_allocation_data,
window_size,
window_title,
decorated,
Expand Down Expand Up @@ -85,6 +86,8 @@ pub fn handle_app_output(
if drag_window {
let _ = window.drag_window();
}

tex_allocation_data
}

// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -186,13 +189,11 @@ impl Persistence {

/// Everything needed to make a winit-based integration for [`epi`].
pub struct EpiIntegration {
integration_name: &'static str,
frame: epi::Frame,
persistence: crate::epi::Persistence,
repaint_signal: std::sync::Arc<dyn epi::RepaintSignal>,
pub egui_ctx: egui::CtxRef,
egui_winit: crate::State,
pub app: Box<dyn epi::App>,
latest_frame_time: Option<f32>,
/// When set, it is time to quit
quit: bool,
}
Expand All @@ -201,63 +202,58 @@ impl EpiIntegration {
pub fn new(
integration_name: &'static str,
window: &winit::window::Window,
tex_allocator: &mut dyn epi::TextureAllocator,
repaint_signal: std::sync::Arc<dyn epi::RepaintSignal>,
repaint_signal: std::sync::Arc<dyn epi::backend::RepaintSignal>,
persistence: crate::epi::Persistence,
app: Box<dyn epi::App>,
) -> Self {
let egui_ctx = egui::CtxRef::default();

*egui_ctx.memory() = persistence.load_memory().unwrap_or_default();

let frame = epi::Frame::new(epi::backend::FrameData {
info: epi::IntegrationInfo {
name: integration_name,
web_info: None,
prefer_dark_mode: None, // TODO: figure out system default
cpu_usage: None,
native_pixels_per_point: Some(crate::native_pixels_per_point(window)),
},
output: Default::default(),
repaint_signal,
});

let mut slf = Self {
integration_name,
frame,
persistence,
repaint_signal,
egui_ctx,
egui_winit: crate::State::new(window),
app,
latest_frame_time: None,
quit: false,
};

slf.setup(window, tex_allocator);
slf.setup(window);
if slf.app.warm_up_enabled() {
slf.warm_up(window, tex_allocator);
slf.warm_up(window);
}

slf
}

fn setup(
&mut self,
window: &winit::window::Window,
tex_allocator: &mut dyn epi::TextureAllocator,
) {
let mut app_output = epi::backend::AppOutput::default();
let mut frame = epi::backend::FrameBuilder {
info: integration_info(self.integration_name, window, None),
tex_allocator,
output: &mut app_output,
repaint_signal: self.repaint_signal.clone(),
}
.build();
fn setup(&mut self, window: &winit::window::Window) {
self.app
.setup(&self.egui_ctx, &mut frame, self.persistence.storage());

.setup(&self.egui_ctx, &self.frame, self.persistence.storage());
let app_output = self.frame.take_app_output();
self.quit |= app_output.quit;

crate::epi::handle_app_output(window, self.egui_ctx.pixels_per_point(), app_output);
let tex_alloc_data =
crate::epi::handle_app_output(window, self.egui_ctx.pixels_per_point(), app_output);
self.frame.lock().output.tex_allocation_data = tex_alloc_data; // Do it later
}

fn warm_up(
&mut self,
window: &winit::window::Window,
tex_allocator: &mut dyn epi::TextureAllocator,
) {
fn warm_up(&mut self, window: &winit::window::Window) {
let saved_memory = self.egui_ctx.memory().clone();
self.egui_ctx.memory().set_everything_is_visible(true);
self.update(window, tex_allocator);
let (_, tex_alloc_data, _) = self.update(window);
self.frame.lock().output.tex_allocation_data = tex_alloc_data; // handle it next frame
*self.egui_ctx.memory() = saved_memory; // We don't want to remember that windows were huge.
self.egui_ctx.clear_animations();
}
Expand All @@ -277,37 +273,31 @@ impl EpiIntegration {
pub fn update(
&mut self,
window: &winit::window::Window,
tex_allocator: &mut dyn epi::TextureAllocator,
) -> (bool, Vec<egui::epaint::ClippedShape>) {
) -> (
bool,
epi::backend::TexAllocationData,
Vec<egui::epaint::ClippedShape>,
) {
let frame_start = std::time::Instant::now();

let raw_input = self.egui_winit.take_egui_input(window);

let mut app_output = epi::backend::AppOutput::default();
let mut frame = epi::backend::FrameBuilder {
info: integration_info(self.integration_name, window, self.latest_frame_time),
tex_allocator,
output: &mut app_output,
repaint_signal: self.repaint_signal.clone(),
}
.build();

let (egui_output, shapes) = self.egui_ctx.run(raw_input, |egui_ctx| {
self.app.update(egui_ctx, &mut frame);
self.app.update(egui_ctx, &self.frame);
});

let needs_repaint = egui_output.needs_repaint;
self.egui_winit
.handle_output(window, &self.egui_ctx, egui_output);

let app_output = self.frame.take_app_output();
self.quit |= app_output.quit;

crate::epi::handle_app_output(window, self.egui_ctx.pixels_per_point(), app_output);
let tex_allocation_data =
crate::epi::handle_app_output(window, self.egui_ctx.pixels_per_point(), app_output);

let frame_time = (std::time::Instant::now() - frame_start).as_secs_f64() as f32;
self.latest_frame_time = Some(frame_time);
self.frame.lock().info.cpu_usage = Some(frame_time);

(needs_repaint, shapes)
(needs_repaint, tex_allocation_data, shapes)
}

pub fn maybe_autosave(&mut self, window: &winit::window::Window) {
Expand All @@ -321,17 +311,3 @@ impl EpiIntegration {
.save(&mut *self.app, &self.egui_ctx, window);
}
}

fn integration_info(
integration_name: &'static str,
window: &winit::window::Window,
previous_frame_time: Option<f32>,
) -> epi::IntegrationInfo {
epi::IntegrationInfo {
name: integration_name,
web_info: None,
prefer_dark_mode: None, // TODO: figure out system default
cpu_usage: previous_frame_time,
native_pixels_per_point: Some(crate::native_pixels_per_point(window)),
}
}
31 changes: 13 additions & 18 deletions egui_demo_lib/src/apps/color_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl epi::App for ColorTest {
"🎨 Color test"
}

fn update(&mut self, ctx: &egui::CtxRef, frame: &mut epi::Frame<'_>) {
fn update(&mut self, ctx: &egui::CtxRef, frame: &epi::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
if frame.is_web() {
ui.label(
Expand All @@ -43,18 +43,14 @@ impl epi::App for ColorTest {
ui.separator();
}
ScrollArea::both().auto_shrink([false; 2]).show(ui, |ui| {
self.ui(ui, &mut Some(frame.tex_allocator()));
self.ui(ui, Some(frame));
});
});
}
}

impl ColorTest {
pub fn ui(
&mut self,
ui: &mut Ui,
mut tex_allocator: &mut Option<&mut dyn epi::TextureAllocator>,
) {
pub fn ui(&mut self, ui: &mut Ui, tex_allocator: Option<&dyn epi::TextureAllocator>) {
ui.set_max_width(680.0);

ui.vertical_centered(|ui| {
Expand Down Expand Up @@ -105,10 +101,10 @@ impl ColorTest {
self.vertex_gradient(ui, "Ground truth (vertices)", WHITE, &g);
self.tex_gradient(ui, tex_allocator, "Ground truth (texture)", WHITE, &g);
}
if let Some(tex_allocator) = &mut tex_allocator {
if let Some(tex_allocator) = tex_allocator {
ui.horizontal(|ui| {
let g = Gradient::one_color(Color32::from(tex_color));
let tex = self.tex_mngr.get(*tex_allocator, &g);
let tex = self.tex_mngr.get(tex_allocator, &g);
let texel_offset = 0.5 / (g.0.len() as f32);
let uv =
Rect::from_min_max(pos2(texel_offset, 0.0), pos2(1.0 - texel_offset, 1.0));
Expand Down Expand Up @@ -167,7 +163,7 @@ impl ColorTest {
fn show_gradients(
&mut self,
ui: &mut Ui,
tex_allocator: &mut Option<&mut dyn epi::TextureAllocator>,
tex_allocator: Option<&dyn epi::TextureAllocator>,
bg_fill: Color32,
(left, right): (Color32, Color32),
) {
Expand Down Expand Up @@ -261,7 +257,7 @@ impl ColorTest {
fn tex_gradient(
&mut self,
ui: &mut Ui,
tex_allocator: &mut Option<&mut dyn epi::TextureAllocator>,
tex_allocator: Option<&dyn epi::TextureAllocator>,
label: &str,
bg_fill: Color32,
gradient: &Gradient,
Expand All @@ -271,7 +267,7 @@ impl ColorTest {
}
if let Some(tex_allocator) = tex_allocator {
ui.horizontal(|ui| {
let tex = self.tex_mngr.get(*tex_allocator, gradient);
let tex = self.tex_mngr.get(tex_allocator, gradient);
let texel_offset = 0.5 / (gradient.0.len() as f32);
let uv = Rect::from_min_max(pos2(texel_offset, 0.0), pos2(1.0 - texel_offset, 1.0));
ui.add(Image::new(tex, GRADIENT_SIZE).bg_fill(bg_fill).uv(uv))
Expand Down Expand Up @@ -391,16 +387,15 @@ impl Gradient {
struct TextureManager(HashMap<Gradient, TextureId>);

impl TextureManager {
fn get(
&mut self,
tex_allocator: &mut dyn epi::TextureAllocator,
gradient: &Gradient,
) -> TextureId {
fn get(&mut self, tex_allocator: &dyn epi::TextureAllocator, gradient: &Gradient) -> TextureId {
*self.0.entry(gradient.clone()).or_insert_with(|| {
let pixels = gradient.to_pixel_row();
let width = pixels.len();
let height = 1;
tex_allocator.alloc_srgba_premultiplied((width, height), &pixels)
tex_allocator.alloc(epi::Image {
size: [width, height],
pixels,
})
})
}
}
Expand Down
4 changes: 2 additions & 2 deletions egui_demo_lib/src/apps/demo/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ impl epi::App for DemoApp {
fn setup(
&mut self,
_ctx: &egui::CtxRef,
_frame: &mut epi::Frame<'_>,
_frame: &epi::Frame,
_storage: Option<&dyn epi::Storage>,
) {
#[cfg(feature = "persistence")]
Expand All @@ -31,7 +31,7 @@ impl epi::App for DemoApp {
epi::set_value(storage, epi::APP_KEY, self);
}

fn update(&mut self, ctx: &egui::CtxRef, _frame: &mut epi::Frame<'_>) {
fn update(&mut self, ctx: &egui::CtxRef, _frame: &epi::Frame) {
self.demo_windows.ui(ctx);
}
}
2 changes: 1 addition & 1 deletion egui_demo_lib/src/apps/fractal_clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl epi::App for FractalClock {
"🕑 Fractal Clock"
}

fn update(&mut self, ctx: &egui::CtxRef, _frame: &mut epi::Frame<'_>) {
fn update(&mut self, ctx: &egui::CtxRef, _frame: &epi::Frame) {
egui::CentralPanel::default()
.frame(Frame::dark_canvas(&ctx.style()))
.show(ctx, |ui| self.ui(ui, crate::seconds_since_midnight()));
Expand Down
Loading

0 comments on commit b7441ee

Please sign in to comment.