Desktop: Restart without UI acceleration when it fails on Linux (#3668)
* Desktop: Restart without UI Acceleration when it fails on Linux * fix spelling
This commit is contained in:
parent
b4e9d7b9eb
commit
5e61d59e50
|
|
@ -1,17 +1,17 @@
|
|||
use rand::Rng;
|
||||
use rfd::AsyncFileDialog;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::mpsc::{Receiver, Sender, SyncSender};
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use winit::event::{ButtonSource, ElementState, MouseButton, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, ControlFlow};
|
||||
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
|
||||
use winit::window::WindowId;
|
||||
|
||||
use crate::cef;
|
||||
use crate::cli::Cli;
|
||||
use crate::consts::CEF_MESSAGE_LOOP_MAX_ITERATIONS;
|
||||
use crate::event::{AppEvent, AppEventScheduler};
|
||||
use crate::persist::PersistentData;
|
||||
|
|
@ -37,13 +37,14 @@ pub(crate) struct App {
|
|||
cef_context: Box<dyn cef::CefContext>,
|
||||
cef_schedule: Option<Instant>,
|
||||
cef_view_info_sender: Sender<cef::ViewInfoUpdate>,
|
||||
last_ui_update: Instant,
|
||||
avg_frame_time: f32,
|
||||
cef_init_successful: bool,
|
||||
start_render_sender: SyncSender<()>,
|
||||
web_communication_initialized: bool,
|
||||
web_communication_startup_buffer: Vec<Vec<u8>>,
|
||||
persistent_data: PersistentData,
|
||||
launch_documents: Vec<PathBuf>,
|
||||
cli: Cli,
|
||||
startup_time: Option<Instant>,
|
||||
exit_reason: ExitReason,
|
||||
}
|
||||
|
||||
impl App {
|
||||
|
|
@ -57,12 +58,12 @@ impl App {
|
|||
wgpu_context: WgpuContext,
|
||||
app_event_receiver: Receiver<AppEvent>,
|
||||
app_event_scheduler: AppEventScheduler,
|
||||
launch_documents: Vec<PathBuf>,
|
||||
cli: Cli,
|
||||
) -> Self {
|
||||
let ctrlc_app_event_scheduler = app_event_scheduler.clone();
|
||||
ctrlc::set_handler(move || {
|
||||
tracing::info!("Termination signal received, exiting...");
|
||||
ctrlc_app_event_scheduler.schedule(AppEvent::CloseWindow);
|
||||
ctrlc_app_event_scheduler.schedule(AppEvent::Exit);
|
||||
})
|
||||
.expect("Error setting Ctrl-C handler");
|
||||
|
||||
|
|
@ -95,19 +96,32 @@ impl App {
|
|||
app_event_receiver,
|
||||
app_event_scheduler,
|
||||
desktop_wrapper,
|
||||
last_ui_update: Instant::now(),
|
||||
cef_context,
|
||||
cef_schedule: Some(Instant::now()),
|
||||
cef_view_info_sender,
|
||||
avg_frame_time: 0.,
|
||||
cef_init_successful: false,
|
||||
start_render_sender,
|
||||
web_communication_initialized: false,
|
||||
web_communication_startup_buffer: Vec::new(),
|
||||
persistent_data,
|
||||
launch_documents,
|
||||
cli,
|
||||
exit_reason: ExitReason::Shutdown,
|
||||
startup_time: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn run(mut self, event_loop: EventLoop) -> ExitReason {
|
||||
event_loop.run_app(&mut self).unwrap();
|
||||
self.exit_reason
|
||||
}
|
||||
|
||||
fn exit(&mut self, reason: Option<ExitReason>) {
|
||||
if let Some(reason) = reason {
|
||||
self.exit_reason = reason;
|
||||
}
|
||||
self.app_event_scheduler.schedule(AppEvent::Exit);
|
||||
}
|
||||
|
||||
fn resize(&mut self) {
|
||||
let Some(window) = &self.window else {
|
||||
tracing::error!("Resize failed due to missing window");
|
||||
|
|
@ -302,11 +316,11 @@ impl App {
|
|||
}
|
||||
}
|
||||
DesktopFrontendMessage::OpenLaunchDocuments => {
|
||||
if self.launch_documents.is_empty() {
|
||||
if self.cli.files.is_empty() {
|
||||
return;
|
||||
}
|
||||
let app_event_scheduler = self.app_event_scheduler.clone();
|
||||
let launch_documents = std::mem::take(&mut self.launch_documents);
|
||||
let launch_documents = std::mem::take(&mut self.cli.files);
|
||||
let _ = thread::spawn(move || {
|
||||
for path in launch_documents {
|
||||
tracing::info!("Opening file from command line: {}", path.display());
|
||||
|
|
@ -343,7 +357,7 @@ impl App {
|
|||
}
|
||||
}
|
||||
DesktopFrontendMessage::WindowClose => {
|
||||
self.app_event_scheduler.schedule(AppEvent::CloseWindow);
|
||||
self.app_event_scheduler.schedule(AppEvent::Exit);
|
||||
}
|
||||
DesktopFrontendMessage::WindowMinimize => {
|
||||
if let Some(window) = &self.window {
|
||||
|
|
@ -431,15 +445,13 @@ impl App {
|
|||
AppEvent::UiUpdate(texture) => {
|
||||
if let Some(render_state) = self.render_state.as_mut() {
|
||||
render_state.bind_ui_texture(texture);
|
||||
let elapsed = self.last_ui_update.elapsed().as_secs_f32();
|
||||
self.last_ui_update = Instant::now();
|
||||
if elapsed < 0.5 {
|
||||
self.avg_frame_time = (self.avg_frame_time * 3. + elapsed) / 4.;
|
||||
}
|
||||
}
|
||||
if let Some(window) = &self.window {
|
||||
window.request_redraw();
|
||||
}
|
||||
if !self.cef_init_successful {
|
||||
self.cef_init_successful = true;
|
||||
}
|
||||
}
|
||||
AppEvent::ScheduleBrowserWork(instant) => {
|
||||
if instant <= Instant::now() {
|
||||
|
|
@ -453,9 +465,7 @@ impl App {
|
|||
window.set_cursor(event_loop, cursor);
|
||||
}
|
||||
}
|
||||
AppEvent::CloseWindow => {
|
||||
// TODO: Implement graceful shutdown
|
||||
|
||||
AppEvent::Exit => {
|
||||
tracing::info!("Exiting main event loop");
|
||||
event_loop.exit();
|
||||
}
|
||||
|
|
@ -481,6 +491,8 @@ impl ApplicationHandler for App {
|
|||
self.resize();
|
||||
|
||||
self.desktop_wrapper.init(self.wgpu_context.clone());
|
||||
|
||||
self.startup_time = Some(Instant::now());
|
||||
}
|
||||
|
||||
fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
|
|
@ -489,7 +501,7 @@ impl ApplicationHandler for App {
|
|||
}
|
||||
}
|
||||
|
||||
fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {
|
||||
fn window_event(&mut self, _event_loop: &dyn ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {
|
||||
// Handle pointer lock release
|
||||
if let Some(pointer_lock_position) = self.pointer_lock_position
|
||||
&& let WindowEvent::PointerButton {
|
||||
|
|
@ -514,7 +526,7 @@ impl ApplicationHandler for App {
|
|||
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
self.app_event_scheduler.schedule(AppEvent::CloseWindow);
|
||||
self.app_event_scheduler.schedule(AppEvent::Exit);
|
||||
}
|
||||
WindowEvent::SurfaceResized(_) | WindowEvent::ScaleFactorChanged { .. } => {
|
||||
self.resize();
|
||||
|
|
@ -539,12 +551,22 @@ impl ApplicationHandler for App {
|
|||
}
|
||||
Err(RenderError::SurfaceError(wgpu::SurfaceError::OutOfMemory)) => {
|
||||
tracing::error!("GPU out of memory");
|
||||
event_loop.exit();
|
||||
self.exit(None);
|
||||
}
|
||||
Err(RenderError::SurfaceError(e)) => tracing::error!("Render error: {:?}", e),
|
||||
}
|
||||
let _ = self.start_render_sender.try_send(());
|
||||
}
|
||||
|
||||
if !self.cef_init_successful
|
||||
&& !self.cli.disable_ui_acceleration
|
||||
&& self.web_communication_initialized
|
||||
&& let Some(startup_time) = self.startup_time
|
||||
&& startup_time.elapsed() > Duration::from_secs(3)
|
||||
{
|
||||
tracing::error!("UI acceleration not working, exiting.");
|
||||
self.exit(Some(ExitReason::UiAccelerationFailure));
|
||||
}
|
||||
}
|
||||
WindowEvent::DragDropped { paths, .. } => {
|
||||
for path in paths {
|
||||
|
|
@ -629,3 +651,8 @@ impl ApplicationHandler for App {
|
|||
event_loop.set_control_flow(ControlFlow::WaitUntil(wait_until));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum ExitReason {
|
||||
Shutdown,
|
||||
UiAccelerationFailure,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ pub(crate) enum AppEvent {
|
|||
WebCommunicationInitialized,
|
||||
DesktopWrapperMessage(DesktopWrapperMessage),
|
||||
NodeGraphExecutionResult(NodeGraphExecutionResult),
|
||||
CloseWindow,
|
||||
Exit,
|
||||
#[cfg(target_os = "macos")]
|
||||
MenuEvent {
|
||||
id: String,
|
||||
|
|
|
|||
|
|
@ -77,6 +77,10 @@ pub fn start() {
|
|||
|
||||
let (cef_view_info_sender, cef_view_info_receiver) = std::sync::mpsc::channel();
|
||||
|
||||
if cli.disable_ui_acceleration {
|
||||
println!("UI acceleration is disabled");
|
||||
}
|
||||
|
||||
let cef_handler = cef::CefHandler::new(wgpu_context.clone(), app_event_scheduler.clone(), cef_view_info_receiver);
|
||||
let cef_context = match cef_context_builder.initialize(cef_handler, cli.disable_ui_acceleration) {
|
||||
Ok(context) => {
|
||||
|
|
@ -101,13 +105,25 @@ pub fn start() {
|
|||
}
|
||||
};
|
||||
|
||||
let mut app = App::new(Box::new(cef_context), cef_view_info_sender, wgpu_context, app_event_receiver, app_event_scheduler, cli.files);
|
||||
let app = App::new(Box::new(cef_context), cef_view_info_sender, wgpu_context, app_event_receiver, app_event_scheduler, cli);
|
||||
|
||||
event_loop.run_app(&mut app).unwrap();
|
||||
let exit_reason = app.run(event_loop);
|
||||
|
||||
// Explicitly drop the instance lock
|
||||
drop(lock);
|
||||
|
||||
match exit_reason {
|
||||
#[cfg(target_os = "linux")]
|
||||
app::ExitReason::UiAccelerationFailure => {
|
||||
use std::os::unix::process::CommandExt;
|
||||
|
||||
tracing::error!("Restarting application without UI acceleration");
|
||||
let _ = std::process::Command::new(std::env::current_exe().unwrap()).arg("--disable-ui-acceleration").exec();
|
||||
tracing::error!("Failed to restart application");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Workaround for a Windows-specific exception that occurs when `app` is dropped.
|
||||
// The issue causes the window to hang for a few seconds before closing.
|
||||
// Appears to be related to CEF object destruction order.
|
||||
|
|
|
|||
Loading…
Reference in New Issue