1
0
Fork 0
mirror of https://github.com/alacritty/alacritty.git synced 2025-04-07 17:43:03 -04:00

Try to recover from GPU resets

Use context robustness to get notified about GPU resets
and try to recover from them by rebuilding the rendering
pipeline.
This commit is contained in:
Kirill Chibisov 2025-01-11 08:47:32 +03:00 committed by GitHub
parent 28910e3adc
commit cd884c984b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 123 additions and 23 deletions

View file

@ -35,6 +35,7 @@ Notable changes to the `alacritty_terminal` crate are documented in its
- Report of Enter/Tab/Backspace in kitty keyboard's report event types mode
- Crash when pressing certain modifier keys on macOS 15+
- Cut off wide characters in preedit string
- Crash when OpenGL context resets
## 0.14.0

View file

@ -17,6 +17,7 @@ fn main() {
Registry::new(Api::Gl, (3, 3), Profile::Core, Fallbacks::All, [
"GL_ARB_blend_func_extended",
"GL_KHR_robustness",
"GL_KHR_debug",
])
.write_bindings(GlobalGenerator, &mut file)

View file

@ -8,7 +8,10 @@ use std::num::NonZeroU32;
use std::ops::{Deref, DerefMut};
use std::time::{Duration, Instant};
use glutin::config::GetGlConfig;
use glutin::context::{NotCurrentContext, PossiblyCurrentContext};
use glutin::display::GetGlDisplay;
use glutin::error::ErrorKind;
use glutin::prelude::*;
use glutin::surface::{Surface, SwapInterval, WindowSurface};
@ -33,6 +36,7 @@ use alacritty_terminal::term::{
};
use alacritty_terminal::vte::ansi::{CursorShape, NamedColor};
use crate::config::debug::RendererPreference;
use crate::config::font::Font;
use crate::config::window::Dimensions;
#[cfg(not(windows))]
@ -49,7 +53,7 @@ use crate::display::window::Window;
use crate::event::{Event, EventType, Mouse, SearchState};
use crate::message_bar::{MessageBuffer, MessageType};
use crate::renderer::rects::{RenderLine, RenderLines, RenderRect};
use crate::renderer::{self, GlyphCache, Renderer};
use crate::renderer::{self, platform, GlyphCache, Renderer};
use crate::scheduler::{Scheduler, TimerId, Topic};
use crate::string::{ShortenDirection, StrShortener};
@ -385,6 +389,7 @@ pub struct Display {
hint_mouse_point: Option<Point>,
renderer: ManuallyDrop<Renderer>,
renderer_preference: Option<RendererPreference>,
surface: ManuallyDrop<Surface<WindowSurface>>,
@ -421,7 +426,7 @@ impl Display {
}
// Create the GL surface to draw into.
let surface = renderer::platform::create_gl_surface(
let surface = platform::create_gl_surface(
&gl_context,
window.inner_size(),
window.raw_window_handle(),
@ -513,6 +518,7 @@ impl Display {
context: ManuallyDrop::new(Replaceable::new(context)),
visual_bell: VisualBell::from(&config.bell),
renderer: ManuallyDrop::new(renderer),
renderer_preference: config.debug.renderer,
surface: ManuallyDrop::new(surface),
colors: List::from(&config.colors),
frame_timer: FrameTimer::new(),
@ -552,10 +558,55 @@ impl Display {
}
}
pub fn make_current(&self) {
if !self.context.get().is_current() {
self.context.make_current(&self.surface).expect("failed to make context current")
pub fn make_current(&mut self) {
let is_current = self.context.get().is_current();
// Attempt to make the context current if it's not.
let context_loss = if is_current {
self.renderer.was_context_reset()
} else {
match self.context.make_current(&self.surface) {
Err(err) if err.error_kind() == ErrorKind::ContextLost => {
info!("Context lost for window {:?}", self.window.id());
true
},
_ => false,
}
};
if !context_loss {
return;
}
let gl_display = self.context.display();
let gl_config = self.context.config();
let raw_window_handle = Some(self.window.raw_window_handle());
let context = platform::create_gl_context(&gl_display, &gl_config, raw_window_handle)
.expect("failed to recreate context.");
// Drop the old context and renderer.
unsafe {
ManuallyDrop::drop(&mut self.renderer);
ManuallyDrop::drop(&mut self.context);
}
// Activate new context.
let context = context.treat_as_possibly_current();
self.context = ManuallyDrop::new(Replaceable::new(context));
self.context.make_current(&self.surface).expect("failed to reativate context after reset.");
// Recreate renderer.
let renderer = Renderer::new(&self.context, self.renderer_preference)
.expect("failed to recreate renderer after reset");
self.renderer = ManuallyDrop::new(renderer);
// Resize the renderer.
self.renderer.resize(&self.size_info);
self.reset_glyph_cache();
self.damage_tracker.frame().mark_fully_damaged();
debug!("Recovered window {:?} from gpu reset", self.window.id());
}
fn swap_buffers(&self) {

View file

@ -9,7 +9,7 @@ use ahash::RandomState;
use crossfont::Metrics;
use glutin::context::{ContextApi, GlContext, PossiblyCurrentContext};
use glutin::display::{GetGlDisplay, GlDisplay};
use log::{debug, error, info, warn, LevelFilter};
use log::{debug, info, LevelFilter};
use unicode_width::UnicodeWidthChar;
use alacritty_terminal::index::Point;
@ -97,6 +97,7 @@ enum TextRendererProvider {
pub struct Renderer {
text_renderer: TextRendererProvider,
rect_renderer: RectRenderer,
robustness: bool,
}
/// Wrapper around gl::GetString with error checking and reporting.
@ -144,6 +145,9 @@ impl Renderer {
info!("Running on {renderer}");
info!("OpenGL version {gl_version}, shader_version {shader_version}");
// Check if robustness is supported.
let robustness = Self::supports_robustness();
let is_gles_context = matches!(context.context_api(), ContextApi::Gles(_));
// Use the config option to enforce a particular renderer configuration.
@ -175,7 +179,7 @@ impl Renderer {
}
}
Ok(Self { text_renderer, rect_renderer })
Ok(Self { text_renderer, rect_renderer, robustness })
}
pub fn draw_cells<I: Iterator<Item = RenderableCell>>(
@ -281,6 +285,49 @@ impl Renderer {
}
}
/// Get the context reset status.
pub fn was_context_reset(&self) -> bool {
// If robustness is not supported, don't use its functions.
if !self.robustness {
return false;
}
let status = unsafe { gl::GetGraphicsResetStatus() };
if status == gl::NO_ERROR {
false
} else {
let reason = match status {
gl::GUILTY_CONTEXT_RESET_KHR => "guilty",
gl::INNOCENT_CONTEXT_RESET_KHR => "innocent",
gl::UNKNOWN_CONTEXT_RESET_KHR => "unknown",
_ => "invalid",
};
info!("GPU reset ({})", reason);
true
}
}
fn supports_robustness() -> bool {
let mut notification_strategy = 0;
if GlExtensions::contains("GL_KHR_robustness") {
unsafe {
gl::GetIntegerv(gl::RESET_NOTIFICATION_STRATEGY_KHR, &mut notification_strategy);
}
} else {
notification_strategy = gl::NO_RESET_NOTIFICATION_KHR as gl::types::GLint;
}
if notification_strategy == gl::LOSE_CONTEXT_ON_RESET_KHR as gl::types::GLint {
info!("GPU reset notifications are enabled");
true
} else {
info!("GPU reset notifications are disabled");
false
}
}
pub fn finish(&self) {
unsafe {
gl::Finish();
@ -349,7 +396,7 @@ impl GlExtensions {
extern "system" fn gl_debug_log(
_: gl::types::GLenum,
kind: gl::types::GLenum,
_: gl::types::GLenum,
_: gl::types::GLuint,
_: gl::types::GLenum,
_: gl::types::GLsizei,
@ -357,11 +404,5 @@ extern "system" fn gl_debug_log(
_: *mut std::os::raw::c_void,
) {
let msg = unsafe { CStr::from_ptr(msg).to_string_lossy() };
match kind {
gl::DEBUG_TYPE_ERROR | gl::DEBUG_TYPE_UNDEFINED_BEHAVIOR => {
error!("[gl_render] {}", msg)
},
gl::DEBUG_TYPE_DEPRECATED_BEHAVIOR => warn!("[gl_render] {}", msg),
_ => debug!("[gl_render] {}", msg),
}
debug!("[gl_render] {}", msg);
}

View file

@ -4,9 +4,9 @@ use std::num::NonZeroU32;
use glutin::config::{ColorBufferType, Config, ConfigTemplateBuilder, GetGlConfig};
use glutin::context::{
ContextApi, ContextAttributesBuilder, GlProfile, NotCurrentContext, Version,
ContextApi, ContextAttributesBuilder, GlProfile, NotCurrentContext, Robustness, Version,
};
use glutin::display::{Display, DisplayApiPreference, GetGlDisplay};
use glutin::display::{Display, DisplayApiPreference, DisplayFeatures, GetGlDisplay};
use glutin::error::Result as GlutinResult;
use glutin::prelude::*;
use glutin::surface::{Surface, SurfaceAttributesBuilder, WindowSurface};
@ -110,18 +110,24 @@ pub fn create_gl_context(
raw_window_handle: Option<RawWindowHandle>,
) -> GlutinResult<NotCurrentContext> {
let debug = log::max_level() >= LevelFilter::Debug;
let mut builder = ContextAttributesBuilder::new().with_debug(debug);
// Try to enable robustness.
if gl_display.supported_features().contains(DisplayFeatures::CONTEXT_ROBUSTNESS) {
builder = builder.with_robustness(Robustness::RobustLoseContextOnReset);
}
let mut profiles = [
ContextAttributesBuilder::new()
.with_debug(debug)
builder
.clone()
.with_context_api(ContextApi::OpenGl(Some(Version::new(3, 3))))
.build(raw_window_handle),
// Try gles before OpenGL 2.1 as it tends to be more stable.
ContextAttributesBuilder::new()
.with_debug(debug)
builder
.clone()
.with_context_api(ContextApi::Gles(Some(Version::new(2, 0))))
.build(raw_window_handle),
ContextAttributesBuilder::new()
.with_debug(debug)
builder
.with_profile(GlProfile::Compatibility)
.with_context_api(ContextApi::OpenGl(Some(Version::new(2, 1))))
.build(raw_window_handle),