diff --git a/Quake/gl_draw.c b/Quake/gl_draw.c index b8014dfcd..d641fe2e6 100644 --- a/Quake/gl_draw.c +++ b/Quake/gl_draw.c @@ -1206,7 +1206,10 @@ void GL_Set2D (void) glcanvas.type = CANVAS_INVALID; glcanvas.texture = NULL; glcanvas.blendmode = GLS_BLEND_ALPHA; - glViewport (glx, gly, 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 1baca521d..ae4c07237 100644 --- a/Quake/gl_rmain.c +++ b/Quake/gl_rmain.c @@ -338,8 +338,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,10 +849,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 (glx + r_refdef.vrect.x, gly + 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 { @@ -1750,8 +1758,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 b23da70ff..15d00aa3b 100644 --- a/Quake/gl_shaders.h +++ b/Quake/gl_shaders.h @@ -245,7 +245,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" @@ -254,7 +254,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 35ee24997..2d57787cf 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -148,6 +148,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}; @@ -410,6 +411,46 @@ 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 is in the format x:y, such as 16:9, the fractional float aspect ratio is calculated. +otherwise the cvars float value is returned. +================ +*/ +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; +} + +/* +================ +VID_UpdateRes + +setup vid.width and vid.height and account for vid_maxaspect. +================ +*/ +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 @@ -535,8 +576,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; @@ -656,6 +696,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; @@ -666,18 +711,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) @@ -1319,6 +1355,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; @@ -1336,6 +1373,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 (); @@ -1580,6 +1627,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); @@ -1590,6 +1638,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_SetCompletion (&vid_width, VID_Width_Completion_f); Cvar_SetCompletion (&vid_height, VID_Height_Completion_f); diff --git a/Quake/in_sdl.c b/Quake/in_sdl.c index 7b8013994..751f96d2f 100644 --- a/Quake/in_sdl.c +++ b/Quake/in_sdl.c @@ -1242,8 +1242,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 e43886c7e..4b8669b93 100644 --- a/Quake/menu.c +++ b/Quake/menu.c @@ -47,7 +47,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; @@ -2778,22 +2780,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); } @@ -2812,6 +2836,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 @@ -3230,6 +3293,7 @@ void M_Menu_Gamepad_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") \ @@ -3455,6 +3519,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 if (state == m_gamepad) { @@ -3676,7 +3743,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); @@ -3684,6 +3753,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; @@ -4107,7 +4179,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)); @@ -4115,12 +4191,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; @@ -4382,6 +4466,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;