From 26768c299010788e5c1ed553f11d2b464ea68a31 Mon Sep 17 00:00:00 2001 From: Kurt Loeffler Date: Sun, 2 Apr 2023 20:18:22 -0700 Subject: [PATCH 1/4] Add desktop fullscreen option in menu and decouple aspect ratio from window resolution. Desktop Fullscreen: It was previously possible to run ironwail in desktop fullscreen mode, but only by manually setting a cvar. This change adds the ability to pick this display mode within the video settings menu. Decoupling Aspect Ratio: Previously in ironwail the window/fullscreen size always dictates the aspect ratio tha the game is renderred in. This is fine for windowed and exclusive fullscreen where you can set the specific resolution you want, however with desktop fullscreen, the window must be the same size that your operating system is outputting. Decoupling the aspect ratio allows the user specify a specific maximum aspect ratio to use in this circumstance. For example 4:3 on a standard 16:9 display, or 16:9 on an ultrawide display. The new vid_maxaspect cvar default to 0, which means auto (match window/fullscreen resolution). --- Quake/gl_draw.c | 2 +- Quake/gl_rmain.c | 14 +++--- Quake/gl_shaders.h | 5 ++- Quake/gl_vidsdl.c | 60 ++++++++++++++++++++------ Quake/in_sdl.c | 2 - Quake/menu.c | 105 ++++++++++++++++++++++++++++++++++++++++----- 6 files changed, 154 insertions(+), 34 deletions(-) diff --git a/Quake/gl_draw.c b/Quake/gl_draw.c index 4f518d888..6e49b96ff 100644 --- a/Quake/gl_draw.c +++ b/Quake/gl_draw.c @@ -1193,7 +1193,7 @@ void GL_Set2D (void) glcanvas.type = CANVAS_INVALID; glcanvas.texture = NULL; glcanvas.blendmode = GLS_BLEND_ALPHA; - glViewport (glx, gly, glwidth, glheight); + glViewport (0, 0, glwidth, glheight); GL_SetCanvas (CANVAS_DEFAULT); GL_SetCanvasColor (1.f, 1.f, 1.f, 1.f); } diff --git a/Quake/gl_rmain.c b/Quake/gl_rmain.c index e1274f88c..4a9be0fbc 100644 --- a/Quake/gl_rmain.c +++ b/Quake/gl_rmain.c @@ -335,8 +335,7 @@ void GL_PostProcess (void) GL_BindNative (GL_TEXTURE0, GL_TEXTURE_2D, framebufs.composite.color_tex); GL_BindNative (GL_TEXTURE1, GL_TEXTURE_3D, gl_palette_lut); GL_BindBufferRange (GL_SHADER_STORAGE_BUFFER, 0, gl_palette_buffer[palidx], 0, 256 * sizeof (GLuint)); - if (variant != 2) // some AMD drivers optimize out the uniform in variant #2 - GL_Uniform3fFunc (0, vid_gamma.value, q_min(2.0f, q_max(1.0f, vid_contrast.value)), 1.f/r_refdef.scale); + GL_Uniform4fFunc (0, vid_gamma.value, q_min(2.0f, q_max(1.0f, vid_contrast.value)), 1.f/r_refdef.scale, glx); glDrawArrays (GL_TRIANGLES, 0, 3); @@ -850,7 +849,7 @@ void R_SetupGL (void) GL_BindFramebufferFunc (GL_FRAMEBUFFER, GL_NeedsPostprocess () ? framebufs.composite.fbo : 0u); framesetup.scene_fbo = framebufs.composite.fbo; framesetup.oit_fbo = framebufs.oit.fbo_composite; - glViewport (glx + r_refdef.vrect.x, gly + glheight - r_refdef.vrect.y - r_refdef.vrect.height, r_refdef.vrect.width, r_refdef.vrect.height); + glViewport (r_refdef.vrect.x, glheight - r_refdef.vrect.y - r_refdef.vrect.height, r_refdef.vrect.width, r_refdef.vrect.height); } else { @@ -1525,8 +1524,13 @@ void R_WarpScaleView (void) if (!GL_NeedsSceneEffects ()) return; - srcx = glx + r_refdef.vrect.x; - srcy = gly + glheight - r_refdef.vrect.y - r_refdef.vrect.height; + srcx = r_refdef.vrect.x; + srcy = glheight - r_refdef.vrect.y - r_refdef.vrect.height; + if (!GL_NeedsPostprocess ()) + { + srcx += glx; + srcy += gly; + } srcw = r_refdef.vrect.width / r_refdef.scale; srch = r_refdef.vrect.height / r_refdef.scale; diff --git a/Quake/gl_shaders.h b/Quake/gl_shaders.h index 31e7816aa..a09bb44f4 100644 --- a/Quake/gl_shaders.h +++ b/Quake/gl_shaders.h @@ -242,7 +242,7 @@ static const char postprocess_fragment_shader[] = PALETTE_BUFFER NOISE_FUNCTIONS "\n" -"layout(location=0) uniform vec3 Params;\n" +"layout(location=0) uniform vec4 Params;\n" "\n" "layout(location=0) out vec4 out_fragcolor;\n" "\n" @@ -251,7 +251,8 @@ NOISE_FUNCTIONS " float gamma = Params.x;\n" " float contrast = Params.y;\n" " float scale = Params.z;\n" -" out_fragcolor = texelFetch(GammaTexture, ivec2(gl_FragCoord), 0);\n" +" ivec2 texelpos = ivec2(gl_FragCoord.x-Params.w, gl_FragCoord.y);\n" +" out_fragcolor = texelFetch(GammaTexture, texelpos, 0);\n" "#if PALETTIZE == 1\n" " vec2 noiseuv = floor(gl_FragCoord.xy * scale) + 0.5;\n" " out_fragcolor.rgb = sqrt(out_fragcolor.rgb);\n" diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index ed406da79..631cae465 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -145,6 +145,7 @@ cvar_t vid_fsaa = {"vid_fsaa", "0", CVAR_ARCHIVE}; // QuakeSpasm cvar_t vid_fsaamode = {"vid_fsaamode", "0", CVAR_ARCHIVE}; cvar_t vid_desktopfullscreen = {"vid_desktopfullscreen", "0", CVAR_ARCHIVE}; // QuakeSpasm cvar_t vid_borderless = {"vid_borderless", "0", CVAR_ARCHIVE}; // QuakeSpasm +cvar_t vid_maxaspect = { "vid_maxaspect", "0", CVAR_ARCHIVE }; //johnfitz cvar_t vid_saveresize = {"vid_saveresize", "0", CVAR_ARCHIVE}; @@ -363,6 +364,29 @@ static qboolean VID_ValidMode (int width, int height, int refreshrate, qboolean return true; } +static float VID_GetAspectRatioCVarValue (cvar_t* cvar) +{ + float aspect = cvar->value; + if (cvar->string && *cvar->string) + { + float num, denom; + if (sscanf (cvar->string, "%f:%f", &num, &denom) == 2) + if (num && denom) + aspect = num / denom; + } + return aspect; +} + +static void VID_UpdateRes () +{ + float maxaspect = VID_GetAspectRatioCVarValue (&vid_maxaspect); + + vid.width = VID_GetCurrentWidth (); + vid.height = VID_GetCurrentHeight (); + + if (maxaspect > 0) + vid.width = q_min (vid.width, (int)floor (vid.height*maxaspect+0.5f)); +} /* ================ VID_SetMode @@ -488,8 +512,7 @@ static qboolean VID_SetMode (int width, int height, int refreshrate, qboolean fu } } - vid.width = VID_GetCurrentWidth(); - vid.height = VID_GetCurrentHeight(); + VID_UpdateRes (); vid.maxscale = q_max (4, vid.height / 240); vid.refreshrate = VID_GetCurrentRefreshRate(); vid.conwidth = vid.width & 0xFFFFFFF8; @@ -609,6 +632,11 @@ void VID_Changed_f (cvar_t *var) vid_changed = true; } +void VID_Resized_f (cvar_t* var) +{ + vid.resized = true; +} + void VID_RecalcConsoleSize (void) { vid.conwidth = (scr_conwidth.value > 0) ? (int)scr_conwidth.value : (scr_conscale.value > 0) ? (int)(vid.guiwidth/scr_conscale.value) : vid.guiwidth; @@ -619,18 +647,9 @@ void VID_RecalcConsoleSize (void) void VID_RecalcInterfaceSize (void) { - vid.guipixelaspect = 1.f; - if (scr_pixelaspect.string && *scr_pixelaspect.string) - { - float num, denom; - if (sscanf (scr_pixelaspect.string, "%f:%f", &num, &denom) == 2) - { - if (num && denom) - vid.guipixelaspect = CLAMP (0.5f, num / denom, 2.f); - } - else if (scr_pixelaspect.value) - vid.guipixelaspect = CLAMP (0.5f, scr_pixelaspect.value, 2.f); - } + vid.guipixelaspect = VID_GetAspectRatioCVarValue(&scr_pixelaspect); + vid.guipixelaspect = CLAMP (0.5f, vid.guipixelaspect, 2.f); + vid.guiwidth = vid.width / q_max (vid.guipixelaspect, 1.f); vid.guiheight = vid.height * q_min (vid.guipixelaspect, 1.f); if (vid.width && vid.height) @@ -1273,6 +1292,7 @@ void GL_BeginRendering (int *x, int *y, int *width, int *height) { vid.resized = false; vid.recalc_refdef = true; + VID_UpdateRes (); if (vid_saveresize.value) { qboolean was_locked = vid_locked; @@ -1290,6 +1310,16 @@ void GL_BeginRendering (int *x, int *y, int *width, int *height) *width = vid.width; *height = vid.height; + if (vid.width != VID_GetCurrentWidth ()) + { + // center within window if vid width is less than window width. + *x = (VID_GetCurrentWidth ()-vid.width)/2; + + // if vid width is not equal to window width the window needs to be cleared. + glClearColor (0, 0, 0, 0); + glClear (GL_COLOR_BUFFER_BIT); + } + // reset state/bindings, just in case some other process // injects code that makes changes without cleaning up GL_ResetState (); @@ -1476,6 +1506,7 @@ void VID_Init (void) Cvar_RegisterVariable (&vid_fsaamode); Cvar_RegisterVariable (&vid_desktopfullscreen); //QuakeSpasm Cvar_RegisterVariable (&vid_borderless); //QuakeSpasm + Cvar_RegisterVariable (&vid_maxaspect); Cvar_RegisterVariable (&vid_saveresize); Cvar_SetCallback (&vid_fullscreen, VID_Changed_f); Cvar_SetCallback (&vid_width, VID_Changed_f); @@ -1486,6 +1517,7 @@ void VID_Init (void) Cvar_SetCallback (&vid_fsaamode, VID_FSAAMode_f); Cvar_SetCallback (&vid_desktopfullscreen, VID_Changed_f); Cvar_SetCallback (&vid_borderless, VID_Changed_f); + Cvar_SetCallback (&vid_maxaspect, VID_Resized_f); Cvar_RegisterVariable (&gl_texture_anisotropy); Cvar_SetCallback (&gl_texture_anisotropy, &TexMgr_Anisotropy_f); diff --git a/Quake/in_sdl.c b/Quake/in_sdl.c index 5db48b319..17a546962 100644 --- a/Quake/in_sdl.c +++ b/Quake/in_sdl.c @@ -894,8 +894,6 @@ void IN_SendKeyEvents (void) } else if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { - vid.width = event.window.data1; - vid.height = event.window.data2; vid.resized = true; } break; diff --git a/Quake/menu.c b/Quake/menu.c index 53d2a1869..1d84270d7 100644 --- a/Quake/menu.c +++ b/Quake/menu.c @@ -45,7 +45,9 @@ extern cvar_t vid_width; extern cvar_t vid_height; extern cvar_t vid_refreshrate; extern cvar_t vid_fullscreen; +extern cvar_t vid_desktopfullscreen; extern cvar_t vid_borderless; +extern cvar_t vid_maxaspect; extern cvar_t vid_vsync; extern cvar_t vid_fsaamode; extern cvar_t vid_fsaa; @@ -2620,22 +2622,44 @@ static void VID_Menu_ChooseNextRate (int dir) typedef enum { DISPLAYMODE_FULLSCREEN, + DISPLAYMODE_DESKTOPFULLSCREEN, DISPLAYMODE_WINDOWED, DISPLAYMODE_BORDERLESS, DISPLAYMODE_COUNT, } windowmode_t; +static windowmode_t displaymode; -static windowmode_t VID_Menu_GetDisplayMode (void) +static void VID_Menu_InitDisplayMode (void) { if (vid_fullscreen.value) - return DISPLAYMODE_FULLSCREEN; - return vid_borderless.value ? DISPLAYMODE_BORDERLESS : DISPLAYMODE_WINDOWED; + displaymode = vid_desktopfullscreen.value ? DISPLAYMODE_DESKTOPFULLSCREEN : DISPLAYMODE_FULLSCREEN; + else + displaymode = vid_borderless.value ? DISPLAYMODE_BORDERLESS : DISPLAYMODE_WINDOWED; +} + +static windowmode_t VID_Menu_GetDisplayMode (void) +{ + return displaymode; } static void VID_Menu_SetDisplayMode (windowmode_t mode) { - Cvar_SetValueQuick (&vid_fullscreen, mode == DISPLAYMODE_FULLSCREEN); + displaymode = mode; +} + +static void VID_Menu_ApplyDisplayMode (void) +{ + windowmode_t mode = VID_Menu_GetDisplayMode (); + + Cvar_SetValueQuick (&vid_fullscreen, mode == DISPLAYMODE_FULLSCREEN || mode == DISPLAYMODE_DESKTOPFULLSCREEN); + + // preserve vid_desktopfullscreen unless explicitly setting a fullscreen mode. + if (mode == DISPLAYMODE_FULLSCREEN) + Cvar_SetValueQuick (&vid_desktopfullscreen, 0); + else if (mode == DISPLAYMODE_DESKTOPFULLSCREEN) + Cvar_SetValueQuick (&vid_desktopfullscreen, 1); + if (mode != DISPLAYMODE_FULLSCREEN) Cvar_SetValueQuick (&vid_borderless, mode == DISPLAYMODE_BORDERLESS); } @@ -2654,6 +2678,45 @@ static void VID_Menu_ChooseNextDisplayMode (int dir) VID_Menu_SetDisplayMode (mode); } +static char* aspectratiovalues[] = { "0", "4:3", "16:9" }; +static int VID_Menu_GetMaxAspectRatioMenuStringIndex () +{ + int i; + int index = -1; + for (i = 0; i < countof (aspectratiovalues); i++) + { + if (vid_maxaspect.string && strcmp (vid_maxaspect.string, aspectratiovalues[i]) == 0) + { + index = i; + break; + } + } + return index; +} + +/* +================ +VID_Menu_ChooseNextMaxAspectRatio + +chooses next max aspect ratio, then updates vid_maxaspect cvar +================ +*/ +static void VID_Menu_ChooseNextMaxAspectRatio (int dir) +{ + int index = VID_Menu_GetMaxAspectRatioMenuStringIndex (); + if (index < 0) + index = 0; + else + { + index = (index+dir); + if (index < 0) + index = countof (aspectratiovalues)-1; + else + index = index%countof (aspectratiovalues); + } + Cvar_SetQuick (&vid_maxaspect, aspectratiovalues[index]); +} + /* ================ VID_Menu_ChooseNextAA @@ -2963,6 +3026,7 @@ void M_Menu_Video_f (void) \ def (VID_OPT_SPACE1, "") \ \ + def (VID_OPT_MAXASPECT, "Aspect Ratio") \ def (VID_OPT_VSYNC, "Vertical Sync") \ def (VID_OPT_FSAA, "Antialiasing") \ def (VID_OPT_FSAA_MODE, "AA Mode") \ @@ -3120,6 +3184,9 @@ void M_Options_Init (enum m_state_e state) //set up rate list based on current cvars VID_Menu_RebuildRateList (); + + // set up the display mode selector. + VID_Menu_InitDisplayMode (); } else { @@ -3330,7 +3397,9 @@ void M_AdjustSliders (int dir) // Video options // case VID_OPT_RESOLUTION: - VID_Menu_ChooseNextResolution (-dir); + // cannot change desktopfullscreen resolution. + if (VID_Menu_GetDisplayMode () != DISPLAYMODE_DESKTOPFULLSCREEN) + VID_Menu_ChooseNextResolution (-dir); break; case VID_OPT_REFRESHRATE: VID_Menu_ChooseNextRate (-dir); @@ -3338,6 +3407,9 @@ void M_AdjustSliders (int dir) case VID_OPT_DISPLAYMODE: VID_Menu_ChooseNextDisplayMode (-dir); break; + case VID_OPT_MAXASPECT: + VID_Menu_ChooseNextMaxAspectRatio (-dir); + break; case VID_OPT_VSYNC: Cbuf_AddText ("toggle vid_vsync\n"); // kristian break; @@ -3650,7 +3722,11 @@ static void M_Options_DrawItem (int y, int item) // Video Options // case VID_OPT_RESOLUTION: - M_Print (x, y, va("%i x %i", (int)vid_width.value, (int)vid_height.value)); + // desktopfullscreen resolution is always the displays native resolution. + if (VID_Menu_GetDisplayMode () == DISPLAYMODE_DESKTOPFULLSCREEN) + M_Print (x, y, va ("Native")); + else + M_Print (x, y, va ("%i x %i", (int)vid_width.value, (int)vid_height.value)); break; case VID_OPT_REFRESHRATE: M_Print (x, y, va("%i Hz", (int)vid_refreshrate.value)); @@ -3658,12 +3734,20 @@ static void M_Options_DrawItem (int y, int item) case VID_OPT_DISPLAYMODE: switch (VID_Menu_GetDisplayMode ()) { - case DISPLAYMODE_FULLSCREEN: M_Print (x, y, "Fullscreen"); break; - case DISPLAYMODE_WINDOWED: M_Print (x, y, "Windowed"); break; - case DISPLAYMODE_BORDERLESS: M_Print (x, y, "Borderless"); break; - default: M_Print (x, y, "Other"); break; + case DISPLAYMODE_FULLSCREEN: M_Print (x, y, "Fullscreen"); break; + case DISPLAYMODE_DESKTOPFULLSCREEN: M_Print (x, y, "DesktopFullscreen"); break; + case DISPLAYMODE_WINDOWED: M_Print (x, y, "Windowed"); break; + case DISPLAYMODE_BORDERLESS: M_Print (x, y, "Borderless"); break; + default: M_Print (x, y, "Other"); break; } break; + case VID_OPT_MAXASPECT: + { + int index = VID_Menu_GetMaxAspectRatioMenuStringIndex (); + char* text = index < 0 ? "Custom" : (index == 0 ? "Auto" : aspectratiovalues[index]); + M_Print (x, y, text); + break; + } case VID_OPT_VSYNC: M_DrawCheckbox (x, y, (int)vid_vsync.value); break; @@ -3830,6 +3914,7 @@ void M_Options_Key (int k) Cbuf_AddText ("vid_test\n"); break; case VID_OPT_APPLY: + VID_Menu_ApplyDisplayMode (); Cbuf_AddText ("vid_restart\n"); key_dest = key_game; m_state = m_none; From 9a5904a69cd9bba88dcdd008bf01a69a0ea64cfb Mon Sep 17 00:00:00 2001 From: Kurt Loeffler Date: Sun, 2 Apr 2023 20:31:00 -0700 Subject: [PATCH 2/4] Add comments to VID_GetAspectRatioCVarValue and VID_UpdateRes. --- Quake/gl_vidsdl.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index 631cae465..ed5f2fe13 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -364,6 +364,15 @@ static qboolean VID_ValidMode (int width, int height, int refreshrate, qboolean return true; } +/* +================ +VID_GetAspectRatioCVarValue + +get an aspect ratio from a cvar. +if the cvar string in in the format x:y, such as 16:9, the fractional float aspect ratio is calculate. +otherwise the cvars float value is returned. +================ +*/ static float VID_GetAspectRatioCVarValue (cvar_t* cvar) { float aspect = cvar->value; @@ -377,6 +386,13 @@ static float VID_GetAspectRatioCVarValue (cvar_t* cvar) return aspect; } +/* +================ +VID_UpdateRes + +setup vid.width and vid.height and account for vid_maxaspect. +================ +*/ static void VID_UpdateRes () { float maxaspect = VID_GetAspectRatioCVarValue (&vid_maxaspect); @@ -387,6 +403,7 @@ static void VID_UpdateRes () if (maxaspect > 0) vid.width = q_min (vid.width, (int)floor (vid.height*maxaspect+0.5f)); } + /* ================ VID_SetMode From b66a91c3472f3ecf59aade383b7d628895ed8653 Mon Sep 17 00:00:00 2001 From: Kurt Loeffler Date: Sun, 2 Apr 2023 20:36:47 -0700 Subject: [PATCH 3/4] Fix some comment typos. --- Quake/gl_vidsdl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index ed5f2fe13..144e5a667 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -369,7 +369,7 @@ static qboolean VID_ValidMode (int width, int height, int refreshrate, qboolean VID_GetAspectRatioCVarValue get an aspect ratio from a cvar. -if the cvar string in in the format x:y, such as 16:9, the fractional float aspect ratio is calculate. +if the cvar string is in the format x:y, such as 16:9, the fractional float aspect ratio is calculated. otherwise the cvars float value is returned. ================ */ From dbab1a476878600e59e818785aafb9886f3564ab Mon Sep 17 00:00:00 2001 From: Kurt Loeffler Date: Mon, 3 Apr 2023 00:33:24 -0700 Subject: [PATCH 4/4] Fixed issue with offset ui/world rendering when vid_maxaspect is used. Under some configurations where GL_NeedsSceneEffects and GL_NeedsPostprocess return different values the viewport offset was not configured correctly. This change addresses this issue. --- Quake/gl_draw.c | 5 ++++- Quake/gl_rmain.c | 13 +++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Quake/gl_draw.c b/Quake/gl_draw.c index 6e49b96ff..cf328e758 100644 --- a/Quake/gl_draw.c +++ b/Quake/gl_draw.c @@ -1193,7 +1193,10 @@ void GL_Set2D (void) glcanvas.type = CANVAS_INVALID; glcanvas.texture = NULL; glcanvas.blendmode = GLS_BLEND_ALPHA; - glViewport (0, 0, glwidth, glheight); + if (GL_NeedsPostprocess ()) + glViewport (0, 0, glwidth, glheight); + else + glViewport (glx, gly, glwidth, glheight); GL_SetCanvas (CANVAS_DEFAULT); GL_SetCanvasColor (1.f, 1.f, 1.f, 1.f); } diff --git a/Quake/gl_rmain.c b/Quake/gl_rmain.c index 4a9be0fbc..f9c54355d 100644 --- a/Quake/gl_rmain.c +++ b/Quake/gl_rmain.c @@ -846,10 +846,19 @@ void R_SetupGL (void) { if (!GL_NeedsSceneEffects ()) { - GL_BindFramebufferFunc (GL_FRAMEBUFFER, GL_NeedsPostprocess () ? framebufs.composite.fbo : 0u); + qboolean needpostprocess = GL_NeedsPostprocess (); + int xoffset = 0; + int yoffset = 0; + if (!needpostprocess) + { + xoffset = glx; + yoffset = gly; + } + + GL_BindFramebufferFunc (GL_FRAMEBUFFER, needpostprocess ? framebufs.composite.fbo : 0u); framesetup.scene_fbo = framebufs.composite.fbo; framesetup.oit_fbo = framebufs.oit.fbo_composite; - glViewport (r_refdef.vrect.x, glheight - r_refdef.vrect.y - r_refdef.vrect.height, r_refdef.vrect.width, r_refdef.vrect.height); + glViewport (xoffset+r_refdef.vrect.x, yoffset+glheight - r_refdef.vrect.y - r_refdef.vrect.height, r_refdef.vrect.width, r_refdef.vrect.height); } else {