Browse Source

Add glow::Context to epi::Frame (#1425)

This can be used, for instance, to:

* Render things to offscreen buffers.
* Read the pixel buffer from the previous frame (glow::Context::read_pixels).
* Render things behind the egui windows.
pull/1429/head
Emil Ernerfeldt 3 years ago
committed by GitHub
parent
commit
8f178fa4e0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      eframe/CHANGELOG.md
  2. 2
      egui-winit/src/epi.rs
  3. 2
      egui/src/ui.rs
  4. 2
      egui_demo_lib/src/backend_panel.rs
  5. 32
      egui_glow/src/epi_backend.rs
  6. 9
      egui_glow/src/painter.rs
  7. 7
      egui_web/src/backend.rs
  8. 6
      egui_web/src/glow_wrapping.rs
  9. 1
      egui_web/src/lib.rs
  10. 19
      epi/src/lib.rs

1
eframe/CHANGELOG.md

@ -18,6 +18,7 @@ NOTE: [`egui_web`](../egui_web/CHANGELOG.md), [`egui-winit`](../egui-winit/CHANG
* You can now load/save state in `App::update`
* Changed `App::update` to take `&mut Frame` instead of `&Frame`.
* `Frame` is no longer `Clone` or `Sync`.
* Add `glow` (OpenGL) context to `Frame` ([#1425](https://github.com/emilk/egui/pull/1425)).
## 0.17.0 - 2022-02-22

2
egui-winit/src/epi.rs

@ -149,6 +149,7 @@ pub struct EpiIntegration {
impl EpiIntegration {
pub fn new(
integration_name: &'static str,
gl: std::rc::Rc<glow::Context>,
max_texture_side: usize,
window: &winit::window::Window,
storage: Option<Box<dyn epi::Storage>>,
@ -169,6 +170,7 @@ impl EpiIntegration {
},
output: Default::default(),
storage,
gl,
};
if prefer_dark_mode == Some(true) {

2
egui/src/ui.rs

@ -792,7 +792,7 @@ impl Ui {
/// So one can think of `cursor` as a constraint on the available region.
///
/// If something has already been added, this will point to `style.spacing.item_spacing` beyond the latest child.
/// The cursor can thus be `style.spacing.item_spacing` pixels outside of the min_rect.
/// The cursor can thus be `style.spacing.item_spacing` pixels outside of the `min_rect`.
pub fn cursor(&self) -> Rect {
self.placer.cursor()
}

2
egui_demo_lib/src/backend_panel.rs

@ -12,7 +12,7 @@ enum RunMode {
/// For instance, a GUI for a thermostat need to repaint each time the temperature changes.
/// To ensure the UI is up to date you need to call `egui::Context::request_repaint()` each
/// time such an event happens. You can also chose to call `request_repaint()` once every second
/// or after every single frame - this is called `Continuous` mode,
/// or after every single frame - this is called "Continuous" mode,
/// and for games and interactive tools that need repainting every frame anyway, this should be the default.
Reactive,

32
egui_glow/src/epi_backend.rs

@ -1,4 +1,3 @@
use crate::*;
use egui_winit::winit;
struct RequestRepaintEvent;
@ -50,6 +49,7 @@ pub fn run(app_name: &str, native_options: &epi::NativeOptions, app_creator: epi
let mut integration = egui_winit::epi::EpiIntegration::new(
"egui_glow",
gl.clone(),
painter.max_texture_side(),
gl_window.window(),
storage,
@ -86,6 +86,10 @@ pub fn run(app_name: &str, native_options: &epi::NativeOptions, app_creator: epi
std::thread::sleep(std::time::Duration::from_millis(10));
}
let screen_size_in_pixels: [u32; 2] = gl_window.window().inner_size().into();
crate::painter::clear(&gl, screen_size_in_pixels, app.clear_color());
let egui::FullOutput {
platform_output,
needs_repaint,
@ -97,24 +101,14 @@ pub fn run(app_name: &str, native_options: &epi::NativeOptions, app_creator: epi
let clipped_primitives = integration.egui_ctx.tessellate(shapes);
// paint:
{
let color = app.clear_color();
unsafe {
use glow::HasContext as _;
gl.disable(glow::SCISSOR_TEST);
gl.clear_color(color[0], color[1], color[2], color[3]);
gl.clear(glow::COLOR_BUFFER_BIT);
}
painter.paint_and_update_textures(
gl_window.window().inner_size().into(),
integration.egui_ctx.pixels_per_point(),
&clipped_primitives,
&textures_delta,
);
gl_window.swap_buffers().unwrap();
}
painter.paint_and_update_textures(
screen_size_in_pixels,
integration.egui_ctx.pixels_per_point(),
&clipped_primitives,
&textures_delta,
);
gl_window.swap_buffers().unwrap();
{
*control_flow = if integration.should_quit() {

9
egui_glow/src/painter.rs

@ -627,11 +627,16 @@ impl Painter {
}
}
pub fn clear(gl: &glow::Context, dimension: [u32; 2], clear_color: egui::Rgba) {
pub fn clear(gl: &glow::Context, screen_size_in_pixels: [u32; 2], clear_color: egui::Rgba) {
unsafe {
gl.disable(glow::SCISSOR_TEST);
gl.viewport(0, 0, dimension[0] as i32, dimension[1] as i32);
gl.viewport(
0,
0,
screen_size_in_pixels[0] as i32,
screen_size_in_pixels[1] as i32,
);
let clear_color: Color32 = clear_color.into();
gl.clear_color(

7
egui_web/src/backend.rs

@ -155,6 +155,7 @@ impl AppRunner {
},
output: Default::default(),
storage: Some(Box::new(LocalStorage::default())),
gl: painter.gl().clone(),
};
let needs_repaint: std::sync::Arc<NeedRepaint> = Default::default();
@ -274,12 +275,14 @@ impl AppRunner {
Ok((needs_repaint, clipped_primitives))
}
pub fn clear_color_buffer(&self) {
self.painter.clear(self.app.clear_color());
}
/// Paint the results of the last call to [`Self::logic`].
pub fn paint(&mut self, clipped_primitives: &[egui::ClippedPrimitive]) -> Result<(), JsValue> {
let textures_delta = std::mem::take(&mut self.textures_delta);
self.painter.clear(self.app.clear_color());
self.painter.paint_and_update_textures(
clipped_primitives,
self.egui_ctx.pixels_per_point(),

6
egui_web/src/glow_wrapping.rs

@ -32,6 +32,10 @@ impl WrappedGlowPainter {
}
impl WrappedGlowPainter {
pub fn gl(&self) -> &std::rc::Rc<glow::Context> {
self.painter.gl()
}
pub fn max_texture_side(&self) -> usize {
self.painter.max_texture_side()
}
@ -48,7 +52,7 @@ impl WrappedGlowPainter {
self.painter.free_texture(tex_id);
}
pub fn clear(&mut self, clear_color: Rgba) {
pub fn clear(&self, clear_color: Rgba) {
let canvas_dimension = [self.canvas.width(), self.canvas.height()];
egui_glow::painter::clear(self.painter.gl(), canvas_dimension, clear_color);
}

1
egui_web/src/lib.rs

@ -339,6 +339,7 @@ fn paint_and_schedule(runner_ref: &AppRunnerRef, panicked: Arc<AtomicBool>) -> R
fn paint_if_needed(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
let mut runner_lock = runner_ref.lock();
if runner_lock.needs_repaint.fetch_and_clear() {
runner_lock.clear_color_buffer();
let (needs_repaint, clipped_primitives) = runner_lock.logic()?;
runner_lock.paint(&clipped_primitives)?;
if needs_repaint {

19
epi/src/lib.rs

@ -248,8 +248,6 @@ pub struct IconData {
///
/// It provides methods to inspect the surroundings (are we on the web?),
/// allocate textures, and change settings (e.g. window size).
///
/// [`Frame`] is cheap to clone and is safe to pass to other threads.
pub struct Frame {
/// Information about the integration.
#[doc(hidden)]
@ -262,6 +260,10 @@ pub struct Frame {
/// A place where you can store custom data in a way that persists when you restart the app.
#[doc(hidden)]
pub storage: Option<Box<dyn Storage>>,
/// A reference to the underlying [`glow`] (OpenGL) context.
#[doc(hidden)]
pub gl: std::rc::Rc<glow::Context>,
}
impl Frame {
@ -285,6 +287,19 @@ impl Frame {
self.storage.as_deref_mut()
}
/// A reference to the underlying [`glow`] (OpenGL) context.
///
/// This can be used, for instance, to:
/// * Render things to offscreen buffers.
/// * Read the pixel buffer from the previous frame (`glow::Context::read_pixels`).
/// * Render things behind the egui windows.
///
/// Note that all egui painting is deferred to after the call to [`App::update`]
/// ([`egui`] only collects [`egui::Shape`]s and then eframe paints them all in one go later on).
pub fn gl(&self) -> &std::rc::Rc<glow::Context> {
&self.gl
}
/// Signal the app to stop/exit/quit the app (only works for native apps, not web apps).
/// The framework will not quit immediately, but at the end of the this frame.
pub fn quit(&mut self) {

Loading…
Cancel
Save