diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h index 1150570fea..9e4041cad7 100644 --- a/include/SDL3/SDL_hints.h +++ b/include/SDL3/SDL_hints.h @@ -3006,6 +3006,42 @@ extern "C" { */ #define SDL_HINT_OPENGL_ES_DRIVER "SDL_OPENGL_ES_DRIVER" +/** + * A variable controlling whether to force an sRGB-capable OpenGL context. + * + * At OpenGL context creation time, some platforms can request an sRGB-capable + * context. However, sometimes any form of the request can cause surprising + * results on some drivers, platforms, and hardware. Usually the surprise is + * in the form of rendering that is either a little darker or a little + * brighter than intended. + * + * This hint allows the user to override the app's sRGB requests and either + * force a specific value, or avoid requesting anything at all, depending on + * what makes things work correctly for their system. + * + * This is meant as a fail-safe; apps should probably not explicitly set this, + * and most users should not, either. + * + * Note that some platforms cannot make this request at all, and on all + * platforms this request can be denied by the operating system. + * + * The variable can be set to the following values: + * + * - "0": Force a request for an OpenGL context that is _not_ sRGB-capable. + * - "1": Force a request for an OpenGL context that _is_ sRGB-capable. + * - "skip": Don't make any request for an sRGB-capable context + * (don't specify the attribute at all during context creation time). + * - any other string is undefined behavior. + * + * If unset, or set to an empty string, SDL will make a request using the + * value the app specified with the SDL_GL_FRAMEBUFFER_SRGB_CAPABLE attribute. + * + * This hint should be set before an OpenGL context is created. + * + * \since This hint is available since SDL 3.4.2. + */ +#define SDL_HINT_OPENGL_FORCE_SRGB_CAPABLE "SDL_OPENGL_FORCE_SRGB_CAPABLE" + /** * Mechanism to specify openvr_api library location * diff --git a/src/video/SDL_egl.c b/src/video/SDL_egl.c index 1d35ef0dd4..52bcb81d95 100644 --- a/src/video/SDL_egl.c +++ b/src/video/SDL_egl.c @@ -38,6 +38,7 @@ #include "SDL_sysvideo.h" #include "SDL_egl_c.h" +#include "../SDL_hints_c.h" #ifdef EGL_KHR_create_context // EGL_OPENGL_ES3_BIT_KHR was added in version 13 of the extension. @@ -1270,18 +1271,22 @@ EGLSurface SDL_EGL_CreateSurface(SDL_VideoDevice *_this, SDL_Window *window, Nat ANativeWindow_setBuffersGeometry(nw, 0, 0, format_wanted); #endif - if (_this->gl_config.framebuffer_srgb_capable >= 0) { #ifdef EGL_KHR_gl_colorspace - if (SDL_EGL_HasExtension(_this, SDL_EGL_DISPLAY_EXTENSION, "EGL_KHR_gl_colorspace")) { + if (SDL_EGL_HasExtension(_this, SDL_EGL_DISPLAY_EXTENSION, "EGL_KHR_gl_colorspace")) { + const char *srgbhint = SDL_GetHint(SDL_HINT_OPENGL_FORCE_SRGB_CAPABLE); + if (srgbhint && *srgbhint) { + if (SDL_strcmp(srgbhint, "skip") == 0) { + // don't set an attribute at all. + } else { + attribs[attr++] = EGL_GL_COLORSPACE_KHR; + attribs[attr++] = SDL_GetStringBoolean(srgbhint, false) ? EGL_GL_COLORSPACE_SRGB_KHR : EGL_GL_COLORSPACE_LINEAR_KHR; + } + } else if (_this->gl_config.framebuffer_srgb_capable >= 0) { // default behavior without the hint. attribs[attr++] = EGL_GL_COLORSPACE_KHR; attribs[attr++] = _this->gl_config.framebuffer_srgb_capable ? EGL_GL_COLORSPACE_SRGB_KHR : EGL_GL_COLORSPACE_LINEAR_KHR; - } else -#endif - if (_this->gl_config.framebuffer_srgb_capable > 0) { - SDL_SetError("EGL implementation does not support sRGB system framebuffers"); - return EGL_NO_SURFACE; } } +#endif int opaque_ext_idx = -1; diff --git a/src/video/uikit/SDL_uikitopengles.m b/src/video/uikit/SDL_uikitopengles.m index 1872b1b43d..20156a7671 100644 --- a/src/video/uikit/SDL_uikitopengles.m +++ b/src/video/uikit/SDL_uikitopengles.m @@ -31,6 +31,7 @@ #include "../../events/SDL_keyboard_c.h" #include "../../events/SDL_mouse_c.h" #include "../../power/uikit/SDL_syspower.h" +#include "../../SDL_hints_c.h" #include @interface SDLEAGLContext : EAGLContext @@ -153,6 +154,12 @@ SDL_GLContext UIKit_GL_CreateContext(SDL_VideoDevice *_this, SDL_Window *window) return NULL; } + int srgb = _this->gl_config.framebuffer_srgb_capable; + const char *srgbhint = SDL_GetHint(SDL_HINT_OPENGL_FORCE_SRGB_CAPABLE); + if (srgbhint && *srgbhint) { + srgb = SDL_GetStringBoolean(srgbhint, false) ? 1 : 0; // there is no "skip" here, since initWithFrame expects it, so we'll treat it as false. + } + // construct our view, passing in SDL's OpenGL configuration data view = [[SDL_uikitopenglview alloc] initWithFrame:frame scale:scale @@ -163,7 +170,7 @@ SDL_GLContext UIKit_GL_CreateContext(SDL_VideoDevice *_this, SDL_Window *window) aBits:_this->gl_config.alpha_size depthBits:_this->gl_config.depth_size stencilBits:_this->gl_config.stencil_size - sRGB:_this->gl_config.framebuffer_srgb_capable + sRGB:srgb multisamples:samples context:context]; diff --git a/src/video/windows/SDL_windowsopengl.c b/src/video/windows/SDL_windowsopengl.c index 1626b5c63e..7e9045b94d 100644 --- a/src/video/windows/SDL_windowsopengl.c +++ b/src/video/windows/SDL_windowsopengl.c @@ -24,6 +24,7 @@ #include "SDL_windowsvideo.h" #include "SDL_windowsopengles.h" +#include "../../SDL_hints_c.h" // WGL implementation of SDL OpenGL support @@ -660,8 +661,18 @@ static bool WIN_GL_SetupWindowInternal(SDL_VideoDevice *_this, SDL_Window *windo } if (_this->gl_data->HAS_WGL_ARB_framebuffer_sRGB) { - *iAttr++ = WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB; - *iAttr++ = (_this->gl_config.framebuffer_srgb_capable > 0) ? GL_TRUE : GL_FALSE; + const char *srgbhint = SDL_GetHint(SDL_HINT_OPENGL_FORCE_SRGB_CAPABLE); + if (srgbhint && *srgbhint) { + if (SDL_strcmp(srgbhint, "skip") == 0) { + // don't set an attribute at all. + } else { + *iAttr++ = WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB; + *iAttr++ = SDL_GetStringBoolean(srgbhint, false) ? GL_TRUE : GL_FALSE; + } + } else if (_this->gl_config.framebuffer_srgb_capable) { // default behavior without the hint. + *iAttr++ = WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB; + *iAttr++ = GL_TRUE; + } } /* We always choose either FULL or NO accel on Windows, because of flaky diff --git a/src/video/x11/SDL_x11opengl.c b/src/video/x11/SDL_x11opengl.c index dedc150ed4..2654b99301 100644 --- a/src/video/x11/SDL_x11opengl.c +++ b/src/video/x11/SDL_x11opengl.c @@ -25,6 +25,7 @@ #include "SDL_x11video.h" #include "SDL_x11xsync.h" +#include "../../SDL_hints_c.h" // GLX implementation of SDL OpenGL support @@ -583,9 +584,19 @@ static int X11_GL_GetAttributes(SDL_VideoDevice *_this, Display *display, int sc attribs[i++] = GLX_RGBA_FLOAT_TYPE_ARB; } - if ((_this->gl_config.framebuffer_srgb_capable >= 0) && _this->gl_data->HAS_GLX_ARB_framebuffer_sRGB) { - attribs[i++] = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB; - attribs[i++] = _this->gl_config.framebuffer_srgb_capable ? True : False; // always needed, for_FBConfig or not! + if (_this->gl_data->HAS_GLX_ARB_framebuffer_sRGB) { + const char *srgbhint = SDL_GetHint(SDL_HINT_OPENGL_FORCE_SRGB_CAPABLE); + if (srgbhint && *srgbhint) { + if (SDL_strcmp(srgbhint, "skip") == 0) { + // don't set an attribute at all. + } else { + attribs[i++] = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB; + attribs[i++] = SDL_GetStringBoolean(srgbhint, false) ? True : False; // always needed, for_FBConfig or not! + } + } else if (_this->gl_config.framebuffer_srgb_capable) { // default behavior without the hint. + attribs[i++] = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB; + attribs[i++] = True; // always needed, for_FBConfig or not! + } } if (_this->gl_config.accelerated >= 0 &&