From d8102bf660facf39946b21eb8f5f24348e2e9fb6 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Thu, 23 Nov 2023 13:47:13 -0500
Subject: [PATCH] x11: Deal with difference in GLX_EXT_swap_control_tear
behavior.
Mesa and Nvidia handle it differently, and one or the other may fix their
implementation in the future, so test which way it works at runtime.
Reference Issue #8004.
(cherry picked from commit 74a25425646d64edeff508ec8e99622a41576905)
---
src/video/x11/SDL_x11opengl.c | 65 ++++++++++++++++++++++++++++++++---
src/video/x11/SDL_x11opengl.h | 10 ++++++
test/testgl2.c | 10 +++++-
3 files changed, 80 insertions(+), 5 deletions(-)
diff --git a/src/video/x11/SDL_x11opengl.c b/src/video/x11/SDL_x11opengl.c
index 057cb1fcd4ec..58f69b1a0129 100644
--- a/src/video/x11/SDL_x11opengl.c
+++ b/src/video/x11/SDL_x11opengl.c
@@ -241,6 +241,8 @@ int X11_GL_LoadLibrary(_THIS, const char *path)
return SDL_SetError("GLX is not supported");
}
+ _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_UNTESTED;
+
/* Initialize extensions */
/* See lengthy comment about the inc/dec in
../windows/SDL_windowsopengl.c. */
@@ -889,7 +891,6 @@ int X11_GL_SetSwapInterval(_THIS, int interval)
int currentInterval = X11_GL_GetSwapInterval(_this);
_this->gl_data->glXSwapIntervalEXT(display, drawable, currentInterval);
_this->gl_data->glXSwapIntervalEXT(display, drawable, interval);
-
status = 0;
swapinterval = interval;
} else if (_this->gl_data->glXSwapIntervalMESA) {
@@ -912,6 +913,53 @@ int X11_GL_SetSwapInterval(_THIS, int interval)
return status;
}
+static SDL_GLSwapIntervalTearBehavior CheckSwapIntervalTearBehavior(SDL_VideoDevice *_this, Window drawable, unsigned int current_val, unsigned int current_allow_late)
+{
+ /* Mesa and Nvidia interpret GLX_EXT_swap_control_tear differently, as of this writing, so
+ figure out which behavior we have.
+ Technical details: https://github.com/libsdl-org/SDL/issues/8004#issuecomment-1819603282 */
+ if (_this->gl_data->swap_interval_tear_behavior == SDL_SWAPINTERVALTEAR_UNTESTED) {
+ if (!_this->gl_data->HAS_GLX_EXT_swap_control_tear) {
+ _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_UNKNOWN;
+ } else {
+ Display *display = ((SDL_VideoData *)_this->driverdata)->display;
+ unsigned int allow_late_swap_tearing = 22;
+ int original_val = (int) current_val;
+
+ /*
+ * This is a workaround for a bug in NVIDIA drivers. Bug has been reported
+ * and will be fixed in a future release (probably 319.xx).
+ *
+ * There's a bug where glXSetSwapIntervalEXT ignores updates because
+ * it has the wrong value cached. To work around it, we just run a no-op
+ * update to the current value.
+ */
+ _this->gl_data->glXSwapIntervalEXT(display, drawable, current_val);
+
+ /* set it to no swap interval and see how it affects GLX_LATE_SWAPS_TEAR_EXT... */
+ _this->gl_data->glXSwapIntervalEXT(display, drawable, 0);
+ _this->gl_data->glXQueryDrawable(display, drawable, GLX_LATE_SWAPS_TEAR_EXT, &allow_late_swap_tearing);
+
+ if (allow_late_swap_tearing == 0) { /* GLX_LATE_SWAPS_TEAR_EXT says whether late swapping is currently in use */
+ _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_NVIDIA;
+ if (current_allow_late) {
+ original_val = -original_val;
+ }
+ } else if (allow_late_swap_tearing == 1) { /* GLX_LATE_SWAPS_TEAR_EXT says whether the Drawable can use late swapping at all */
+ _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_MESA;
+ } else { /* unexpected outcome! */
+ _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_UNKNOWN;
+ }
+
+ /* set us back to what it was originally... */
+ _this->gl_data->glXSwapIntervalEXT(display, drawable, original_val);
+ }
+ }
+
+ return _this->gl_data->swap_interval_tear_behavior;
+}
+
+
int X11_GL_GetSwapInterval(_THIS)
{
if (_this->gl_data->glXSwapIntervalEXT) {
@@ -924,6 +972,7 @@ int X11_GL_GetSwapInterval(_THIS)
unsigned int interval = 0;
if (_this->gl_data->HAS_GLX_EXT_swap_control_tear) {
+ allow_late_swap_tearing = 22; /* set this to nonsense. */
_this->gl_data->glXQueryDrawable(display, drawable,
GLX_LATE_SWAPS_TEAR_EXT,
&allow_late_swap_tearing);
@@ -932,11 +981,19 @@ int X11_GL_GetSwapInterval(_THIS)
_this->gl_data->glXQueryDrawable(display, drawable,
GLX_SWAP_INTERVAL_EXT, &interval);
- if ((allow_late_swap_tearing) && (interval > 0)) {
- return -((int)interval);
+ switch (CheckSwapIntervalTearBehavior(_this, drawable, interval, allow_late_swap_tearing)) {
+ case SDL_SWAPINTERVALTEAR_MESA:
+ return (int)interval; /* unsigned int cast to signed that generates negative value if necessary. */
+
+ case SDL_SWAPINTERVALTEAR_NVIDIA:
+ default:
+ if ((allow_late_swap_tearing) && (interval > 0)) {
+ return -((int)interval);
+ }
+ return (int)interval;
}
- return (int)interval;
+ return (int)interval; /* shouldn't hit this, but just in case. */
} else if (_this->gl_data->glXGetSwapIntervalMESA) {
return _this->gl_data->glXGetSwapIntervalMESA();
} else {
diff --git a/src/video/x11/SDL_x11opengl.h b/src/video/x11/SDL_x11opengl.h
index c66340237e5b..3c67b3d4ace1 100644
--- a/src/video/x11/SDL_x11opengl.h
+++ b/src/video/x11/SDL_x11opengl.h
@@ -27,6 +27,14 @@
#include "SDL_opengl.h"
#include <GL/glx.h>
+typedef enum SDL_GLSwapIntervalTearBehavior
+{
+ SDL_SWAPINTERVALTEAR_UNTESTED,
+ SDL_SWAPINTERVALTEAR_UNKNOWN,
+ SDL_SWAPINTERVALTEAR_MESA,
+ SDL_SWAPINTERVALTEAR_NVIDIA
+} SDL_GLSwapIntervalTearBehavior;
+
struct SDL_GLDriverData
{
int errorBase, eventBase;
@@ -48,6 +56,8 @@ struct SDL_GLDriverData
int minor;
} es_profile_max_supported_version;
+ SDL_GLSwapIntervalTearBehavior swap_interval_tear_behavior;
+
Bool (*glXQueryExtension)(Display *, int *, int *);
void *(*glXGetProcAddress)(const GLubyte *);
XVisualInfo *(*glXChooseVisual)(Display *, int, int *);
diff --git a/test/testgl2.c b/test/testgl2.c
index 7ae37743e41c..29b4bb85fc8d 100644
--- a/test/testgl2.c
+++ b/test/testgl2.c
@@ -205,6 +205,11 @@ Render()
ctx.glRotatef(5.0, 1.0, 1.0, 1.0);
}
+static void LogSwapInterval(void)
+{
+ SDL_Log("Swap Interval : %d\n", SDL_GL_GetSwapInterval());
+}
+
int main(int argc, char *argv[])
{
int fsaa, accel;
@@ -300,7 +305,9 @@ int main(int argc, char *argv[])
SDL_GetCurrentDisplayMode(0, &mode);
SDL_Log("Screen BPP : %" SDL_PRIu32 "\n", SDL_BITSPERPIXEL(mode.format));
- SDL_Log("Swap Interval : %d\n", SDL_GL_GetSwapInterval());
+
+ LogSwapInterval();
+
SDL_GetWindowSize(state->windows[0], &dw, &dh);
SDL_Log("Window Size : %d,%d\n", dw, dh);
SDL_GL_GetDrawableSize(state->windows[0], &dw, &dh);
@@ -408,6 +415,7 @@ int main(int argc, char *argv[])
SDL_GL_MakeCurrent(state->windows[i], context);
if (update_swap_interval) {
SDL_GL_SetSwapInterval(swap_interval);
+ LogSwapInterval();
}
SDL_GL_GetDrawableSize(state->windows[i], &w, &h);
ctx.glViewport(0, 0, w, h);