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.
This commit is contained in:
parent
b7ebe16cfb
commit
8f178fa4e0
10 changed files with 52 additions and 27 deletions
|
@ -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`
|
* You can now load/save state in `App::update`
|
||||||
* Changed `App::update` to take `&mut Frame` instead of `&Frame`.
|
* Changed `App::update` to take `&mut Frame` instead of `&Frame`.
|
||||||
* `Frame` is no longer `Clone` or `Sync`.
|
* `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
|
## 0.17.0 - 2022-02-22
|
||||||
|
|
|
@ -149,6 +149,7 @@ pub struct EpiIntegration {
|
||||||
impl EpiIntegration {
|
impl EpiIntegration {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
integration_name: &'static str,
|
integration_name: &'static str,
|
||||||
|
gl: std::rc::Rc<glow::Context>,
|
||||||
max_texture_side: usize,
|
max_texture_side: usize,
|
||||||
window: &winit::window::Window,
|
window: &winit::window::Window,
|
||||||
storage: Option<Box<dyn epi::Storage>>,
|
storage: Option<Box<dyn epi::Storage>>,
|
||||||
|
@ -169,6 +170,7 @@ impl EpiIntegration {
|
||||||
},
|
},
|
||||||
output: Default::default(),
|
output: Default::default(),
|
||||||
storage,
|
storage,
|
||||||
|
gl,
|
||||||
};
|
};
|
||||||
|
|
||||||
if prefer_dark_mode == Some(true) {
|
if prefer_dark_mode == Some(true) {
|
||||||
|
|
|
@ -792,7 +792,7 @@ impl Ui {
|
||||||
/// So one can think of `cursor` as a constraint on the available region.
|
/// 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.
|
/// 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 {
|
pub fn cursor(&self) -> Rect {
|
||||||
self.placer.cursor()
|
self.placer.cursor()
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ enum RunMode {
|
||||||
/// For instance, a GUI for a thermostat need to repaint each time the temperature changes.
|
/// 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
|
/// 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
|
/// 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.
|
/// and for games and interactive tools that need repainting every frame anyway, this should be the default.
|
||||||
Reactive,
|
Reactive,
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::*;
|
|
||||||
use egui_winit::winit;
|
use egui_winit::winit;
|
||||||
|
|
||||||
struct RequestRepaintEvent;
|
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(
|
let mut integration = egui_winit::epi::EpiIntegration::new(
|
||||||
"egui_glow",
|
"egui_glow",
|
||||||
|
gl.clone(),
|
||||||
painter.max_texture_side(),
|
painter.max_texture_side(),
|
||||||
gl_window.window(),
|
gl_window.window(),
|
||||||
storage,
|
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));
|
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 {
|
let egui::FullOutput {
|
||||||
platform_output,
|
platform_output,
|
||||||
needs_repaint,
|
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);
|
let clipped_primitives = integration.egui_ctx.tessellate(shapes);
|
||||||
|
|
||||||
// paint:
|
painter.paint_and_update_textures(
|
||||||
{
|
screen_size_in_pixels,
|
||||||
let color = app.clear_color();
|
integration.egui_ctx.pixels_per_point(),
|
||||||
unsafe {
|
&clipped_primitives,
|
||||||
use glow::HasContext as _;
|
&textures_delta,
|
||||||
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();
|
gl_window.swap_buffers().unwrap();
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
*control_flow = if integration.should_quit() {
|
*control_flow = if integration.should_quit() {
|
||||||
|
|
|
@ -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 {
|
unsafe {
|
||||||
gl.disable(glow::SCISSOR_TEST);
|
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();
|
let clear_color: Color32 = clear_color.into();
|
||||||
gl.clear_color(
|
gl.clear_color(
|
||||||
|
|
|
@ -155,6 +155,7 @@ impl AppRunner {
|
||||||
},
|
},
|
||||||
output: Default::default(),
|
output: Default::default(),
|
||||||
storage: Some(Box::new(LocalStorage::default())),
|
storage: Some(Box::new(LocalStorage::default())),
|
||||||
|
gl: painter.gl().clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let needs_repaint: std::sync::Arc<NeedRepaint> = Default::default();
|
let needs_repaint: std::sync::Arc<NeedRepaint> = Default::default();
|
||||||
|
@ -274,12 +275,14 @@ impl AppRunner {
|
||||||
Ok((needs_repaint, clipped_primitives))
|
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`].
|
/// Paint the results of the last call to [`Self::logic`].
|
||||||
pub fn paint(&mut self, clipped_primitives: &[egui::ClippedPrimitive]) -> Result<(), JsValue> {
|
pub fn paint(&mut self, clipped_primitives: &[egui::ClippedPrimitive]) -> Result<(), JsValue> {
|
||||||
let textures_delta = std::mem::take(&mut self.textures_delta);
|
let textures_delta = std::mem::take(&mut self.textures_delta);
|
||||||
|
|
||||||
self.painter.clear(self.app.clear_color());
|
|
||||||
|
|
||||||
self.painter.paint_and_update_textures(
|
self.painter.paint_and_update_textures(
|
||||||
clipped_primitives,
|
clipped_primitives,
|
||||||
self.egui_ctx.pixels_per_point(),
|
self.egui_ctx.pixels_per_point(),
|
||||||
|
|
|
@ -32,6 +32,10 @@ impl WrappedGlowPainter {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WrappedGlowPainter {
|
impl WrappedGlowPainter {
|
||||||
|
pub fn gl(&self) -> &std::rc::Rc<glow::Context> {
|
||||||
|
self.painter.gl()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn max_texture_side(&self) -> usize {
|
pub fn max_texture_side(&self) -> usize {
|
||||||
self.painter.max_texture_side()
|
self.painter.max_texture_side()
|
||||||
}
|
}
|
||||||
|
@ -48,7 +52,7 @@ impl WrappedGlowPainter {
|
||||||
self.painter.free_texture(tex_id);
|
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()];
|
let canvas_dimension = [self.canvas.width(), self.canvas.height()];
|
||||||
egui_glow::painter::clear(self.painter.gl(), canvas_dimension, clear_color);
|
egui_glow::painter::clear(self.painter.gl(), canvas_dimension, clear_color);
|
||||||
}
|
}
|
||||||
|
|
|
@ -339,6 +339,7 @@ fn paint_and_schedule(runner_ref: &AppRunnerRef, panicked: Arc<AtomicBool>) -> R
|
||||||
fn paint_if_needed(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
|
fn paint_if_needed(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
|
||||||
let mut runner_lock = runner_ref.lock();
|
let mut runner_lock = runner_ref.lock();
|
||||||
if runner_lock.needs_repaint.fetch_and_clear() {
|
if runner_lock.needs_repaint.fetch_and_clear() {
|
||||||
|
runner_lock.clear_color_buffer();
|
||||||
let (needs_repaint, clipped_primitives) = runner_lock.logic()?;
|
let (needs_repaint, clipped_primitives) = runner_lock.logic()?;
|
||||||
runner_lock.paint(&clipped_primitives)?;
|
runner_lock.paint(&clipped_primitives)?;
|
||||||
if needs_repaint {
|
if needs_repaint {
|
||||||
|
|
|
@ -248,8 +248,6 @@ pub struct IconData {
|
||||||
///
|
///
|
||||||
/// It provides methods to inspect the surroundings (are we on the web?),
|
/// It provides methods to inspect the surroundings (are we on the web?),
|
||||||
/// allocate textures, and change settings (e.g. window size).
|
/// 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 {
|
pub struct Frame {
|
||||||
/// Information about the integration.
|
/// Information about the integration.
|
||||||
#[doc(hidden)]
|
#[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.
|
/// A place where you can store custom data in a way that persists when you restart the app.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub storage: Option<Box<dyn Storage>>,
|
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 {
|
impl Frame {
|
||||||
|
@ -285,6 +287,19 @@ impl Frame {
|
||||||
self.storage.as_deref_mut()
|
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).
|
/// 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.
|
/// The framework will not quit immediately, but at the end of the this frame.
|
||||||
pub fn quit(&mut self) {
|
pub fn quit(&mut self) {
|
||||||
|
|
Loading…
Reference in a new issue