]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - vid_shared.c
vid: refactor modesetting fallbacks
[xonotic/darkplaces.git] / vid_shared.c
index bc4c98552e0918f82454f61ddd25d1bf6dae3d36..f8868f950e8e12acd98b0329440b0931baba0363 100644 (file)
@@ -129,7 +129,6 @@ cvar_t gl_info_vendor = {CF_CLIENT | CF_READONLY, "gl_info_vendor", "", "indicat
 cvar_t gl_info_renderer = {CF_CLIENT | CF_READONLY, "gl_info_renderer", "", "indicates graphics chip model and other information"};
 cvar_t gl_info_version = {CF_CLIENT | CF_READONLY, "gl_info_version", "", "indicates version of current renderer. begins with 1.0.0, 1.1.0, 1.2.0, 1.3.1 etc."};
 cvar_t gl_info_extensions = {CF_CLIENT | CF_READONLY, "gl_info_extensions", "", "indicates extension list found by engine, space separated."};
-cvar_t gl_info_platform = {CF_CLIENT | CF_READONLY, "gl_info_platform", "", "indicates GL platform: WGL, GLX, or AGL."};
 cvar_t gl_info_driver = {CF_CLIENT | CF_READONLY, "gl_info_driver", "", "name of driver library (opengl32.dll, libGL.so.1, or whatever)."};
 
 cvar_t vid_fullscreen = {CF_CLIENT | CF_ARCHIVE, "vid_fullscreen", "1", "use fullscreen (1) or windowed (0)"};
@@ -148,9 +147,10 @@ cvar_t vid_touchscreen_density = {CF_CLIENT, "vid_touchscreen_density", "2.0", "
 cvar_t vid_touchscreen_xdpi = {CF_CLIENT, "vid_touchscreen_xdpi", "300", "Horizontal DPI of the screen (only valid on Android currently)"};
 cvar_t vid_touchscreen_ydpi = {CF_CLIENT, "vid_touchscreen_ydpi", "300", "Vertical DPI of the screen (only valid on Android currently)"};
 
-cvar_t vid_vsync = {CF_CLIENT | CF_ARCHIVE, "vid_vsync", "0", "sync to vertical blank, prevents 'tearing' (seeing part of one frame and part of another on the screen at the same time), automatically disabled when doing timedemo benchmarks"};
+cvar_t vid_vsync = {CF_CLIENT | CF_ARCHIVE, "vid_vsync", "0", "sync to vertical blank, prevents 'tearing' (seeing part of one frame and part of another on the screen at the same time) at the cost of latency, 1 always syncs and -1 is adaptive (stops syncing if the framerate drops, unsupported by some platforms), automatically disabled when doing timedemo benchmarks"};
 cvar_t vid_mouse = {CF_CLIENT | CF_ARCHIVE, "vid_mouse", "1", "whether to use the mouse in windowed mode (fullscreen always does)"};
 cvar_t vid_mouse_clickthrough = {CF_CLIENT | CF_ARCHIVE, "vid_mouse_clickthrough", "0", "mouse behavior in windowed mode: 0 = click to focus, 1 = allow interaction even if the window is not focused (click-through behaviour, can be useful when using third-party game overlays)"};
+cvar_t vid_minimize_on_focus_loss = {CF_CLIENT | CF_ARCHIVE, "vid_minimize_on_focus_loss", "0", "whether to minimize the fullscreen window if it loses focus (such as by alt+tab)"};
 cvar_t vid_grabkeyboard = {CF_CLIENT | CF_ARCHIVE, "vid_grabkeyboard", "0", "whether to grab the keyboard when mouse is active (prevents use of volume control keys, music player keys, etc on some keyboards)"};
 cvar_t vid_minwidth = {CF_CLIENT, "vid_minwidth", "0", "minimum vid_width that is acceptable (to be set in default.cfg in mods)"};
 cvar_t vid_minheight = {CF_CLIENT, "vid_minheight", "0", "minimum vid_height that is acceptable (to be set in default.cfg in mods)"};
@@ -162,10 +162,12 @@ cvar_t vid_touchscreen = {CF_CLIENT, "vid_touchscreen", "0", "Use touchscreen-st
 cvar_t vid_touchscreen_showkeyboard = {CF_CLIENT, "vid_touchscreen_showkeyboard", "0", "shows the platform's screen keyboard for text entry, can be set by csqc or menu qc if it wants to receive text input, does nothing if the platform has no screen keyboard"};
 cvar_t vid_touchscreen_supportshowkeyboard = {CF_CLIENT | CF_READONLY, "vid_touchscreen_supportshowkeyboard", "0", "indicates if the platform supports a virtual keyboard"};
 cvar_t vid_stick_mouse = {CF_CLIENT | CF_ARCHIVE, "vid_stick_mouse", "0", "have the mouse stuck in the center of the screen" };
-cvar_t vid_resizable = {CF_CLIENT | CF_ARCHIVE, "vid_resizable", "0", "0: window not resizable, 1: resizable, 2: window can be resized but the framebuffer isn't adjusted" };
+cvar_t vid_resizable = {CF_CLIENT | CF_ARCHIVE, "vid_resizable", "1", "0: window not resizable, 1: resizable, 2: window can be resized but the framebuffer isn't adjusted" };
 cvar_t vid_desktopfullscreen = {CF_CLIENT | CF_ARCHIVE, "vid_desktopfullscreen", "1", "force desktop resolution for fullscreen; also use some OS dependent tricks for better fullscreen integration"};
+cvar_t vid_display = {CF_CLIENT | CF_ARCHIVE, "vid_display", "0", "which monitor to render on, numbered from 0 (system default)" };
+cvar_t vid_info_displaycount = {CF_CLIENT | CF_READONLY, "vid_info_displaycount", "1", "how many monitors are currently available, updated by hotplug events" };
 #ifdef WIN32
-cvar_t vid_ignore_taskbar = {CF_CLIENT | CF_ARCHIVE, "vid_ignore_taskbar", "0", "in windowed mode, prevent the Windows taskbar and window borders from affecting the size and placement of the window. it will be aligned centered and uses the unaltered vid_width/vid_height values"};
+cvar_t vid_ignore_taskbar = {CF_CLIENT | CF_ARCHIVE, "vid_ignore_taskbar", "1", "in windowed mode, prevent the Windows taskbar and window borders from affecting the size and placement of the window. it will be aligned centered and uses the unaltered vid_width/vid_height values"};
 #endif
 
 cvar_t v_gamma = {CF_CLIENT | CF_ARCHIVE, "v_gamma", "1", "inverse gamma correction value, a brightness effect that does not affect white or black, and tends to make the image grey and dull"};
@@ -191,12 +193,6 @@ const char *gl_vendor;
 const char *gl_renderer;
 // begins with 1.0.0, 1.1.0, 1.2.0, 1.2.1, 1.3.0, 1.3.1, or 1.4.0
 const char *gl_version;
-// extensions list, space separated
-const char *gl_extensions;
-// WGL, GLX, or AGL
-const char *gl_platform;
-// name of driver library (opengl32.dll, libGL.so.1, or whatever)
-char gl_driver[256];
 
 #ifndef USE_GLES2
 GLboolean (GLAPIENTRY *qglIsBuffer) (GLuint buffer);
@@ -686,20 +682,40 @@ void VID_ClearExtensions(void)
        memset(&vid.support, 0, sizeof(vid.support));
 }
 
-void GL_Setup(void)
+void GL_InitFunctions(void)
 {
-       char *s;
-       int j;
-       GLint numextensions = 0;
+#ifndef USE_GLES2
        const glfunction_t *func;
        qbool missingrequiredfuncs = false;
        static char missingfuncs[16384];
 
-#ifndef USE_GLES2
        // first fetch the function pointers for everything - after this we can begin making GL calls.
        for (func = openglfuncs; func->name != NULL; func++)
                *func->funcvariable = (void *)GL_GetProcAddress(func->name);
+
+       missingfuncs[0] = 0;
+       for (func = openglfuncs; func && func->name != NULL; func++)
+       {
+               if (!*func->funcvariable && !strcmp(func->extension, "core"))
+               {
+                       Con_DPrintf("GL context is missing required function \"%s\"!\n", func->name);
+                       missingrequiredfuncs = true;
+                       dp_strlcat(missingfuncs, " ", sizeof(missingfuncs));
+                       dp_strlcat(missingfuncs, func->name, sizeof(missingfuncs));
+               }
+       }
+
+       if (missingrequiredfuncs)
+               Sys_Error("OpenGL driver/hardware lacks required features:\n%s", missingfuncs);
 #endif
+}
+
+void GL_Setup(void)
+{
+       char *s;
+       int j;
+       GLint numextensions = 0;
+       int majorv, minorv;
 
        gl_renderer = (const char *)qglGetString(GL_RENDERER);
        gl_vendor = (const char *)qglGetString(GL_VENDOR);
@@ -710,6 +726,13 @@ void GL_Setup(void)
        Con_Printf("GL_VERSION: %s\n", gl_version);
 
 #ifndef USE_GLES2
+       qglGetIntegerv(GL_MAJOR_VERSION, &majorv);
+       qglGetIntegerv(GL_MINOR_VERSION, &minorv);
+       vid.support.glversion = 10 * majorv + minorv;
+       if (vid.support.glversion < 32)
+               // fallback, should never get here: GL context creation should have failed
+               Sys_Error("OpenGL driver/hardware supports version %i.%i but 3.2 is the minimum\n", majorv, minorv);
+
        qglGetIntegerv(GL_NUM_EXTENSIONS, &numextensions);
        Con_DPrint("GL_EXTENSIONS:\n");
        for (j = 0; j < numextensions; j++)
@@ -722,23 +745,6 @@ void GL_Setup(void)
        Con_DPrint("\n");
 #endif //USE_GLES2
 
-#ifndef USE_GLES2
-       missingfuncs[0] = 0;
-       for (func = openglfuncs; func && func->name != NULL; func++)
-       {
-               if (!*func->funcvariable && !strcmp(func->extension, "core"))
-               {
-                       Con_DPrintf("GL context is missing required function \"%s\"!\n", func->name);
-                       missingrequiredfuncs = true;
-                       strlcat(missingfuncs, " ", sizeof(missingfuncs));
-                       strlcat(missingfuncs, func->name, sizeof(missingfuncs));
-               }
-       }
-
-       if (missingrequiredfuncs)
-               Sys_Error("OpenGL driver/hardware lacks required features:\n%s", missingfuncs);
-#endif
-
        Con_DPrint("Checking OpenGL extensions...\n");
 
        // detect what GLSL version is available, to enable features like higher quality reliefmapping
@@ -774,6 +780,17 @@ void GL_Setup(void)
 // COMMANDLINEOPTION: GL: -notexturegather disables GL_ARB_texture_gather (which provides fetch4 sampling)
 // COMMANDLINEOPTION: GL: -nogldebugoutput disables GL_ARB_debug_output (which provides the gl_debug feature, if enabled)
 
+#ifdef WIN32
+       // gl_texturecompression_color is somehow broken on AMD's Windows driver,
+       // see: https://gitlab.com/xonotic/darkplaces/-/issues/228
+       // HACK: force it off (less bad than adding hacky checks to the renderer)
+       if (strncmp(gl_renderer, "AMD Radeon(TM)", 14) == 0)
+       {
+               Cvar_SetQuick(&gl_texturecompression_color, "0");
+               gl_texturecompression_color.flags |= CF_READONLY;
+       }
+#endif
+
 #ifdef GL_MAX_DRAW_BUFFERS
        qglGetIntegerv(GL_MAX_DRAW_BUFFERS, (GLint*)&vid.maxdrawbuffers);
        CHECKGLERROR
@@ -1260,7 +1277,6 @@ void VID_Shared_Init(void)
        Cvar_RegisterVariable(&gl_info_renderer);
        Cvar_RegisterVariable(&gl_info_version);
        Cvar_RegisterVariable(&gl_info_extensions);
-       Cvar_RegisterVariable(&gl_info_platform);
        Cvar_RegisterVariable(&gl_info_driver);
        Cvar_RegisterVariable(&v_gamma);
        Cvar_RegisterVariable(&v_brightness);
@@ -1297,6 +1313,7 @@ void VID_Shared_Init(void)
        Cvar_RegisterVariable(&vid_vsync);
        Cvar_RegisterVariable(&vid_mouse);
        Cvar_RegisterVariable(&vid_mouse_clickthrough);
+       Cvar_RegisterVariable(&vid_minimize_on_focus_loss);
        Cvar_RegisterVariable(&vid_grabkeyboard);
        Cvar_RegisterVariable(&vid_touchscreen);
        Cvar_RegisterVariable(&vid_touchscreen_showkeyboard);
@@ -1304,6 +1321,8 @@ void VID_Shared_Init(void)
        Cvar_RegisterVariable(&vid_stick_mouse);
        Cvar_RegisterVariable(&vid_resizable);
        Cvar_RegisterVariable(&vid_desktopfullscreen);
+       Cvar_RegisterVariable(&vid_display);
+       Cvar_RegisterVariable(&vid_info_displaycount);
 #ifdef WIN32
        Cvar_RegisterVariable(&vid_ignore_taskbar);
 #endif
@@ -1385,15 +1404,14 @@ static int VID_Mode(int fullscreen, int width, int height, int bpp, float refres
        if (VID_InitMode(&mode))
        {
                // accept the (possibly modified) mode
-               vid.mode = mode;
-               vid.fullscreen     = vid.mode.fullscreen;
-               vid.width          = vid.mode.width;
-               vid.height         = vid.mode.height;
-               vid.bitsperpixel   = vid.mode.bitsperpixel;
-               vid.refreshrate    = vid.mode.refreshrate;
-               vid.userefreshrate = vid.mode.userefreshrate;
-               vid.stereobuffer   = vid.mode.stereobuffer;
-               vid.stencil        = vid.mode.bitsperpixel > 16;
+               vid.fullscreen     = mode.fullscreen;
+               vid.width          = mode.width;
+               vid.height         = mode.height;
+               vid.bitsperpixel   = mode.bitsperpixel;
+               vid.refreshrate    = mode.refreshrate;
+               vid.userefreshrate = mode.userefreshrate;
+               vid.stereobuffer   = mode.stereobuffer;
+               vid.stencil        = mode.bitsperpixel > 16;
                vid.sRGB2D         = vid_sRGB.integer >= 1 && vid.sRGBcapable2D;
                vid.sRGB3D         = vid_sRGB.integer >= 1 && vid.sRGBcapable3D;
 
@@ -1422,16 +1440,7 @@ static int VID_Mode(int fullscreen, int width, int height, int bpp, float refres
                )
                        vid.sRGB2D = vid.sRGB3D = false;
 
-               Con_Printf("Video Mode: %s %dx%dx%dx%.2fhz%s\n", mode.fullscreen ? "fullscreen" : "window", mode.width, mode.height, mode.bitsperpixel, mode.refreshrate, mode.stereobuffer ? " stereo" : "");
-
-               Cvar_SetValueQuick(&vid_fullscreen, vid.mode.fullscreen);
-               Cvar_SetValueQuick(&vid_width, vid.mode.width);
-               Cvar_SetValueQuick(&vid_height, vid.mode.height);
-               Cvar_SetValueQuick(&vid_bitsperpixel, vid.mode.bitsperpixel);
-               Cvar_SetValueQuick(&vid_samples, vid.mode.samples);
-               if(vid_userefreshrate.integer)
-                       Cvar_SetValueQuick(&vid_refreshrate, vid.mode.refreshrate);
-               Cvar_SetValueQuick(&vid_stereobuffer, vid.stereobuffer ? 1 : 0);
+               Con_Printf("Video Mode: %s %dx%dx%dx%.2fhz%s on display %i\n", mode.fullscreen ? "fullscreen" : "window", mode.width, mode.height, mode.bitsperpixel, mode.refreshrate, mode.stereobuffer ? " stereo" : "", vid.displayindex);
 
                if (vid_touchscreen.integer)
                {
@@ -1445,59 +1454,64 @@ static int VID_Mode(int fullscreen, int width, int height, int bpp, float refres
                return false;
 }
 
-static void VID_OpenSystems(void)
-{
-       Key_ReleaseAll();
-       R_Modules_Start();
-       S_Startup();
-}
-
-static void VID_CloseSystems(void)
-{
-       S_Shutdown();
-       R_Modules_Shutdown();
-       Key_ReleaseAll();
-}
-
 qbool vid_commandlinecheck = true;
 extern qbool vid_opened;
 
 void VID_Restart_f(cmd_state_t *cmd)
 {
        char vabuf[1024];
+       viddef_mode_t oldmode;
 
        // don't crash if video hasn't started yet
        if (vid_commandlinecheck)
                return;
 
-       if (!vid_opened)
-       {
-               SCR_BeginLoadingPlaque(false);
-               return;
-       }
+       oldmode.fullscreen     = vid.fullscreen;
+       oldmode.width          = vid.width;
+       oldmode.height         = vid.height;
+       oldmode.bitsperpixel   = vid.bitsperpixel;
+       oldmode.refreshrate    = vid.refreshrate;
+       oldmode.userefreshrate = vid.userefreshrate;
+       oldmode.stereobuffer   = vid.stereobuffer;
 
        Con_Printf("VID_Restart: changing from %s %dx%dx%dbpp%s, to %s %dx%dx%dbpp%s.\n",
-               vid.mode.fullscreen ? "fullscreen" : "window", vid.mode.width, vid.mode.height, vid.mode.bitsperpixel, vid.mode.fullscreen && vid.mode.userefreshrate ? va(vabuf, sizeof(vabuf), "x%.2fhz", vid.mode.refreshrate) : "",
+               oldmode.fullscreen ? "fullscreen" : "window", oldmode.width, oldmode.height, oldmode.bitsperpixel, oldmode.fullscreen && oldmode.userefreshrate ? va(vabuf, sizeof(vabuf), "x%.2fhz", oldmode.refreshrate) : "",
                vid_fullscreen.integer ? "fullscreen" : "window", vid_width.integer, vid_height.integer, vid_bitsperpixel.integer, vid_fullscreen.integer && vid_userefreshrate.integer ? va(vabuf, sizeof(vabuf), "x%.2fhz", vid_refreshrate.value) : "");
-       VID_CloseSystems();
+       SCR_DeferLoadingPlaque(false);
+       R_Modules_Shutdown();
        VID_Shutdown();
        if (!VID_Mode(vid_fullscreen.integer, vid_width.integer, vid_height.integer, vid_bitsperpixel.integer, vid_refreshrate.value, vid_stereobuffer.integer))
        {
                Con_Print("Video mode change failed\n");
-               if (!VID_Mode(vid.mode.fullscreen, vid.mode.width, vid.mode.height, vid.mode.bitsperpixel, vid.mode.refreshrate, vid.mode.stereobuffer))
+               if (!VID_Mode(oldmode.fullscreen, oldmode.width, oldmode.height, oldmode.bitsperpixel, oldmode.refreshrate, oldmode.stereobuffer))
                        Sys_Error("Unable to restore to last working video mode");
+               else
+               {
+                       Cvar_SetValueQuick(&vid_fullscreen,        oldmode.fullscreen);
+                       Cvar_SetValueQuick(&vid_width,             oldmode.width);
+                       Cvar_SetValueQuick(&vid_height,            oldmode.height);
+                       Cvar_SetValueQuick(&vid_bitsperpixel,      oldmode.bitsperpixel);
+                       Cvar_SetValueQuick(&vid_refreshrate,       oldmode.refreshrate);
+                       Cvar_SetValueQuick(&vid_stereobuffer,      oldmode.stereobuffer);
+               }
        }
-       VID_OpenSystems();
+       R_Modules_Start();
+       Key_ReleaseAll();
 }
 
-const char *vidfallbacks[][2] =
+struct vidfallback_s
 {
-       {"vid_stereobuffer", "0"},
-       {"vid_samples", "1"},
-       {"vid_userefreshrate", "0"},
-       {"vid_width", "640"},
-       {"vid_height", "480"},
-       {"vid_bitsperpixel", "32"},
+       cvar_t *cvar;
+       const char *safevalue;
+};
+static struct vidfallback_s vidfallbacks[] =
+{
+       {&vid_stereobuffer, "0"},
+       {&vid_samples, "1"},
+       {&vid_userefreshrate, "0"},
+       {&vid_width, "640"},
+       {&vid_height, "480"},
+       {&vid_bitsperpixel, "32"},
        {NULL, NULL}
 };
 
@@ -1552,22 +1566,18 @@ void VID_Start(void)
        success = VID_Mode(vid_fullscreen.integer, vid_width.integer, vid_height.integer, vid_bitsperpixel.integer, vid_refreshrate.value, vid_stereobuffer.integer);
        if (!success)
        {
-               Con_Print("Desired video mode fail, trying fallbacks...\n");
-               for (i = 0;!success && vidfallbacks[i][0] != NULL;i++)
+               Con_Print(CON_WARN "Desired video mode fail, trying fallbacks...\n");
+               for (i = 0; !success && vidfallbacks[i].cvar != NULL; i++)
                {
-                       Cvar_Set(&cvars_all, vidfallbacks[i][0], vidfallbacks[i][1]);
+                       Cvar_SetQuick(vidfallbacks[i].cvar, vidfallbacks[i].safevalue);
                        success = VID_Mode(vid_fullscreen.integer, vid_width.integer, vid_height.integer, vid_bitsperpixel.integer, vid_refreshrate.value, vid_stereobuffer.integer);
                }
                if (!success)
                        Sys_Error("Video modes failed");
        }
-       VID_OpenSystems();
-}
 
-void VID_Stop(void)
-{
-       VID_CloseSystems();
-       VID_Shutdown();
+       R_Modules_Start();
+       Key_ReleaseAll();
 }
 
 static int VID_SortModes_Compare(const void *a_, const void *b_)