Compare commits
3 commits
master
...
fix-reques
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6a3e7d17ac | ||
![]() |
079490b30d | ||
![]() |
72d264ed6c |
9 changed files with 92 additions and 65 deletions
|
@ -9,7 +9,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w
|
||||||
### Added ⭐
|
### Added ⭐
|
||||||
* Added `Shape::Callback` for backend-specific painting ([#1351](https://github.com/emilk/egui/pull/1351)).
|
* Added `Shape::Callback` for backend-specific painting ([#1351](https://github.com/emilk/egui/pull/1351)).
|
||||||
* Added `Frame::canvas` ([#1362](https://github.com/emilk/egui/pull/1362)).
|
* Added `Frame::canvas` ([#1362](https://github.com/emilk/egui/pull/1362)).
|
||||||
* `Context::request_repaint` will wake up UI thread, if integrations has called `Context::set_request_repaint_callback` ([#1366](https://github.com/emilk/egui/pull/1366)).
|
* `Context::request_repaint` will wake up UI thread, if integrations has called `Context::with_repaint_callback` ([#1366](https://github.com/emilk/egui/pull/1366)).
|
||||||
* Added `Ui::push_id` ([#1374](https://github.com/emilk/egui/pull/1374)).
|
* Added `Ui::push_id` ([#1374](https://github.com/emilk/egui/pull/1374)).
|
||||||
|
|
||||||
### Changed 🔧
|
### Changed 🔧
|
||||||
|
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1005,6 +1005,7 @@ dependencies = [
|
||||||
"ahash 0.7.6",
|
"ahash 0.7.6",
|
||||||
"epaint",
|
"epaint",
|
||||||
"nohash-hasher",
|
"nohash-hasher",
|
||||||
|
"parking_lot 0.12.0",
|
||||||
"ron",
|
"ron",
|
||||||
"serde",
|
"serde",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|
|
@ -229,12 +229,11 @@ pub struct EpiIntegration {
|
||||||
impl EpiIntegration {
|
impl EpiIntegration {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
integration_name: &'static str,
|
integration_name: &'static str,
|
||||||
|
egui_ctx: egui::Context,
|
||||||
max_texture_side: usize,
|
max_texture_side: usize,
|
||||||
window: &winit::window::Window,
|
window: &winit::window::Window,
|
||||||
persistence: crate::epi::Persistence,
|
persistence: crate::epi::Persistence,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let egui_ctx = egui::Context::default();
|
|
||||||
|
|
||||||
*egui_ctx.memory() = persistence.load_memory().unwrap_or_default();
|
*egui_ctx.memory() = persistence.load_memory().unwrap_or_default();
|
||||||
|
|
||||||
let prefer_dark_mode = prefer_dark_mode();
|
let prefer_dark_mode = prefer_dark_mode();
|
||||||
|
|
|
@ -57,6 +57,7 @@ epaint = { version = "0.17.0", path = "../epaint", default-features = false }
|
||||||
|
|
||||||
ahash = "0.7"
|
ahash = "0.7"
|
||||||
nohash-hasher = "0.2"
|
nohash-hasher = "0.2"
|
||||||
|
parking_lot = "0.12"
|
||||||
|
|
||||||
# Optional:
|
# Optional:
|
||||||
ron = { version = "0.7", optional = true }
|
ron = { version = "0.7", optional = true }
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
// #![warn(missing_docs)]
|
// #![warn(missing_docs)]
|
||||||
|
|
||||||
|
use std::sync::atomic::AtomicU32;
|
||||||
|
use std::sync::atomic::Ordering::SeqCst;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
animation_manager::AnimationManager, data::output::PlatformOutput, frame_state::FrameState,
|
animation_manager::AnimationManager, data::output::PlatformOutput, frame_state::FrameState,
|
||||||
input_state::*, layers::GraphicLayers, memory::Options, output::FullOutput, TextureHandle, *,
|
input_state::*, layers::GraphicLayers, memory::Options, output::FullOutput, TextureHandle, *,
|
||||||
|
@ -8,7 +11,10 @@ use epaint::{mutex::*, stats::*, text::Fonts, TessellationOptions, *};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
struct WrappedTextureManager(Arc<RwLock<epaint::TextureManager>>);
|
// NOTE: we always use `parking_lot` here so we can
|
||||||
|
// use `WrappedTextureManager` from any thread.
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct WrappedTextureManager(Arc<parking_lot::RwLock<epaint::TextureManager>>);
|
||||||
|
|
||||||
impl Default for WrappedTextureManager {
|
impl Default for WrappedTextureManager {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
@ -21,7 +27,26 @@ impl Default for WrappedTextureManager {
|
||||||
);
|
);
|
||||||
assert_eq!(font_id, TextureId::default());
|
assert_eq!(font_id, TextureId::default());
|
||||||
|
|
||||||
Self(Arc::new(RwLock::new(tex_mngr)))
|
Self(Arc::new(parking_lot::RwLock::new(tex_mngr)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct RepaintInfo {
|
||||||
|
/// While positive, keep requesting repaints. Decrement at the end of each frame.
|
||||||
|
repaint_requests: AtomicU32,
|
||||||
|
request_repaint_callbacks: Option<Box<dyn Fn() + Send + Sync>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RepaintInfo {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
// Start with painting an extra frame to compensate for some widgets
|
||||||
|
// that take two frames before they "settle":
|
||||||
|
repaint_requests: 1.into(),
|
||||||
|
request_repaint_callbacks: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +58,6 @@ struct ContextImpl {
|
||||||
fonts: Option<Fonts>,
|
fonts: Option<Fonts>,
|
||||||
memory: Memory,
|
memory: Memory,
|
||||||
animation_manager: AnimationManager,
|
animation_manager: AnimationManager,
|
||||||
tex_manager: WrappedTextureManager,
|
|
||||||
|
|
||||||
input: InputState,
|
input: InputState,
|
||||||
|
|
||||||
|
@ -45,10 +69,6 @@ struct ContextImpl {
|
||||||
output: PlatformOutput,
|
output: PlatformOutput,
|
||||||
|
|
||||||
paint_stats: PaintStats,
|
paint_stats: PaintStats,
|
||||||
|
|
||||||
/// While positive, keep requesting repaints. Decrement at the end of each frame.
|
|
||||||
repaint_requests: u32,
|
|
||||||
request_repaint_callbacks: Option<Box<dyn Fn() + Send + Sync>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContextImpl {
|
impl ContextImpl {
|
||||||
|
@ -142,33 +162,46 @@ impl ContextImpl {
|
||||||
/// paint(full_output.textures_delta, clipped_primitives);
|
/// paint(full_output.textures_delta, clipped_primitives);
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Default)]
|
||||||
pub struct Context(Arc<RwLock<ContextImpl>>);
|
pub struct Context {
|
||||||
|
ctx: Arc<RwLock<ContextImpl>>,
|
||||||
|
// These are not inside of `ContextImpl` because we want to have thread-safe access to them
|
||||||
|
// even without running with the multi_threaded feature flag.
|
||||||
|
// See https://github.com/emilk/egui/issues/1379.
|
||||||
|
repaint_info: Arc<RepaintInfo>,
|
||||||
|
tex_manager: WrappedTextureManager,
|
||||||
|
}
|
||||||
|
|
||||||
impl std::cmp::PartialEq for Context {
|
impl std::cmp::PartialEq for Context {
|
||||||
fn eq(&self, other: &Context) -> bool {
|
fn eq(&self, other: &Context) -> bool {
|
||||||
Arc::ptr_eq(&self.0, &other.0)
|
Arc::ptr_eq(&self.ctx, &other.ctx)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Context {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(Arc::new(RwLock::new(ContextImpl {
|
|
||||||
// Start with painting an extra frame to compensate for some widgets
|
|
||||||
// that take two frames before they "settle":
|
|
||||||
repaint_requests: 1,
|
|
||||||
..ContextImpl::default()
|
|
||||||
})))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a [`Context`] with a callback that is called when the user calls
|
||||||
|
/// [`Context::request_repaint`].
|
||||||
|
pub fn with_repaint_callback(callback: impl Fn() + Send + Sync + 'static) -> Self {
|
||||||
|
Self {
|
||||||
|
ctx: Default::default(),
|
||||||
|
repaint_info: Arc::new(RepaintInfo {
|
||||||
|
request_repaint_callbacks: Some(Box::new(callback)),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
tex_manager: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn read(&self) -> RwLockReadGuard<'_, ContextImpl> {
|
fn read(&self) -> RwLockReadGuard<'_, ContextImpl> {
|
||||||
self.0.read()
|
self.ctx.read()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&self) -> RwLockWriteGuard<'_, ContextImpl> {
|
fn write(&self) -> RwLockWriteGuard<'_, ContextImpl> {
|
||||||
self.0.write()
|
self.ctx.write()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the ui code for one frame.
|
/// Run the ui code for one frame.
|
||||||
|
@ -538,26 +571,19 @@ impl Context {
|
||||||
/// If this is called at least once in a frame, then there will be another frame right after this.
|
/// If this is called at least once in a frame, then there will be another frame right after this.
|
||||||
/// Call as many times as you wish, only one repaint will be issued.
|
/// Call as many times as you wish, only one repaint will be issued.
|
||||||
///
|
///
|
||||||
|
/// It is safe to call this from any thread at any time.
|
||||||
|
///
|
||||||
/// If called from outside the UI thread, the UI thread will wake up and run,
|
/// If called from outside the UI thread, the UI thread will wake up and run,
|
||||||
/// provided the egui integration has set that up via [`Self::set_request_repaint_callback`]
|
/// provided the egui integration has set that up via [`Self::with_repaint_callback`]
|
||||||
/// (this will work on `eframe`).
|
/// (this will work on `eframe`).
|
||||||
pub fn request_repaint(&self) {
|
pub fn request_repaint(&self) {
|
||||||
// request two frames of repaint, just to cover some corner cases (frame delays):
|
// request two frames of repaint, just to cover some corner cases (frame delays):
|
||||||
let mut ctx = self.write();
|
self.repaint_info.repaint_requests.store(2, SeqCst);
|
||||||
ctx.repaint_requests = 2;
|
if let Some(callback) = &self.repaint_info.request_repaint_callbacks {
|
||||||
if let Some(callback) = &ctx.request_repaint_callbacks {
|
|
||||||
(callback)();
|
(callback)();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For integrations: this callback will be called when an egui user calls [`Self::request_repaint`].
|
|
||||||
///
|
|
||||||
/// This lets you wake up a sleeping UI thread.
|
|
||||||
pub fn set_request_repaint_callback(&self, callback: impl Fn() + Send + Sync + 'static) {
|
|
||||||
let callback = Box::new(callback);
|
|
||||||
self.write().request_repaint_callbacks = Some(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tell `egui` which fonts to use.
|
/// Tell `egui` which fonts to use.
|
||||||
///
|
///
|
||||||
/// The default `egui` fonts only support latin and cyrillic alphabets,
|
/// The default `egui` fonts only support latin and cyrillic alphabets,
|
||||||
|
@ -661,6 +687,8 @@ impl Context {
|
||||||
///
|
///
|
||||||
/// For how to load an image, see [`ImageData`] and [`ColorImage::from_rgba_unmultiplied`].
|
/// For how to load an image, see [`ImageData`] and [`ColorImage::from_rgba_unmultiplied`].
|
||||||
///
|
///
|
||||||
|
/// This is safe to call from any thread at any time.
|
||||||
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// struct MyImage {
|
/// struct MyImage {
|
||||||
/// texture: Option<egui::TextureHandle>,
|
/// texture: Option<egui::TextureHandle>,
|
||||||
|
@ -706,8 +734,10 @@ impl Context {
|
||||||
/// In general it is easier to use [`Self::load_texture`] and [`TextureHandle`].
|
/// In general it is easier to use [`Self::load_texture`] and [`TextureHandle`].
|
||||||
///
|
///
|
||||||
/// You can show stats about the allocated textures using [`Self::texture_ui`].
|
/// You can show stats about the allocated textures using [`Self::texture_ui`].
|
||||||
pub fn tex_manager(&self) -> Arc<RwLock<epaint::textures::TextureManager>> {
|
///
|
||||||
self.read().tex_manager.0.clone()
|
/// This is safe to call from any thread at any time.
|
||||||
|
pub fn tex_manager(&self) -> Arc<parking_lot::RwLock<epaint::textures::TextureManager>> {
|
||||||
|
self.tex_manager.0.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
@ -764,20 +794,19 @@ impl Context {
|
||||||
|
|
||||||
let font_image_delta = ctx_impl.fonts.as_ref().unwrap().font_image_delta();
|
let font_image_delta = ctx_impl.fonts.as_ref().unwrap().font_image_delta();
|
||||||
if let Some(font_image_delta) = font_image_delta {
|
if let Some(font_image_delta) = font_image_delta {
|
||||||
ctx_impl
|
self.tex_manager
|
||||||
.tex_manager
|
|
||||||
.0
|
.0
|
||||||
.write()
|
.write()
|
||||||
.set(TextureId::default(), font_image_delta);
|
.set(TextureId::default(), font_image_delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
textures_delta = ctx_impl.tex_manager.0.write().take_delta();
|
textures_delta = self.tex_manager.0.write().take_delta();
|
||||||
};
|
};
|
||||||
|
|
||||||
let platform_output: PlatformOutput = std::mem::take(&mut self.output());
|
let platform_output: PlatformOutput = std::mem::take(&mut self.output());
|
||||||
|
|
||||||
let needs_repaint = if self.read().repaint_requests > 0 {
|
let needs_repaint = if self.repaint_info.repaint_requests.load(SeqCst) > 0 {
|
||||||
self.write().repaint_requests -= 1;
|
self.repaint_info.repaint_requests.fetch_sub(1, SeqCst);
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|
|
@ -46,20 +46,21 @@ pub fn run(app_name: &str, native_options: &epi::NativeOptions, app_creator: epi
|
||||||
let mut painter = crate::Painter::new(gl.clone(), None, "")
|
let mut painter = crate::Painter::new(gl.clone(), None, "")
|
||||||
.unwrap_or_else(|error| panic!("some OpenGL error occurred {}\n", error));
|
.unwrap_or_else(|error| panic!("some OpenGL error occurred {}\n", error));
|
||||||
|
|
||||||
|
let event_loop_proxy = parking_lot::Mutex::new(event_loop.create_proxy());
|
||||||
|
let egui_ctx = egui::Context::with_repaint_callback({
|
||||||
|
move || {
|
||||||
|
event_loop_proxy.lock().send_event(RequestRepaintEvent).ok();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let mut integration = egui_winit::epi::EpiIntegration::new(
|
let mut integration = egui_winit::epi::EpiIntegration::new(
|
||||||
"egui_glow",
|
"egui_glow",
|
||||||
|
egui_ctx,
|
||||||
painter.max_texture_side(),
|
painter.max_texture_side(),
|
||||||
gl_window.window(),
|
gl_window.window(),
|
||||||
persistence,
|
persistence,
|
||||||
);
|
);
|
||||||
|
|
||||||
{
|
|
||||||
let event_loop_proxy = parking_lot::Mutex::new(event_loop.create_proxy());
|
|
||||||
integration.egui_ctx.set_request_repaint_callback(move || {
|
|
||||||
event_loop_proxy.lock().send_event(RequestRepaintEvent).ok();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut app = app_creator(&epi::CreationContext {
|
let mut app = app_creator(&epi::CreationContext {
|
||||||
egui_ctx: integration.egui_ctx.clone(),
|
egui_ctx: integration.egui_ctx.clone(),
|
||||||
integration_info: integration.frame.info(),
|
integration_info: integration.frame.info(),
|
||||||
|
|
|
@ -159,14 +159,12 @@ impl AppRunner {
|
||||||
|
|
||||||
let needs_repaint: std::sync::Arc<NeedRepaint> = Default::default();
|
let needs_repaint: std::sync::Arc<NeedRepaint> = Default::default();
|
||||||
|
|
||||||
let egui_ctx = egui::Context::default();
|
let egui_ctx = egui::Context::with_repaint_callback({
|
||||||
|
|
||||||
{
|
|
||||||
let needs_repaint = needs_repaint.clone();
|
let needs_repaint = needs_repaint.clone();
|
||||||
egui_ctx.set_request_repaint_callback(move || {
|
move || {
|
||||||
needs_repaint.0.store(true, SeqCst);
|
needs_repaint.0.store(true, SeqCst);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
load_memory(&egui_ctx);
|
load_memory(&egui_ctx);
|
||||||
if prefer_dark_mode == Some(true) {
|
if prefer_dark_mode == Some(true) {
|
||||||
|
|
|
@ -51,7 +51,7 @@ single_threaded = ["atomic_refcell"]
|
||||||
|
|
||||||
# Only needed if you plan to use the same fonts from multiple threads.
|
# Only needed if you plan to use the same fonts from multiple threads.
|
||||||
# It comes with a minor performance impact.
|
# It comes with a minor performance impact.
|
||||||
multi_threaded = ["parking_lot"]
|
multi_threaded = []
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -63,7 +63,7 @@ atomic_refcell = { version = "0.1", optional = true } # Used
|
||||||
bytemuck = { version = "1.7.2", optional = true, features = ["derive"] }
|
bytemuck = { version = "1.7.2", optional = true, features = ["derive"] }
|
||||||
cint = { version = "^0.2.2", optional = true }
|
cint = { version = "^0.2.2", optional = true }
|
||||||
nohash-hasher = "0.2"
|
nohash-hasher = "0.2"
|
||||||
parking_lot = { version = "0.12", optional = true } # Using parking_lot over std::sync::Mutex gives 50% speedups in some real-world scenarios.
|
parking_lot = "0.12"
|
||||||
serde = { version = "1", optional = true, features = ["derive", "rc"] }
|
serde = { version = "1", optional = true, features = ["derive", "rc"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use crate::{
|
use crate::{emath::NumExt, mutex::Arc, ImageData, ImageDelta, TextureId, TextureManager};
|
||||||
emath::NumExt,
|
|
||||||
mutex::{Arc, RwLock},
|
use parking_lot::RwLock;
|
||||||
ImageData, ImageDelta, TextureId, TextureManager,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Used to paint images.
|
/// Used to paint images.
|
||||||
///
|
///
|
||||||
|
|
Loading…
Reference in a new issue