aboutsummaryrefslogtreecommitdiff
path: root/alacritty
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 /alacritty
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.
Diffstat (limited to 'alacritty')
-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
4 files changed, 122 insertions, 23 deletions
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),