Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle GPU resets #6530

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Crash with `OT-SVG` fonts on Linux/BSD
- Crash during text compose on old GNOME under Wayland
- Mouse cursor staying hidden after window regains focus on macOS Ventura
- OpenGL context reset not being handled

## 0.11.0

Expand Down
22 changes: 9 additions & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion alacritty/Cargo.toml
Expand Up @@ -29,7 +29,7 @@ fnv = "1"
serde = { version = "1", features = ["derive"] }
serde_yaml = "0.8"
serde_json = "1"
glutin = { version = "0.30.0", default-features = false, features = ["egl", "wgl"] }
glutin = { git = "https://github.com/rust-windowing/glutin", default-features = false, features = ["egl", "wgl"] }
winit = { version = "0.27.4", default-features = false, features = ["serde"] }
notify-debouncer-mini = { version = "0.2.1", default-features = false }
parking_lot = "0.12.0"
Expand Down
9 changes: 6 additions & 3 deletions alacritty/build.rs
Expand Up @@ -15,9 +15,12 @@ fn main() {
let dest = env::var("OUT_DIR").unwrap();
let mut file = File::create(Path::new(&dest).join("gl_bindings.rs")).unwrap();

Registry::new(Api::Gl, (3, 3), Profile::Core, Fallbacks::All, ["GL_ARB_blend_func_extended"])
.write_bindings(GlobalGenerator, &mut file)
.unwrap();
Registry::new(Api::Gl, (3, 3), Profile::Core, Fallbacks::All, [
"GL_ARB_blend_func_extended",
"GL_KHR_robustness",
])
.write_bindings(GlobalGenerator, &mut file)
.unwrap();

#[cfg(windows)]
embed_resource::compile("./windows/alacritty.rc");
Expand Down
58 changes: 52 additions & 6 deletions alacritty/src/display/mod.rs
Expand Up @@ -9,7 +9,10 @@ use std::ops::{Deref, DerefMut};
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
use std::sync::atomic::Ordering;

use glutin::config::GetGlConfig;
use glutin::context::{NotCurrentContext, PossiblyCurrentContext};
use glutin::display::GetGlDisplay;
use glutin::error::ErrorKind;
use glutin::prelude::*;
use glutin::surface::{Rect as DamageRect, Surface, SwapInterval, WindowSurface};

Expand Down Expand Up @@ -537,9 +540,52 @@ 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, handle_lost: bool) {
let is_current = self.context.get().is_current();
if is_current && !handle_lost {
return;
}

let mut was_context_reset = if is_current {
false
} else {
match self.context.make_current(&self.surface) {
Err(err) if err.error_kind() == ErrorKind::ContextLost => true,
_ => false,
}
};

if !was_context_reset {
was_context_reset |= self.renderer.was_context_reset();
}

if was_context_reset && handle_lost {
let gl_display = self.context.display();
let gl_config = self.context.config();
let raw_window_handle = Some(self.window.raw_window_handle());
let context =
renderer::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.");

let renderer =
Renderer::new(&self.context).expect("failed to recreate renderer after reset");
self.renderer = ManuallyDrop::new(renderer);

// Resize the renderer.
self.renderer.resize(&self.size_info);
}
}

Expand Down Expand Up @@ -672,7 +718,7 @@ impl Display {
}

// Ensure we're modifying the correct OpenGL context.
self.make_current();
self.make_current(true);

if renderer_update.clear_font_cache {
self.reset_glyph_cache();
Expand Down Expand Up @@ -778,7 +824,7 @@ impl Display {
drop(terminal);

// Make sure this window's OpenGL context is active.
self.make_current();
self.make_current(true);

self.renderer.clear(background_color, config.window_opacity());
let mut lines = RenderLines::new();
Expand Down Expand Up @@ -1390,7 +1436,7 @@ impl Drop for Display {
fn drop(&mut self) {
// Switch OpenGL context before dropping, otherwise objects (like programs) from other
// contexts might be deleted during droping renderer.
self.make_current();
self.make_current(false);
unsafe {
ManuallyDrop::drop(&mut self.renderer);
ManuallyDrop::drop(&mut self.context);
Expand Down
43 changes: 43 additions & 0 deletions alacritty/src/renderer/mod.rs
Expand Up @@ -16,6 +16,7 @@ use alacritty_terminal::term::color::Rgb;
use crate::display::content::RenderableCell;
use crate::display::SizeInfo;
use crate::gl;
use crate::gl::types::GLint;
use crate::renderer::rects::{RectRenderer, RenderRect};
use crate::renderer::shader::ShaderError;

Expand Down Expand Up @@ -106,6 +107,9 @@ impl Renderer {

info!("Running on {}", renderer);

// Log the GPU reset status.
Self::log_reset_notification_status();

let (text_renderer, rect_renderer) = if version.as_ref() >= "3.3" {
let text_renderer = TextRendererProvider::Glsl3(Glsl3Renderer::new()?);
let rect_renderer = RectRenderer::new(ShaderVersion::Glsl3)?;
Expand Down Expand Up @@ -208,6 +212,45 @@ impl Renderer {
}
}

/// Get the context reset status.
pub fn was_context_reset(&self) -> bool {
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
}
}

/// Log reset notifications.
fn log_reset_notification_status() -> bool {
let mut notification_strategy = 0;
if !GlExtensions::contains("GL_KHR_robustness") {
notification_strategy = gl::NO_RESET_NOTIFICATION_KHR as GLint;
} else {
unsafe {
gl::GetIntegerv(gl::RESET_NOTIFICATION_STRATEGY_KHR, &mut notification_strategy);
}
}

if notification_strategy == gl::LOSE_CONTEXT_ON_RESET_KHR as GLint {
info!("GPU reset notifications are enabled");
true
} else {
info!("GPU reset notifications are disabled");
false
}
}

#[cfg(not(any(target_os = "macos", windows)))]
pub fn finish(&self) {
unsafe {
Expand Down
18 changes: 13 additions & 5 deletions alacritty/src/renderer/platform.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -78,9 +78,17 @@ pub fn create_gl_context(
gl_config: &Config,
raw_window_handle: Option<RawWindowHandle>,
) -> GlutinResult<NotCurrentContext> {
let context_attributes = ContextAttributesBuilder::new()
.with_context_api(ContextApi::OpenGl(Some(Version::new(3, 3))))
.build(raw_window_handle);
let mut context_attributes = ContextAttributesBuilder::new()
.with_context_api(ContextApi::OpenGl(Some(Version::new(3, 3))));

// Try to enable robustness.
if gl_display.supported_features().contains(DisplayFeatures::CONTEXT_ROBUSTNESS) {
context_attributes =
context_attributes.with_robustness(Robustness::RobustLoseContextOnReset);
}

// Build the attributes.
let context_attributes = context_attributes.build(raw_window_handle);

unsafe {
if let Ok(gl_context) = gl_display.create_context(gl_config, &context_attributes) {
Expand Down