From 261d6998fb7461fcdf6cf66a57863b8ed505a67c Mon Sep 17 00:00:00 2001
From: Simon Ser <contact@emersion.fr>
Date: Wed, 12 Jan 2022 18:56:08 +0100
Subject: render/gles2: query glGetGraphicsResetStatusKHR

Call glGetGraphicsResetStatusKHR in wlr_renderer_begin to figure
out when a GPU reset occurs. Destroy the renderer when this
happens (the OpenGL context is defunct).
---
 include/render/gles2.h  |  1 +
 render/gles2/renderer.c | 37 +++++++++++++++++++++++++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/include/render/gles2.h b/include/render/gles2.h
index 6631d34c..ff39206c 100644
--- a/include/render/gles2.h
+++ b/include/render/gles2.h
@@ -57,6 +57,7 @@ struct wlr_gles2_renderer {
 		PFNGLPOPDEBUGGROUPKHRPROC glPopDebugGroupKHR;
 		PFNGLPUSHDEBUGGROUPKHRPROC glPushDebugGroupKHR;
 		PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES;
+		PFNGLGETGRAPHICSRESETSTATUSKHRPROC glGetGraphicsResetStatusKHR;
 	} procs;
 
 	struct {
diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c
index ee4f60e8..47c58b19 100644
--- a/render/gles2/renderer.c
+++ b/render/gles2/renderer.c
@@ -198,6 +198,19 @@ static bool gles2_bind_buffer(struct wlr_renderer *wlr_renderer,
 	return true;
 }
 
+static const char *reset_status_str(GLenum status) {
+	switch (status) {
+	case GL_GUILTY_CONTEXT_RESET_KHR:
+		return "guilty";
+	case GL_INNOCENT_CONTEXT_RESET_KHR:
+		return "innocent";
+	case GL_UNKNOWN_CONTEXT_RESET_KHR:
+		return "unknown";
+	default:
+		return "<invalid>";
+	}
+}
+
 static bool gles2_begin(struct wlr_renderer *wlr_renderer, uint32_t width,
 		uint32_t height) {
 	struct wlr_gles2_renderer *renderer =
@@ -205,6 +218,15 @@ static bool gles2_begin(struct wlr_renderer *wlr_renderer, uint32_t width,
 
 	push_gles2_debug(renderer);
 
+	if (renderer->procs.glGetGraphicsResetStatusKHR) {
+		GLenum status = renderer->procs.glGetGraphicsResetStatusKHR();
+		if (status != GL_NO_ERROR) {
+			wlr_log(WLR_ERROR, "GPU reset (%s)", reset_status_str(status));
+			wl_signal_emit_mutable(&wlr_renderer->events.lost, NULL);
+			return false;
+		}
+	}
+
 	glViewport(0, 0, width, height);
 	renderer->viewport_width = width;
 	renderer->viewport_height = height;
@@ -771,6 +793,21 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) {
 			"glEGLImageTargetRenderbufferStorageOES");
 	}
 
+	if (check_gl_ext(exts_str, "GL_KHR_robustness")) {
+		GLint notif_strategy = 0;
+		glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_KHR, &notif_strategy);
+		switch (notif_strategy) {
+		case GL_LOSE_CONTEXT_ON_RESET_KHR:
+			wlr_log(WLR_DEBUG, "GPU reset notifications are enabled");
+			load_gl_proc(&renderer->procs.glGetGraphicsResetStatusKHR,
+				"glGetGraphicsResetStatusKHR");
+			break;
+		case GL_NO_RESET_NOTIFICATION_KHR:
+			wlr_log(WLR_DEBUG, "GPU reset notifications are disabled");
+			break;
+		}
+	}
+
 	if (renderer->exts.KHR_debug) {
 		glEnable(GL_DEBUG_OUTPUT_KHR);
 		glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
-- 
cgit v1.2.3