mirror of
https://github.com/Zenithsiz/zsw.git
synced 2026-02-07 11:59:19 +00:00
Removed App and Inner, decentralizing the full app state.
This commit is contained in:
parent
8403aa4522
commit
8b33caa0b1
391
src/app.rs
391
src/app.rs
@ -2,6 +2,9 @@
|
||||
//!
|
||||
//! See the [`App`] type for more details
|
||||
|
||||
// Lints
|
||||
#![allow(clippy::too_many_arguments)] // We need to share a lot of state and we can't couple it together in most cases
|
||||
|
||||
// Imports
|
||||
use crate::{paths, util, Args, Egui, ImageLoader, Panel, PanelState, PanelsProfile, PanelsRenderer, Rect, Wgpu};
|
||||
use anyhow::Context;
|
||||
@ -9,6 +12,7 @@ use cgmath::{Point2, Vector2};
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
use egui::Widget;
|
||||
use parking_lot::Mutex;
|
||||
use pollster::FutureExt;
|
||||
use std::{mem, num::NonZeroUsize, thread, time::Duration};
|
||||
use winit::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
@ -22,168 +26,124 @@ use winit::{
|
||||
};
|
||||
use x11::xlib;
|
||||
|
||||
/// Inner state
|
||||
// Note: This is required because the event loop can't be
|
||||
// shared in between threads, but everything else we need
|
||||
// to share.
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
/// Window
|
||||
///
|
||||
/// [`Wgpu`] required a static reference to it, which is
|
||||
/// why we leak it.
|
||||
window: &'static Window,
|
||||
/// Runs the application
|
||||
pub fn run(args: &Args) -> Result<(), anyhow::Error> {
|
||||
// Build the window
|
||||
let (mut event_loop, window) = self::create_window(args)?;
|
||||
|
||||
// Create the wgpu interface
|
||||
let wgpu = Wgpu::new(window).block_on().context("Unable to create renderer")?;
|
||||
|
||||
// Create the paths channel
|
||||
let (paths_distributer, paths_rx) = paths::new(args.images_dir.clone());
|
||||
|
||||
// Create the image loader
|
||||
let image_loader = ImageLoader::new(paths_rx).context("Unable to create image loader")?;
|
||||
|
||||
// Create all panels
|
||||
let panels = args
|
||||
.panel_geometries
|
||||
.iter()
|
||||
.map(|&geometry| Panel::new(geometry, PanelState::Empty, args.image_duration, args.fade_point))
|
||||
.collect::<Vec<_>>();
|
||||
let panels = Mutex::new(panels);
|
||||
|
||||
// Create the panels renderer
|
||||
let panels_renderer = PanelsRenderer::new(wgpu.device(), wgpu.surface_texture_format())
|
||||
.context("Unable to create panels renderer")?;
|
||||
|
||||
// Create egui
|
||||
let egui = Egui::new(window, &wgpu).context("Unable to create egui state")?;
|
||||
|
||||
// Read all profiles
|
||||
let _profiles: Vec<PanelsProfile> = match util::parse_json_from_file("zsw_profiles.json") {
|
||||
Ok(profiles) => {
|
||||
log::info!("Loaded profiles {profiles:#?}");
|
||||
profiles
|
||||
},
|
||||
Err(err) => {
|
||||
log::info!("Unable to load profiles: {err:?}");
|
||||
vec![]
|
||||
},
|
||||
};
|
||||
|
||||
let queued_settings_window_open_click = AtomicCell::new(None);
|
||||
|
||||
// Create the event handler
|
||||
let mut event_handler = EventHandler::new(&wgpu, &egui, &queued_settings_window_open_click);
|
||||
|
||||
// Create the renderer
|
||||
let mut renderer = Renderer::new(
|
||||
window,
|
||||
&wgpu,
|
||||
&paths_distributer,
|
||||
&image_loader,
|
||||
&panels_renderer,
|
||||
&panels,
|
||||
&egui,
|
||||
&queued_settings_window_open_click,
|
||||
);
|
||||
|
||||
// Start all threads and then wait in the main thread for events
|
||||
// TODO: Not ignore errors here, although given how `thread::scope` works
|
||||
// it's somewhat hard to do so
|
||||
crossbeam::thread::scope(|s| {
|
||||
// Spawn the path distributer thread
|
||||
let _path_distributer = util::spawn_scoped(s, "Path distributer", || paths_distributer.run())?;
|
||||
|
||||
// Spawn all image loaders
|
||||
let loader_threads = thread::available_parallelism().map_or(1, NonZeroUsize::get);
|
||||
let _image_loaders = util::spawn_scoped_multiple(s, "Image loader", loader_threads, || || image_loader.run())?;
|
||||
|
||||
// Spawn the renderer thread
|
||||
let _renderer_thread = util::spawn_scoped(s, "Renderer", || renderer.run())?;
|
||||
|
||||
// Run event loop in this thread until we quit
|
||||
event_loop.run_return(|event, _, control_flow| {
|
||||
event_handler.handle_event(event, control_flow);
|
||||
});
|
||||
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.expect("Unable to start all threads")
|
||||
.expect("Unable to run all threads 'till completion");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Event handler
|
||||
pub struct EventHandler<'a> {
|
||||
/// Wgpu
|
||||
wgpu: Wgpu,
|
||||
|
||||
/// Path distributer
|
||||
paths_distributer: paths::Distributer,
|
||||
|
||||
/// Image loader
|
||||
image_loader: ImageLoader,
|
||||
|
||||
/// Panels renderer
|
||||
panels_renderer: PanelsRenderer,
|
||||
|
||||
/// Panels
|
||||
panels: Mutex<Vec<Panel>>,
|
||||
wgpu: &'a Wgpu,
|
||||
|
||||
/// Egui
|
||||
egui: Egui,
|
||||
egui: &'a Egui,
|
||||
|
||||
/// Cursor position
|
||||
cursor_pos: Option<PhysicalPosition<f64>>,
|
||||
|
||||
/// Queued settings window open click
|
||||
queued_settings_window_open_click: AtomicCell<Option<PhysicalPosition<f64>>>,
|
||||
|
||||
/// If the settings window is currently open
|
||||
settings_window_open: Mutex<bool>,
|
||||
|
||||
/// New panel parameters
|
||||
new_panel_parameters: Mutex<(Rect<u32>, f32, f32)>,
|
||||
|
||||
/// Profiles
|
||||
_profiles: Vec<PanelsProfile>,
|
||||
queued_settings_window_open_click: &'a AtomicCell<Option<PhysicalPosition<f64>>>,
|
||||
}
|
||||
|
||||
/// Application state
|
||||
///
|
||||
/// Stores all of the application state
|
||||
#[derive(Debug)]
|
||||
pub struct App {
|
||||
/// Event loop
|
||||
event_loop: EventLoop<!>,
|
||||
|
||||
/// Inner
|
||||
inner: Inner,
|
||||
}
|
||||
|
||||
impl App {
|
||||
/// Creates a new app
|
||||
#[allow(clippy::future_not_send)] // Unfortunately we can't do much about it, we must build the window in the main thread
|
||||
pub async fn new(args: Args) -> Result<Self, anyhow::Error> {
|
||||
// Build the window
|
||||
let (event_loop, window) = self::create_window(&args)?;
|
||||
|
||||
// Create the wgpu interface
|
||||
let wgpu = Wgpu::new(window).await.context("Unable to create renderer")?;
|
||||
|
||||
// Create the paths channel
|
||||
let (paths_distributer, paths_rx) = paths::new(args.images_dir);
|
||||
|
||||
// Create the image loader
|
||||
let image_loader = ImageLoader::new(paths_rx).context("Unable to create image loader")?;
|
||||
|
||||
// Create all panels
|
||||
let panels = args
|
||||
.panel_geometries
|
||||
.iter()
|
||||
.map(|&geometry| Panel::new(geometry, PanelState::Empty, args.image_duration, args.fade_point))
|
||||
.collect::<Vec<_>>();
|
||||
let panels = Mutex::new(panels);
|
||||
|
||||
// Create the panels renderer
|
||||
let panels_renderer = PanelsRenderer::new(wgpu.device(), wgpu.surface_texture_format())
|
||||
.await
|
||||
.context("Unable to create panels renderer")?;
|
||||
|
||||
// Create egui
|
||||
let egui = Egui::new(window, &wgpu).context("Unable to create egui state")?;
|
||||
|
||||
// Read all profiles
|
||||
let profiles = match util::parse_json_from_file("zsw_profiles.json") {
|
||||
Ok(profiles) => {
|
||||
log::info!("Loaded profiles {profiles:#?}");
|
||||
profiles
|
||||
},
|
||||
Err(err) => {
|
||||
log::info!("Unable to load profiles: {err:?}");
|
||||
vec![]
|
||||
},
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
event_loop,
|
||||
inner: Inner {
|
||||
window,
|
||||
wgpu,
|
||||
paths_distributer,
|
||||
image_loader,
|
||||
panels,
|
||||
panels_renderer,
|
||||
egui,
|
||||
queued_settings_window_open_click: AtomicCell::new(None),
|
||||
settings_window_open: Mutex::new(false),
|
||||
// TODO: Copy existing panel, or surface
|
||||
new_panel_parameters: Mutex::new((
|
||||
Rect {
|
||||
pos: Point2::new(0, 0),
|
||||
size: Vector2::new(0, 0),
|
||||
},
|
||||
15.0,
|
||||
0.85,
|
||||
)),
|
||||
_profiles: profiles,
|
||||
},
|
||||
})
|
||||
impl<'a> EventHandler<'a> {
|
||||
/// Creates the event handler
|
||||
pub fn new(
|
||||
wgpu: &'a Wgpu, egui: &'a Egui,
|
||||
queued_settings_window_open_click: &'a AtomicCell<Option<PhysicalPosition<f64>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
wgpu,
|
||||
egui,
|
||||
cursor_pos: None,
|
||||
queued_settings_window_open_click,
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs the app 'till completion
|
||||
pub fn run(mut self) -> Result<(), anyhow::Error> {
|
||||
// Start all threads and then wait in the main thread for events
|
||||
// TODO: Not ignore errors here, although given how `thread::scope` works
|
||||
// it's somewhat hard to do so
|
||||
crossbeam::thread::scope(|s| {
|
||||
// Spawn the path distributer thread
|
||||
let _path_distributer = util::spawn_scoped(s, "Path distributer", || self.inner.paths_distributer.run())?;
|
||||
|
||||
// Spawn all image loaders
|
||||
let loader_threads = thread::available_parallelism().map_or(1, NonZeroUsize::get);
|
||||
let _image_loaders =
|
||||
util::spawn_scoped_multiple(s, "Image loader", loader_threads, || || self.inner.image_loader.run())?;
|
||||
|
||||
// Spawn the renderer thread
|
||||
let _renderer_thread = util::spawn_scoped(s, "Renderer", || Self::run_renderer(&self.inner))?;
|
||||
|
||||
// Run event loop in this thread until we quit
|
||||
let mut cursor_pos = PhysicalPosition::new(0.0, 0.0);
|
||||
self.event_loop.run_return(|event, _, control_flow| {
|
||||
Self::event_handler(&self.inner, event, control_flow, &mut cursor_pos);
|
||||
});
|
||||
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.expect("Unable to start all threads")
|
||||
.expect("Unable to run all threads 'till completion");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Event handler
|
||||
fn event_handler(
|
||||
inner: &Inner, event: Event<!>, control_flow: &mut EventLoopControlFlow, cursor_pos: &mut PhysicalPosition<f64>,
|
||||
) {
|
||||
/// Handles an event
|
||||
pub fn handle_event(&mut self, event: Event<!>, control_flow: &mut EventLoopControlFlow) {
|
||||
// Update egui
|
||||
inner.egui.platform().lock().handle_event(&event);
|
||||
self.egui.platform().lock().handle_event(&event);
|
||||
|
||||
// Set control for to wait for next event, since we're not doing
|
||||
// anything else on the main thread
|
||||
@ -205,10 +165,10 @@ impl App {
|
||||
},
|
||||
|
||||
// If we resized, queue a resize on wgpu
|
||||
WindowEvent::Resized(size) => inner.wgpu.resize(size),
|
||||
WindowEvent::Resized(size) => self.wgpu.resize(size),
|
||||
|
||||
// On move, update the cursor position
|
||||
WindowEvent::CursorMoved { position, .. } => *cursor_pos = position,
|
||||
WindowEvent::CursorMoved { position, .. } => self.cursor_pos = Some(position),
|
||||
|
||||
// If right clicked, queue a click
|
||||
WindowEvent::MouseInput {
|
||||
@ -216,16 +176,78 @@ impl App {
|
||||
button: winit::event::MouseButton::Right,
|
||||
..
|
||||
} => {
|
||||
inner.queued_settings_window_open_click.store(Some(*cursor_pos));
|
||||
self.queued_settings_window_open_click.store(self.cursor_pos);
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Renderer
|
||||
pub struct Renderer<'a> {
|
||||
/// Window
|
||||
window: &'a Window,
|
||||
|
||||
/// Wgpu
|
||||
wgpu: &'a Wgpu,
|
||||
|
||||
/// Path distributer
|
||||
paths_distributer: &'a paths::Distributer,
|
||||
|
||||
/// Image loader
|
||||
image_loader: &'a ImageLoader,
|
||||
|
||||
/// Panels renderer
|
||||
panels_renderer: &'a PanelsRenderer,
|
||||
|
||||
/// Panels
|
||||
panels: &'a Mutex<Vec<Panel>>,
|
||||
|
||||
/// Egui
|
||||
egui: &'a Egui,
|
||||
|
||||
/// Queued settings window open click
|
||||
queued_settings_window_open_click: &'a AtomicCell<Option<PhysicalPosition<f64>>>,
|
||||
|
||||
/// If the settings window is currently open
|
||||
settings_window_open: bool,
|
||||
|
||||
/// New panel parameters
|
||||
new_panel_parameters: (Rect<u32>, f32, f32),
|
||||
}
|
||||
|
||||
impl<'a> Renderer<'a> {
|
||||
/// Creates a new renderer
|
||||
pub fn new(
|
||||
window: &'a Window, wgpu: &'a Wgpu, paths_distributer: &'a paths::Distributer, image_loader: &'a ImageLoader,
|
||||
panels_renderer: &'a PanelsRenderer, panels: &'a Mutex<Vec<Panel>>, egui: &'a Egui,
|
||||
queued_settings_window_open_click: &'a AtomicCell<Option<PhysicalPosition<f64>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
window,
|
||||
wgpu,
|
||||
paths_distributer,
|
||||
image_loader,
|
||||
panels_renderer,
|
||||
panels,
|
||||
egui,
|
||||
queued_settings_window_open_click,
|
||||
settings_window_open: false,
|
||||
new_panel_parameters: (
|
||||
Rect {
|
||||
pos: Point2::new(0, 0),
|
||||
size: Vector2::new(0, 0),
|
||||
},
|
||||
15.0,
|
||||
0.85,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs the renderer
|
||||
fn run_renderer(inner: &Inner) {
|
||||
fn run(&mut self) {
|
||||
// Duration we're sleep
|
||||
let sleep_duration = Duration::from_secs_f32(1.0 / 60.0);
|
||||
|
||||
@ -234,14 +256,14 @@ impl App {
|
||||
// Note: The update is only useful for displaying, so there's no use
|
||||
// in running it in another thread.
|
||||
// Especially given that `update` doesn't block.
|
||||
let (res, frame_duration) = crate::util::measure(|| Self::update(inner));
|
||||
let (res, frame_duration) = crate::util::measure(|| self.update());
|
||||
match res {
|
||||
Ok(()) => log::trace!(target: "zsw::perf", "Took {frame_duration:?} to update"),
|
||||
Err(err) => log::warn!("Unable to update: {err:?}"),
|
||||
};
|
||||
|
||||
// Render
|
||||
let (res, frame_duration) = crate::util::measure(|| Self::render(inner));
|
||||
let (res, frame_duration) = crate::util::measure(|| self.render());
|
||||
match res {
|
||||
Ok(()) => log::trace!(target: "zsw::perf", "Took {frame_duration:?} to render"),
|
||||
Err(err) => log::warn!("Unable to render: {err:?}"),
|
||||
@ -255,10 +277,10 @@ impl App {
|
||||
}
|
||||
|
||||
/// Updates all panels
|
||||
fn update(inner: &Inner) -> Result<(), anyhow::Error> {
|
||||
let mut panels = inner.panels.lock();
|
||||
fn update(&mut self) -> Result<(), anyhow::Error> {
|
||||
let mut panels = self.panels.lock();
|
||||
for panel in &mut *panels {
|
||||
if let Err(err) = panel.update(&inner.wgpu, &inner.panels_renderer, &inner.image_loader) {
|
||||
if let Err(err) = panel.update(self.wgpu, self.panels_renderer, self.image_loader) {
|
||||
log::warn!("Unable to update panel: {err:?}");
|
||||
}
|
||||
}
|
||||
@ -267,25 +289,24 @@ impl App {
|
||||
}
|
||||
|
||||
/// Renders
|
||||
fn render(inner: &Inner) -> Result<(), anyhow::Error> {
|
||||
fn render(&mut self) -> Result<(), anyhow::Error> {
|
||||
// Draw egui
|
||||
// TODO: When this is moved to it's own thread, regardless of issues with
|
||||
// synchronizing the platform, we should synchronize the drawing to ensure
|
||||
// we don't draw twice without displaying, as the first draw would never be
|
||||
// visible to the user.
|
||||
let paint_jobs = inner
|
||||
let paint_jobs = self
|
||||
.egui
|
||||
.draw(inner.window, |ctx, frame| {
|
||||
Self::draw_egui(inner, ctx, frame, inner.wgpu.surface_size())
|
||||
.draw(self.window, |ctx, frame| {
|
||||
self.draw_egui(ctx, frame, self.wgpu.surface_size())
|
||||
})
|
||||
.context("Unable to draw egui")?;
|
||||
|
||||
inner.wgpu.render(|encoder, surface_view, surface_size| {
|
||||
self.wgpu.render(|encoder, surface_view, surface_size| {
|
||||
// Render the panels
|
||||
let mut panels = inner.panels.lock();
|
||||
inner
|
||||
.panels_renderer
|
||||
.render(&mut *panels, inner.wgpu.queue(), encoder, surface_view, surface_size)
|
||||
let mut panels = self.panels.lock();
|
||||
self.panels_renderer
|
||||
.render(&mut *panels, self.wgpu.queue(), encoder, surface_view, surface_size)
|
||||
.context("Unable to render panels")?;
|
||||
|
||||
// Render egui
|
||||
@ -293,15 +314,15 @@ impl App {
|
||||
let screen_descriptor = egui_wgpu_backend::ScreenDescriptor {
|
||||
physical_width: surface_size.width,
|
||||
physical_height: surface_size.height,
|
||||
scale_factor: inner.window.scale_factor() as f32,
|
||||
scale_factor: self.window.scale_factor() as f32,
|
||||
};
|
||||
let device = inner.wgpu.device();
|
||||
let queue = inner.wgpu.queue();
|
||||
let mut egui_render_pass = inner.egui.render_pass().lock();
|
||||
let device = self.wgpu.device();
|
||||
let queue = self.wgpu.queue();
|
||||
let mut egui_render_pass = self.egui.render_pass().lock();
|
||||
|
||||
// TODO: Check if it's fine to get the platform here without synchronizing
|
||||
// with the drawing.
|
||||
let egui_platform = inner.egui.platform().lock();
|
||||
let egui_platform = self.egui.platform().lock();
|
||||
egui_render_pass.update_texture(device, queue, &egui_platform.context().font_image());
|
||||
egui_render_pass.update_user_textures(device, queue);
|
||||
egui_render_pass.update_buffers(device, queue, &paint_jobs, &screen_descriptor);
|
||||
@ -316,27 +337,25 @@ impl App {
|
||||
/// Draws egui app
|
||||
#[allow(unused_results)] // `egui` returns a response on every operation, but we don't use them
|
||||
fn draw_egui(
|
||||
inner: &Inner, ctx: &egui::CtxRef, _frame: &epi::Frame, surface_size: PhysicalSize<u32>,
|
||||
&mut self, ctx: &egui::CtxRef, _frame: &epi::Frame, surface_size: PhysicalSize<u32>,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let mut settings_window_open = inner.settings_window_open.lock();
|
||||
|
||||
// Create the base settings window
|
||||
let mut settings_window = egui::Window::new("Settings");
|
||||
|
||||
// If we have any queued click, summon the window there
|
||||
if let Some(cursor_pos) = inner.queued_settings_window_open_click.take() {
|
||||
if let Some(cursor_pos) = self.queued_settings_window_open_click.take() {
|
||||
// Adjust cursor pos to account for the scale factor
|
||||
let scale_factor = inner.window.scale_factor();
|
||||
let scale_factor = self.window.scale_factor();
|
||||
let cursor_pos = cursor_pos.to_logical(scale_factor);
|
||||
|
||||
// Then set the current position and that we're open
|
||||
settings_window = settings_window.current_pos(egui::pos2(cursor_pos.x, cursor_pos.y));
|
||||
*settings_window_open = true;
|
||||
self.settings_window_open = true;
|
||||
}
|
||||
|
||||
// Then render it
|
||||
settings_window.open(&mut *settings_window_open).show(ctx, |ui| {
|
||||
let mut panels = inner.panels.lock();
|
||||
settings_window.open(&mut self.settings_window_open).show(ctx, |ui| {
|
||||
let mut panels = self.panels.lock();
|
||||
for (idx, panel) in panels.iter_mut().enumerate() {
|
||||
ui.collapsing(format!("Panel {idx}"), |ui| {
|
||||
// TODO: Make a macro to make this more readable
|
||||
@ -371,8 +390,7 @@ impl App {
|
||||
});
|
||||
}
|
||||
ui.collapsing("Add panel", |ui| {
|
||||
let mut new_panel_parameters = inner.new_panel_parameters.lock();
|
||||
let (geometry, image_duration, fade_point) = &mut *new_panel_parameters;
|
||||
let (geometry, image_duration, fade_point) = &mut self.new_panel_parameters;
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Geometry");
|
||||
@ -401,7 +419,7 @@ impl App {
|
||||
mem::drop(panels);
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
let cur_root_path = inner.paths_distributer.root_path();
|
||||
let cur_root_path = self.paths_distributer.root_path();
|
||||
|
||||
ui.label("Root path");
|
||||
ui.label(cur_root_path.display().to_string());
|
||||
@ -413,7 +431,7 @@ impl App {
|
||||
Ok(file_dialog) => {
|
||||
if let Some(root_path) = file_dialog {
|
||||
// Set the root path
|
||||
inner.paths_distributer.set_root_path(root_path);
|
||||
self.paths_distributer.set_root_path(root_path);
|
||||
|
||||
// TODO: Reset all existing images and paths loaded from the
|
||||
// old path distributer, maybe?
|
||||
@ -429,6 +447,7 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
/// Draws a geometry rectangle
|
||||
fn draw_rect(ui: &mut egui::Ui, geometry: &mut Rect<u32>, max_size: PhysicalSize<u32>) -> egui::Response {
|
||||
// Calculate the limits
|
||||
// TODO: If two values are changed at the same time, during 1 frame it's
|
||||
|
||||
22
src/main.rs
22
src/main.rs
@ -71,7 +71,6 @@ mod wgpu;
|
||||
|
||||
// Exports
|
||||
pub use self::{
|
||||
app::App,
|
||||
args::Args,
|
||||
egui::Egui,
|
||||
img::ImageLoader,
|
||||
@ -82,7 +81,6 @@ pub use self::{
|
||||
|
||||
// Imports
|
||||
use anyhow::Context;
|
||||
use pollster::FutureExt;
|
||||
|
||||
fn main() -> Result<(), anyhow::Error> {
|
||||
// Initialize logger
|
||||
@ -95,11 +93,21 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
let args = args::get().context("Unable to retrieve arguments")?;
|
||||
log::debug!("Arguments: {args:#?}");
|
||||
|
||||
// Create the app
|
||||
let app = App::new(args).block_on().context("Unable to initialize app")?;
|
||||
|
||||
// Then run it
|
||||
app.run().context("Unable to run app")?;
|
||||
// Run the app and restart if we get an error (up to 5 errors)
|
||||
let mut errors = 0;
|
||||
while errors < 5 {
|
||||
match app::run(&args) {
|
||||
Ok(()) => {
|
||||
log::info!("Application finished");
|
||||
break;
|
||||
},
|
||||
Err(err) => {
|
||||
log::error!("Application encountered fatal error: {err:?}");
|
||||
errors += 1;
|
||||
continue;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Wgpu seems to segfault during a global destructor.
|
||||
// Not sure what's causing this, but happens after "Destroying 2 command encoders"
|
||||
|
||||
@ -50,9 +50,7 @@ pub struct PanelsRenderer {
|
||||
|
||||
impl PanelsRenderer {
|
||||
/// Creates a new renderer for the panels
|
||||
pub async fn new(
|
||||
device: &wgpu::Device, surface_texture_format: wgpu::TextureFormat,
|
||||
) -> Result<Self, anyhow::Error> {
|
||||
pub fn new(device: &wgpu::Device, surface_texture_format: wgpu::TextureFormat) -> Result<Self, anyhow::Error> {
|
||||
// Create the index buffer
|
||||
let indices = self::create_indices(device);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user