aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKirill Chibisov <contact@kchibisov.com>2025-01-11 08:47:32 +0300
committerGitHub <noreply@github.com>2025-01-11 08:47:32 +0300
commitcd884c984bbd6a7c65965681083da61e091ff7e5 (patch)
tree013ee3133480b90d3ca8455962997b24574e4659
parent28910e3adc9d48edc4f43008d987eecd869ded31 (diff)
downloadr-alacritty-cd884c984bbd6a7c65965681083da61e091ff7e5.tar.gz
r-alacritty-cd884c984bbd6a7c65965681083da61e091ff7e5.tar.bz2
r-alacritty-cd884c984bbd6a7c65965681083da61e091ff7e5.zip
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.
-rw-r--r--CHANGELOG.md1
-rw-r--r--alacritty/build.rs1
-rw-r--r--alacritty/src/display/mod.rs61
-rw-r--r--alacritty/src/renderer/mod.rs61
-rw-r--r--alacritty/src/renderer/platform.rs22
5 files changed, 123 insertions, 23 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6ab157db..c8666916 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/alacritty/build.rs b/alacritty/build.rs
index 7c33040e..0856e3a5 100644
--- a/alacritty/build.rs
+++ b/alacritty/build.rs
@@ -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)
diff --git a/alacritty/src/display/mod.rs b/alacritty/src/display/mod.rs
index 6c685a2a..9f11ccf6 100644
--- a/alacritty/src/display/mod.rs
+++ b/alacritty/src/display/mod.rs
@@ -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) {
diff --git a/alacritty/src/renderer/mod.rs b/alacritty/src/renderer/mod.rs
index 3362db42..92998e58 100644
--- a/alacritty/src/renderer/mod.rs
+++ b/alacritty/src/renderer/mod.rs
@@ -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);
}
diff --git a/alacritty/src/renderer/platform.rs b/alacritty/src/renderer/platform.rs
index 87ed29c2..3b2e2fce 100644
--- a/alacritty/src/renderer/platform.rs
+++ b/alacritty/src/renderer/platform.rs
@@ -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),