gpui_linux: Force a full render on X11 Expose after GPU recovery (#64570)
# Objective Zed on X11 could crash with `index out of bounds: the len is 0 but the index is 0` in `WgpuAtlas::get_texture_info` right after recovering from a lost GPU device (Sentry ZED-BVQ, Linear FR-488). ## Solution When the device is lost, `X11Window::draw` recreates the renderer, which clears the sprite atlas, and sets `force_render_after_recovery` so the next frame rebuilds the scene instead of replaying the cached one. Only the periodic refresh timer consumed that flag though. An `Expose` event handled in the same event loop turn (a compositor repainting after the same GPU reset, for example) requested a presentation-only frame with `force_render: false`, so GPUI presented the previously rendered scene, whose sprites still pointed at atlas textures that no longer exist. We now consume the flag in `X11WindowStatePtr::refresh`, so every frame request, Expose or timer, forces a full render after recovery. The field is private now so a caller can't bypass it again. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed a crash on Linux (X11) that could occur when a window was redrawn immediately after recovering from a lost GPU device. - [GPUI] Fixed a crash on Linux (X11) when an `Expose` event presented a cached frame immediately after GPU device recovery.
This commit is contained in:
1 parent
1df382558f
commit
9d801eec7a
2 files changed
+7
-6
No files matched your search
@@ -2007,14 +2007,11 @@ impl X11ClientState {
|
||||
let mut state = client.0.borrow_mut();
|
||||
let xcb_connection = state.xcb_connection.clone();
|
||||
if let Some(window) = state.windows.get_mut(&x_window) {
|
||||
let force_render = std::mem::take(
|
||||
&mut window.window.state.borrow_mut().force_render_after_recovery,
|
||||
);
|
||||
let window = window.window.clone();
|
||||
drop(state);
|
||||
window.refresh(RequestFrameOptions {
|
||||
require_presentation: false,
|
||||
force_render,
|
||||
force_render: false,
|
||||
});
|
||||
}
|
||||
xcb_connection
|
||||
|
||||
@@ -283,7 +283,7 @@ pub struct X11WindowState {
|
||||
/// `VisibilityNotify`; this is the last value it reported.
|
||||
visibility: WindowVisibility,
|
||||
hovered: bool,
|
||||
pub(crate) force_render_after_recovery: bool,
|
||||
force_render_after_recovery: bool,
|
||||
fullscreen: bool,
|
||||
client_side_decorations_supported: bool,
|
||||
decorations: WindowDecorations,
|
||||
@@ -1181,9 +1181,13 @@ impl X11WindowStatePtr {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn refresh(&self, request_frame_options: RequestFrameOptions) {
|
||||
pub fn refresh(&self, mut request_frame_options: RequestFrameOptions) {
|
||||
let callback = self.callbacks.borrow_mut().request_frame.take();
|
||||
if let Some(mut fun) = callback {
|
||||
// Expose events can present a frame before the refresh timer runs,
|
||||
// so every frame request must rebuild stale atlas references after recovery.
|
||||
request_frame_options.force_render |=
|
||||
std::mem::take(&mut self.state.borrow_mut().force_render_after_recovery);
|
||||
fun(request_frame_options);
|
||||
self.callbacks.borrow_mut().request_frame = Some(fun);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user