diff --git a/doc/client.asciidoc b/doc/client.asciidoc index 2a0a44e09..cd9830136 100644 --- a/doc/client.asciidoc +++ b/doc/client.asciidoc @@ -830,6 +830,22 @@ gl_texturebits:: (should be typically 0, 8, 16 or 32). Default value is 0 (choose the best internal format automatically). +gl_debug:: + Create debug OpenGL context, if supported by OpenGL implementation. + This option may hurt performance and is intended for developers only. + Default value is 0. + +gl_profile:: + Specifies custom profile and version of OpenGL context to create. Default + value is empty string, which means to create highest version compatibility + profile context. Can be set to "gl" to create OpenGL core profile context, + or "es" to create OpenGL ES context. May be followed by desired minimum + OpenGL version number. For example, "gl4.6" or "es3.0". Not all OpenGL + implementations support this. + +NOTE: Using OpenGL compatibility profile is recommended. Some features may not +work in more restricted profiles, or performance may be suboptimal. + gl_screenshot_format:: Specifies image format ‘screenshot’ command uses. Possible values are "png", "jpg" and "tga". Default value is "jpg". diff --git a/inc/refresh/refresh.h b/inc/refresh/refresh.h index 6f0bbbf37..6dbd8b229 100644 --- a/inc/refresh/refresh.h +++ b/inc/refresh/refresh.h @@ -109,12 +109,21 @@ typedef struct { particle_t *particles; } refdef_t; +enum { + QGL_PROFILE_NONE, + QGL_PROFILE_CORE, + QGL_PROFILE_ES, +}; + typedef struct { uint8_t colorbits; uint8_t depthbits; uint8_t stencilbits; uint8_t multisamples; bool debug; + uint8_t profile; + uint8_t major_ver; + uint8_t minor_ver; } r_opengl_config_t; typedef enum { diff --git a/src/refresh/main.c b/src/refresh/main.c index 9fc263101..a079e4afa 100644 --- a/src/refresh/main.c +++ b/src/refresh/main.c @@ -1173,6 +1173,29 @@ r_opengl_config_t R_GetGLConfig(void) if (cfg.multisamples < 2) cfg.multisamples = 0; + const char *s = Cvar_Get("gl_profile", "", CVAR_REFRESH)->string; + + if (!Q_stricmpn(s, "gl", 2)) + cfg.profile = QGL_PROFILE_CORE; + else if (!Q_stricmpn(s, "es", 2)) + cfg.profile = QGL_PROFILE_ES; + + if (cfg.profile) { + int major = 0, minor = 0; + + sscanf(s + 2, "%d.%d", &major, &minor); + if (major >= 1 && minor >= 0) { + cfg.major_ver = major; + cfg.minor_ver = minor; + } else if (cfg.profile == QGL_PROFILE_CORE) { + cfg.major_ver = 3; + cfg.minor_ver = 2; + } else if (cfg.profile == QGL_PROFILE_ES) { + cfg.major_ver = 3; + cfg.minor_ver = 0; + } + } + return cfg; } diff --git a/src/unix/video/sdl.c b/src/unix/video/sdl.c index 4a30d417d..57cfa3227 100644 --- a/src/unix/video/sdl.c +++ b/src/unix/video/sdl.c @@ -74,15 +74,16 @@ static void set_gl_attributes(void) SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, cfg.multisamples); } - if (cfg.debug) { + + if (cfg.debug) SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); - } -#if USE_GLES - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); -#endif + if (cfg.profile) { + if (cfg.profile == QGL_PROFILE_ES) + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, cfg.major_ver); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, cfg.minor_ver); + } } static void *get_proc_addr(const char *sym) diff --git a/src/unix/video/x11.c b/src/unix/video/x11.c index f5cdaca17..09a730824 100644 --- a/src/unix/video/x11.c +++ b/src/unix/video/x11.c @@ -78,10 +78,11 @@ static struct { } x11; enum { - QGLX_ARB_create_context = BIT(0), - QGLX_ARB_multisample = BIT(1), - QGLX_EXT_swap_control = BIT(2), - QGLX_EXT_swap_control_tear = BIT(3), + QGLX_ARB_create_context = BIT(0), + QGLX_ARB_multisample = BIT(1), + QGLX_EXT_create_context_es_profile = BIT(2), + QGLX_EXT_swap_control = BIT(3), + QGLX_EXT_swap_control_tear = BIT(4), }; static unsigned glx_parse_extension_string(const char *s) @@ -89,6 +90,7 @@ static unsigned glx_parse_extension_string(const char *s) static const char *const extnames[] = { "GLX_ARB_create_context", "GLX_ARB_multisample", + "GLX_EXT_create_context_es_profile", "GLX_EXT_swap_control", "GLX_EXT_swap_control_tear", NULL @@ -379,20 +381,39 @@ static bool init(void) XFree(list); } - if (cfg.debug) { + if (cfg.profile == QGL_PROFILE_ES && !(x11.extensions & QGLX_EXT_create_context_es_profile)) { + Com_WPrintf("GLX_EXT_create_context_es_profile not found\n"); + cfg.profile = QGL_PROFILE_NONE; + } + + if (cfg.debug || cfg.profile) { PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = NULL; if (x11.extensions & QGLX_ARB_create_context) glXCreateContextAttribsARB = get_proc_addr("glXCreateContextAttribsARB"); if (glXCreateContextAttribsARB) { - int ctx_attr[] = { - GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, - None - }; + int ctx_attr[9]; + int i = 0; + + if (cfg.profile) { + ctx_attr[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB; + ctx_attr[i++] = cfg.major_ver; + ctx_attr[i++] = GLX_CONTEXT_MINOR_VERSION_ARB; + ctx_attr[i++] = cfg.minor_ver; + } + if (cfg.profile == QGL_PROFILE_ES) { + ctx_attr[i++] = GLX_CONTEXT_PROFILE_MASK_ARB; + ctx_attr[i++] = GLX_CONTEXT_ES_PROFILE_BIT_EXT; + } + if (cfg.debug) { + ctx_attr[i++] = GLX_CONTEXT_FLAGS_ARB; + ctx_attr[i++] = GLX_CONTEXT_DEBUG_BIT_ARB; + } + ctx_attr[i] = None; if (!(x11.ctx = glXCreateContextAttribsARB(x11.dpy, fbc, NULL, True, ctx_attr))) - Com_EPrintf("Failed to create debug GL context\n"); + Com_EPrintf("Failed to create GL context with attributes\n"); } else { Com_WPrintf("GLX_ARB_create_context not found\n"); } diff --git a/src/windows/wgl.c b/src/windows/wgl.c index 06b44e80e..4c526924a 100644 --- a/src/windows/wgl.c +++ b/src/windows/wgl.c @@ -33,11 +33,12 @@ static struct { static cvar_t *gl_allow_software; enum { - QWGL_ARB_create_context = BIT(0), - QWGL_ARB_multisample = BIT(1), - QWGL_ARB_pixel_format = BIT(2), - QWGL_EXT_swap_control = BIT(3), - QWGL_EXT_swap_control_tear = BIT(4), + QWGL_ARB_create_context = BIT(0), + QWGL_ARB_multisample = BIT(1), + QWGL_ARB_pixel_format = BIT(2), + QWGL_EXT_create_context_es_profile = BIT(3), + QWGL_EXT_swap_control = BIT(4), + QWGL_EXT_swap_control_tear = BIT(5), }; static unsigned wgl_parse_extension_string(const char *s) @@ -46,6 +47,7 @@ static unsigned wgl_parse_extension_string(const char *s) "WGL_ARB_create_context", "WGL_ARB_multisample", "WGL_ARB_pixel_format", + "WGL_EXT_create_context_es_profile", "WGL_EXT_swap_control", "WGL_EXT_swap_control_tear", NULL @@ -152,11 +154,26 @@ static int wgl_setup_gl(r_opengl_config_t cfg) } // startup the OpenGL subsystem by creating a context and making it current - if (wgl.CreateContextAttribsARB && cfg.debug) { - int attr[] = { - WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB, - 0 - }; + if (wgl.CreateContextAttribsARB && (cfg.debug || cfg.profile)) { + int attr[9]; + int i = 0; + + if (cfg.profile) { + attr[i++] = WGL_CONTEXT_MAJOR_VERSION_ARB; + attr[i++] = cfg.major_ver; + attr[i++] = WGL_CONTEXT_MINOR_VERSION_ARB; + attr[i++] = cfg.minor_ver; + } + if (cfg.profile == QGL_PROFILE_ES) { + attr[i++] = WGL_CONTEXT_PROFILE_MASK_ARB; + attr[i++] = WGL_CONTEXT_ES_PROFILE_BIT_EXT; + } + if (cfg.debug) { + attr[i++] = WGL_CONTEXT_FLAGS_ARB; + attr[i++] = WGL_CONTEXT_DEBUG_BIT_ARB; + } + attr[i] = 0; + if (!(wgl.context = wgl.CreateContextAttribsARB(win.dc, NULL, attr))) { print_error("wglCreateContextAttribsARB"); goto soft; @@ -282,7 +299,7 @@ static bool wgl_init(void) cfg = R_GetGLConfig(); // check for extensions by creating a fake window - if (cfg.multisamples || cfg.debug) + if (cfg.multisamples || cfg.debug || cfg.profile) fake_extensions = get_fake_window_extensions(); if (cfg.multisamples) { @@ -297,9 +314,15 @@ static bool wgl_init(void) } } - if (cfg.debug && !wgl.CreateContextAttribsARB) { + if ((cfg.debug || cfg.profile) && !wgl.CreateContextAttribsARB) { Com_WPrintf("WGL_ARB_create_context not found\n"); cfg.debug = false; + cfg.profile = QGL_PROFILE_NONE; + } + + if (cfg.profile == QGL_PROFILE_ES && !(fake_extensions & QWGL_EXT_create_context_es_profile)) { + Com_WPrintf("WGL_EXT_create_context_es_profile not found\n"); + cfg.profile = QGL_PROFILE_NONE; } // create window, choose PFD, setup OpenGL context