mirror of
https://github.com/Zenithsiz/zsw.git
synced 2026-02-04 02:08:37 +00:00
Egui paint jobs now go over a channel.
This commit is contained in:
parent
461ce33a17
commit
8b586614eb
@ -4,12 +4,12 @@
|
||||
{
|
||||
"geometry": {
|
||||
"pos": {
|
||||
"x": 0,
|
||||
"y": 312
|
||||
"x": 1360,
|
||||
"y": 0
|
||||
},
|
||||
"size": {
|
||||
"x": 1360,
|
||||
"y": 768
|
||||
"x": 1920,
|
||||
"y": 1080
|
||||
}
|
||||
},
|
||||
"duration": 1800,
|
||||
|
||||
@ -7,13 +7,12 @@
|
||||
use {
|
||||
anyhow::Context,
|
||||
crossbeam::atomic::AtomicCell,
|
||||
futures::{channel::mpsc, SinkExt},
|
||||
std::{
|
||||
sync::Arc,
|
||||
task::Waker,
|
||||
time::{Duration, Instant},
|
||||
},
|
||||
winit::window::Window,
|
||||
zsw_util::FetchUpdate,
|
||||
zsw_wgpu::Wgpu,
|
||||
};
|
||||
|
||||
@ -25,9 +24,6 @@ pub struct Egui {
|
||||
|
||||
/// Last frame time
|
||||
frame_time: AtomicCell<Option<Duration>>,
|
||||
|
||||
/// Paint jobs waker
|
||||
paint_jobs_waker: AtomicCell<Option<Waker>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Egui {
|
||||
@ -35,7 +31,6 @@ impl std::fmt::Debug for Egui {
|
||||
f.debug_struct("Egui")
|
||||
.field("repaint_signal", &self.repaint_signal)
|
||||
.field("frame_time", &self.frame_time)
|
||||
.field("paint_jobs_waker", &"..")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@ -46,15 +41,7 @@ impl Egui {
|
||||
pub fn new(
|
||||
window: &Window,
|
||||
wgpu: &Wgpu,
|
||||
) -> Result<
|
||||
(
|
||||
Self,
|
||||
EguiPlatformResource,
|
||||
EguiRenderPassResource,
|
||||
EguiPaintJobsResource,
|
||||
),
|
||||
anyhow::Error,
|
||||
> {
|
||||
) -> Result<(Self, EguiPlatformResource, EguiRenderPassResource, EguiPainterResource), anyhow::Error> {
|
||||
// Create the egui platform
|
||||
// TODO: Check if it's fine to use the window size here instead of the
|
||||
// wgpu surface size
|
||||
@ -77,18 +64,20 @@ impl Egui {
|
||||
let service = Self {
|
||||
repaint_signal,
|
||||
frame_time: AtomicCell::new(None),
|
||||
paint_jobs_waker: AtomicCell::new(None),
|
||||
};
|
||||
|
||||
// Create the resources
|
||||
// Note: By using a 0-size channel we achieve the least latency
|
||||
let (paint_jobs_tx, paint_jobs_rx) = mpsc::channel(0);
|
||||
let platform_resource = EguiPlatformResource { platform };
|
||||
let render_pass_resource = EguiRenderPassResource { render_pass };
|
||||
let paint_jobs_resource = EguiPaintJobsResource {
|
||||
paint_jobs: FetchUpdate::new(vec![]),
|
||||
let render_pass_resource = EguiRenderPassResource {
|
||||
render_pass,
|
||||
paint_jobs: vec![],
|
||||
paint_jobs_rx,
|
||||
};
|
||||
let painter_resource = EguiPainterResource { paint_jobs_tx };
|
||||
|
||||
|
||||
Ok((service, platform_resource, render_pass_resource, paint_jobs_resource))
|
||||
Ok((service, platform_resource, render_pass_resource, painter_resource))
|
||||
}
|
||||
|
||||
/// Draws egui
|
||||
@ -138,25 +127,23 @@ impl Egui {
|
||||
platform_resource.platform.context().font_image()
|
||||
}
|
||||
|
||||
/// Returns the current paint jobs
|
||||
pub fn paint_jobs<'a>(&self, paint_jobs_resource: &'a mut EguiPaintJobsResource) -> &'a [egui::ClippedMesh] {
|
||||
// Get the paint jobs
|
||||
let paint_jobs = paint_jobs_resource.paint_jobs.fetch();
|
||||
|
||||
// If we have a waker, wake them
|
||||
if let Some(waker) = self.paint_jobs_waker.take() {
|
||||
waker.wake();
|
||||
}
|
||||
|
||||
paint_jobs
|
||||
}
|
||||
|
||||
/// Returns the render pass
|
||||
pub fn render_pass<'a>(
|
||||
/// Returns the render pass and paint jobs
|
||||
pub fn render_pass_with_paint_jobs<'a>(
|
||||
&self,
|
||||
render_pass_resource: &'a mut EguiRenderPassResource,
|
||||
) -> &'a mut egui_wgpu_backend::RenderPass {
|
||||
&mut render_pass_resource.render_pass
|
||||
) -> (&'a mut egui_wgpu_backend::RenderPass, &'a [egui::ClippedMesh]) {
|
||||
// If we have any new paint jobs, update them
|
||||
// TODO: Not panic here when the painter quit
|
||||
if let Ok(paint_jobs) = render_pass_resource
|
||||
.paint_jobs_rx
|
||||
.try_next()
|
||||
.transpose()
|
||||
.expect("Egui painter quit")
|
||||
{
|
||||
render_pass_resource.paint_jobs = paint_jobs;
|
||||
}
|
||||
|
||||
(&mut render_pass_resource.render_pass, &render_pass_resource.paint_jobs)
|
||||
}
|
||||
|
||||
/// Updates the paint jobs
|
||||
@ -164,21 +151,15 @@ impl Egui {
|
||||
/// Returns `Err` if they haven't been fetched yet
|
||||
pub async fn update_paint_jobs(
|
||||
&self,
|
||||
paint_jobs_resource: &mut EguiPaintJobsResource,
|
||||
painter_resource: &mut EguiPainterResource,
|
||||
paint_jobs: Vec<egui::ClippedMesh>,
|
||||
) -> Result<(), Vec<egui::ClippedMesh>> {
|
||||
paint_jobs_resource.paint_jobs.update(paint_jobs)
|
||||
}
|
||||
|
||||
/// Registers a waker to be woken up by the paint jobs being fetched
|
||||
pub fn set_paint_jobs_waker(&self, paint_jobs_resource: &EguiPaintJobsResource, waker: Waker) {
|
||||
// Set the waker
|
||||
self.paint_jobs_waker.store(Some(waker));
|
||||
|
||||
// If the paint jobs were fetched in the meantime without waking, wake up
|
||||
if paint_jobs_resource.paint_jobs.is_seen() && let Some(waker) = self.paint_jobs_waker.take() {
|
||||
waker.wake();
|
||||
}
|
||||
) {
|
||||
// TDO: Not panic
|
||||
painter_resource
|
||||
.paint_jobs_tx
|
||||
.send(paint_jobs)
|
||||
.await
|
||||
.expect("Egui renderer quit");
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,7 +177,14 @@ impl std::fmt::Debug for EguiPlatformResource {
|
||||
|
||||
/// Render pass resource
|
||||
pub struct EguiRenderPassResource {
|
||||
/// Render pass
|
||||
render_pass: egui_wgpu_backend::RenderPass,
|
||||
|
||||
/// Current paint jobs
|
||||
paint_jobs: Vec<egui::ClippedMesh>,
|
||||
|
||||
/// Paint jobs receiver
|
||||
paint_jobs_rx: mpsc::Receiver<Vec<egui::ClippedMesh>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for EguiRenderPassResource {
|
||||
@ -207,10 +195,11 @@ impl std::fmt::Debug for EguiRenderPassResource {
|
||||
}
|
||||
}
|
||||
|
||||
/// Paint jobs resource
|
||||
/// Painter resource
|
||||
#[derive(Debug)]
|
||||
pub struct EguiPaintJobsResource {
|
||||
paint_jobs: FetchUpdate<Vec<egui::ClippedMesh>>,
|
||||
pub struct EguiPainterResource {
|
||||
/// Paint jobs sender
|
||||
paint_jobs_tx: mpsc::Sender<Vec<egui::ClippedMesh>>,
|
||||
}
|
||||
|
||||
/// Repaint signal
|
||||
|
||||
@ -9,7 +9,7 @@ use {
|
||||
std::{mem, time::Duration},
|
||||
tokio::time::Instant,
|
||||
winit::window::Window,
|
||||
zsw_egui::{Egui, EguiPaintJobsResource, EguiPlatformResource, EguiRenderPassResource},
|
||||
zsw_egui::{Egui, EguiPainterResource, EguiPlatformResource, EguiRenderPassResource},
|
||||
zsw_img::ImageLoader,
|
||||
zsw_input::Input,
|
||||
zsw_panels::{Panels, PanelsResource},
|
||||
@ -50,7 +50,7 @@ impl Renderer {
|
||||
+ Resources<WgpuSurfaceResource>
|
||||
+ Resources<EguiPlatformResource>
|
||||
+ Resources<EguiRenderPassResource>
|
||||
+ Resources<EguiPaintJobsResource>,
|
||||
+ Resources<EguiPainterResource>,
|
||||
{
|
||||
// Duration we're sleeping
|
||||
let sleep_duration = Duration::from_secs_f32(1.0 / 60.0);
|
||||
@ -104,9 +104,8 @@ impl Renderer {
|
||||
/// Lock tree:
|
||||
/// [`zsw_wgpu::SurfaceLock`] on `wgpu`
|
||||
/// - [`zsw_panels::PanelsLock`] on `panels`
|
||||
/// - [`zsw_egui::PaintJobsLock`] on `egui`
|
||||
/// - [`zsw_egui::RenderPassLock`] on `egui`
|
||||
/// - [`zsw_egui::PlatformLock`] on `egui`
|
||||
/// - [`zsw_egui::RenderPassLock`] on `egui`
|
||||
/// - [`zsw_egui::PlatformLock`] on `egui`
|
||||
async fn render<S, R>(services: &S, resources: &R) -> Result<(), anyhow::Error>
|
||||
where
|
||||
S: Services<Wgpu> + Services<Egui> + Services<Window> + Services<Panels> + Services<Input>,
|
||||
@ -114,7 +113,7 @@ impl Renderer {
|
||||
+ Resources<WgpuSurfaceResource>
|
||||
+ Resources<EguiPlatformResource>
|
||||
+ Resources<EguiRenderPassResource>
|
||||
+ Resources<EguiPaintJobsResource>,
|
||||
+ Resources<EguiPainterResource>,
|
||||
{
|
||||
let wgpu = services.service::<Wgpu>();
|
||||
let egui = services.service::<Egui>();
|
||||
@ -158,15 +157,11 @@ impl Renderer {
|
||||
|
||||
// Get the egui render results
|
||||
// DEADLOCK: Caller ensures we can lock it.
|
||||
let mut egui_paint_jobs_resource = resources.resource::<EguiPaintJobsResource>().await;
|
||||
let egui_paint_jobs = egui.paint_jobs(&mut egui_paint_jobs_resource);
|
||||
let mut render_pass_resource = resources.resource::<EguiRenderPassResource>().await;
|
||||
let (egui_render_pass, egui_paint_jobs) = egui.render_pass_with_paint_jobs(&mut render_pass_resource);
|
||||
|
||||
// If we have any paint jobs, draw egui
|
||||
if !egui_paint_jobs.is_empty() {
|
||||
// DEADLOCK: Caller ensures we can lock it after the wgpu surface lock
|
||||
let mut render_pass_resource = resources.resource::<EguiRenderPassResource>().await;
|
||||
let egui_render_pass = egui.render_pass(&mut render_pass_resource);
|
||||
|
||||
let font_image = {
|
||||
// DEADLOCK: Caller ensures we can lock it after the egui render pass lock
|
||||
let platform_resource = resources.resource::<EguiPlatformResource>().await;
|
||||
@ -189,7 +184,7 @@ impl Renderer {
|
||||
.context("Unable to render egui")?;
|
||||
}
|
||||
|
||||
mem::drop(egui_paint_jobs_resource);
|
||||
mem::drop(render_pass_resource);
|
||||
wgpu.finish_render(frame);
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -15,16 +15,15 @@ use {
|
||||
egui::{plot, Widget},
|
||||
futures::lock::Mutex,
|
||||
pollster::FutureExt,
|
||||
std::mem,
|
||||
winit::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
window::Window,
|
||||
},
|
||||
zsw_egui::{Egui, EguiPaintJobsResource, EguiPlatformResource},
|
||||
zsw_egui::{Egui, EguiPainterResource, EguiPlatformResource},
|
||||
zsw_panels::{Panel, PanelState, PanelStateImage, PanelStateImages, Panels, PanelsResource},
|
||||
zsw_playlist::{Playlist, PlaylistImage, PlaylistResource},
|
||||
zsw_profiles::{Profile, Profiles, ProfilesResource},
|
||||
zsw_util::{CondvarFuture, Rect, Resources, Services},
|
||||
zsw_util::{Rect, Resources, Services},
|
||||
zsw_wgpu::{Wgpu, WgpuSurfaceResource},
|
||||
};
|
||||
|
||||
@ -82,7 +81,7 @@ impl SettingsWindow {
|
||||
/// - [`zsw_profiles::ProfilesLock`] on `profiles`
|
||||
/// - [`zsw_playlist::PlaylistLock`] on `playlist`
|
||||
/// - [`zsw_panels::PanelsLock`] on `panels`
|
||||
/// Blocks until [`Self::paint_jobs`] on `egui` is called.
|
||||
/// Blocks until [`Self::update_paint_jobs`] on `egui` is called.
|
||||
pub async fn run<S, R>(&self, services: &S, resources: &R) -> !
|
||||
where
|
||||
S: Services<Wgpu>
|
||||
@ -96,7 +95,7 @@ impl SettingsWindow {
|
||||
+ Resources<ProfilesResource>
|
||||
+ Resources<WgpuSurfaceResource>
|
||||
+ Resources<EguiPlatformResource>
|
||||
+ Resources<EguiPaintJobsResource>,
|
||||
+ Resources<EguiPainterResource>,
|
||||
{
|
||||
let wgpu = services.service::<Wgpu>();
|
||||
let egui = services.service::<Egui>();
|
||||
@ -148,26 +147,10 @@ impl SettingsWindow {
|
||||
})
|
||||
};
|
||||
|
||||
// Try to update the paint jobs
|
||||
let mut egui_painter_resource = resources.resource::<EguiPainterResource>().await;
|
||||
match res {
|
||||
// If we got the paint jobs, try to update
|
||||
Ok(mut paint_jobs) => loop {
|
||||
let mut paint_jobs_resource = resources.resource::<EguiPaintJobsResource>().await;
|
||||
match egui.update_paint_jobs(&mut paint_jobs_resource, paint_jobs).await {
|
||||
Ok(()) => break,
|
||||
// If we didn't get it, register a waker and wait
|
||||
Err(old_paint_jobs) => {
|
||||
// Drop the resource and wait until woken
|
||||
CondvarFuture::new(|waker| {
|
||||
egui.set_paint_jobs_waker(&paint_jobs_resource, waker.clone());
|
||||
mem::drop(paint_jobs_resource);
|
||||
})
|
||||
.await;
|
||||
|
||||
// Then try again
|
||||
paint_jobs = old_paint_jobs;
|
||||
},
|
||||
}
|
||||
},
|
||||
Ok(paint_jobs) => egui.update_paint_jobs(&mut egui_painter_resource, paint_jobs).await,
|
||||
Err(err) => tracing::warn!(?err, "Unable to draw egui"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ pub async fn create_services_resources(window: Arc<Window>) -> Result<(Services,
|
||||
Panels::new(wgpu.device(), wgpu.surface_texture_format()).context("Unable to create panels")?;
|
||||
|
||||
// Create egui
|
||||
let (egui, egui_platform_resource, egui_render_pass_resource, egui_paint_jobs_resource) =
|
||||
let (egui, egui_platform_resource, egui_render_pass_resource, egui_painter_resource) =
|
||||
Egui::new(&window, &wgpu).context("Unable to create egui state")?;
|
||||
|
||||
// Create the profiles
|
||||
@ -127,7 +127,7 @@ pub async fn create_services_resources(window: Arc<Window>) -> Result<(Services,
|
||||
wgpu_surface: Mutex::new(wgpu_surface_resource),
|
||||
egui_platform: Mutex::new(egui_platform_resource),
|
||||
egui_render_pass: Mutex::new(egui_render_pass_resource),
|
||||
egui_paint_jobs: Mutex::new(egui_paint_jobs_resource),
|
||||
egui_painter: Mutex::new(egui_painter_resource),
|
||||
};
|
||||
|
||||
Ok((services, resources))
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
//! Resources
|
||||
|
||||
use zsw_egui::EguiPaintJobsResource;
|
||||
|
||||
// Imports
|
||||
use {
|
||||
futures::lock::{Mutex, MutexLockFuture},
|
||||
zsw_egui::{EguiPlatformResource, EguiRenderPassResource},
|
||||
zsw_egui::{EguiPainterResource, EguiPlatformResource, EguiRenderPassResource},
|
||||
zsw_panels::PanelsResource,
|
||||
zsw_playlist::PlaylistResource,
|
||||
zsw_profiles::ProfilesResource,
|
||||
@ -34,8 +32,8 @@ pub struct Resources {
|
||||
/// Egui render pass
|
||||
pub egui_render_pass: Mutex<EguiRenderPassResource>,
|
||||
|
||||
/// Egui paint jobs
|
||||
pub egui_paint_jobs: Mutex<EguiPaintJobsResource>,
|
||||
/// Egui painter
|
||||
pub egui_painter: Mutex<EguiPainterResource>,
|
||||
}
|
||||
|
||||
impl ResourcesBundle for Resources {}
|
||||
@ -48,7 +46,7 @@ impl ResourcesBundle for Resources {}
|
||||
[ WgpuSurfaceResource ] [ wgpu_surface ];
|
||||
[ EguiPlatformResource ] [ egui_platform ];
|
||||
[ EguiRenderPassResource ] [ egui_render_pass ];
|
||||
[ EguiPaintJobsResource ] [ egui_paint_jobs ];
|
||||
[ EguiPainterResource ] [ egui_painter ];
|
||||
)]
|
||||
impl zsw_util::Resources<ty> for Resources {
|
||||
fn lock(&self) -> MutexLockFuture<ty> {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user