Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make epi::Frame cloneable so you can allocate textures in other threads #999

Merged
merged 8 commits into from
Dec 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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