From 5376c6d74cb34cdb5849434cbccaf740afbd0680 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Mon, 3 Jun 2024 11:55:49 +0200 Subject: [PATCH] HighDPI support, hopefully --- neo/framework/Dhewm3SettingsMenu.cpp | 18 ++++++++++++++++++ neo/renderer/RenderSystem.h | 22 ++++++++++++++++++++++ neo/renderer/RenderSystem_init.cpp | 11 ++++++++--- neo/sys/glimp.cpp | 28 +++++++++++++++++++++------- neo/sys/sys_imgui.cpp | 9 +++++++-- neo/ui/UserInterface.cpp | 12 ++++++++---- 6 files changed, 84 insertions(+), 16 deletions(-) diff --git a/neo/framework/Dhewm3SettingsMenu.cpp b/neo/framework/Dhewm3SettingsMenu.cpp index 158834ce..2a61c98d 100644 --- a/neo/framework/Dhewm3SettingsMenu.cpp +++ b/neo/framework/Dhewm3SettingsMenu.cpp @@ -1,5 +1,7 @@ #ifndef IMGUI_DISABLE +#include // to show display size + #define IMGUI_DEFINE_MATH_OPERATORS #include "Common.h" @@ -1833,6 +1835,22 @@ static void DrawVideoOptionsMenu() AddTooltip( "r_customWidth / r_customHeight" ); } } + int sdlDisplayIdx = SDL_GetWindowDisplayIndex( SDL_GL_GetCurrentWindow() ); + SDL_Rect displayRect = {}; + SDL_GetDisplayBounds( sdlDisplayIdx, &displayRect ); + if ( (int)glConfig.winWidth != glConfig.vidWidth ) { + ImGui::TextDisabled( "Current Resolution: %g x %g (Physical: %d x %d)", + glConfig.winWidth, glConfig.winHeight, glConfig.vidWidth, glConfig.vidHeight ); + AddDescrTooltip( "Apparently your system is using a HighDPI mode, where the logical resolution (used to specify" + " window sizes) is lower than the physical resolution (number of pixels actually rendered)." ); + float scale = float(glConfig.vidWidth)/glConfig.winWidth; + int pw = scale * displayRect.w; + int ph = scale * displayRect.h; + ImGui::TextDisabled( "Display Size: %d x %d (Physical: %d x %d)", displayRect.w, displayRect.h, pw, ph ); + } else { + ImGui::TextDisabled( "Current Resolution: %d x %d", glConfig.vidWidth, glConfig.vidHeight ); + ImGui::TextDisabled( "Display Size: %d x %d", displayRect.w, displayRect.h ); + } static const char* msaaLevels[] = { "No Antialiasing", "2x", "4x", "8x", "16x" }; const char* curLvl = msaaLevels[msaaModeIndex]; diff --git a/neo/renderer/RenderSystem.h b/neo/renderer/RenderSystem.h index b57aad93..2972ba26 100644 --- a/neo/renderer/RenderSystem.h +++ b/neo/renderer/RenderSystem.h @@ -79,6 +79,7 @@ typedef struct glconfig_s { bool textureNonPowerOfTwoAvailable; bool depthBoundsTestAvailable; + // GL framebuffer size, see also winWidth and winHeight int vidWidth, vidHeight; // passed to R_BeginFrame int displayFrequency; @@ -92,6 +93,23 @@ typedef struct glconfig_s { // DG: current video backend is known to need opaque default framebuffer // used if r_fillWindowAlphaChan == -1 bool shouldFillWindowAlpha; + + // For some reason people decided that we need displays with ultra small pixels, + // so everything rendered on them must be scaled up to be legible. + // unfortunately, this bullshit feature was "improved" upon by deciding that the best + // way to implement "High DPI" was to pretend that windows have fewer pixels than they + // actually do, so the window size you get and mouse coordinates in them etc + // are in e.g. 1024x768, while the physical window size is e.g. 1536x1152 pixels + // (when the scaling factor is 1.5), and ideally the GL framebuffer has the physical + // window size so things still look crisp. + // Of course the reasonable solution would be to go back and time and nuke Cupertino, + // where this nonsense scheme was invented, but as I lack the necessary funds, + // I reluctantly add winWidth and winHeight and adjust the code that deals with window + // coordinates, as far as that's possible.. + // (Isn't it fun that you have a 2256x1504 display, tell SDL to create a 1920x1080 window + // and you get one that's much bigger and doesn't fit on the screen?) + + float winWidth, winHeight; // logical window size (different to vidWidth/height in HighDPI cases) } glconfig_t; @@ -171,7 +189,11 @@ public: virtual bool IsOpenGLRunning( void ) const = 0; virtual bool IsFullScreen( void ) const = 0; + // NOTE: this is the physical width of the GL drawable (framebuffer) in pixels, + // *not* the logical window size (in case of HighDPI that's not the same!) virtual int GetScreenWidth( void ) const = 0; + // NOTE: this is the physical height of the GL drawable (framebuffer) in pixels, + // *not* the logical window size (in case of HighDPI that's not the same!) virtual int GetScreenHeight( void ) const = 0; // allocate a renderWorld to be used for drawing diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index 77ca9c65..8a82b486 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -1914,6 +1914,10 @@ static void GfxInfo_f( const idCmdArgs &args ) { "fullscreen" }; + const char* fsmode = fsstrings[r_fullscreen.GetBool()]; + if ( r_fullscreen.GetBool() && r_fullscreenDesktop.GetBool() ) + fsmode = "desktop-fullscreen"; + common->Printf( "\nGL_VENDOR: %s\n", glConfig.vendor_string ); common->Printf( "GL_RENDERER: %s\n", glConfig.renderer_string ); common->Printf( "GL_VERSION: %s\n", glConfig.version_string ); @@ -1923,13 +1927,14 @@ static void GfxInfo_f( const idCmdArgs &args ) { common->Printf( "GL_MAX_TEXTURE_COORDS_ARB: %d\n", glConfig.maxTextureCoords ); common->Printf( "GL_MAX_TEXTURE_IMAGE_UNITS_ARB: %d\n", glConfig.maxTextureImageUnits ); common->Printf( "\nPIXELFORMAT: color(%d-bits) Z(%d-bit) stencil(%d-bits)\n", glConfig.colorBits, glConfig.depthBits, glConfig.stencilBits ); - common->Printf( "MODE: %d, %d x %d %s hz:", r_mode.GetInteger(), glConfig.vidWidth, glConfig.vidHeight, fsstrings[r_fullscreen.GetBool()] ); + common->Printf( "MODE: %d, %d x %d %s hz:", r_mode.GetInteger(), glConfig.vidWidth, glConfig.vidHeight, fsmode ); if ( glConfig.displayFrequency ) { common->Printf( "%d\n", glConfig.displayFrequency ); } else { common->Printf( "N/A\n" ); } + common->Printf( "Logical Window size: %g x %g\n", glConfig.winWidth, glConfig.winHeight ); const char *active[2] = { "", " (ACTIVE)" }; @@ -2033,8 +2038,8 @@ void R_VidRestart_f( const idCmdArgs &args ) { globalImages->ReloadAllImages(); } else { glimpParms_t parms; - parms.width = glConfig.vidWidth; - parms.height = glConfig.vidHeight; + parms.width = glConfig.winWidth; // DG: HighDPI adjustment: explicitly use window size + parms.height = glConfig.winHeight; parms.fullScreen = ( forceWindow ) ? false : r_fullscreen.GetBool(); parms.displayHz = r_displayRefresh.GetInteger(); parms.multiSamples = r_multiSamples.GetInteger(); diff --git a/neo/sys/glimp.cpp b/neo/sys/glimp.cpp index 6554723b..df501c3c 100644 --- a/neo/sys/glimp.cpp +++ b/neo/sys/glimp.cpp @@ -165,6 +165,8 @@ bool GLimp_Init(glimpParms_t parms) { } #if SDL_VERSION_ATLEAST(2, 0, 0) + flags |= SDL_WINDOW_ALLOW_HIGHDPI; + /* Doom3 has the nasty habit of modifying the default framebuffer's alpha channel and then * relying on those modifications in blending operations (using GL_DST_(ONE_MINUS_)ALPHA). * So far that hasn't been much of a problem, because Windows, macOS, X11 etc @@ -272,14 +274,14 @@ try_again: #if SDL_VERSION_ATLEAST(2, 0, 0) - const char* windowMode = ""; - if(r_fullscreen.GetBool()) { - windowMode = r_fullscreenDesktop.GetBool() ? "desktop-fullscreen-" : "fullscreen-"; + if ( r_fullscreen.GetBool() && r_fullscreenDesktop.GetBool() ) { + common->Printf( "Will create a pseudo-fullscreen window at the current desktop resolution\n" ); + } else { + const char* windowMode = r_fullscreen.GetBool() ? "fullscreen-" : ""; + common->Printf("Will create a %swindow with resolution %dx%d (r_mode = %d)\n", + windowMode, parms.width, parms.height, r_mode.GetInteger()); } - common->Printf("Will create a %swindow with resolution %dx%d (r_mode = %d)\n", - windowMode, parms.width, parms.height, r_mode.GetInteger()); - int displayIndex = 0; #if SDL_VERSION_ATLEAST(2, 0, 4) // try to put the window on the display the mousecursor currently is on @@ -407,11 +409,23 @@ try_again: GLimp_SetSwapInterval( r_swapInterval.GetInteger() ); r_swapInterval.ClearModified(); - SDL_GetWindowSize(window, &glConfig.vidWidth, &glConfig.vidHeight); + // for HighDPI, window size and drawable size can differ + int ww=0, wh=0; + SDL_GetWindowSize(window, &ww, &wh); + glConfig.winWidth = ww; + glConfig.winHeight = wh; + SDL_GL_GetDrawableSize(window, &glConfig.vidWidth, &glConfig.vidHeight); SetSDLIcon(); // for SDL2 this must be done after creating the window glConfig.isFullscreen = (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN; + const char* fsStr = glConfig.isFullscreen ? "fullscreen " : ""; + if ( ww != glConfig.vidWidth ) { + common->Printf( "Got a HighDPI %swindow with physical resolution %d x %d and virtual resolution %d x %d\n", + fsStr, glConfig.vidWidth, glConfig.vidHeight, ww, wh ); + } else { + common->Printf( "Got a %swindow with resolution %d x %d\n", fsStr, ww, wh ); + } #else SDL_WM_SetCaption(ENGINE_VERSION, ENGINE_VERSION); diff --git a/neo/sys/sys_imgui.cpp b/neo/sys/sys_imgui.cpp index 665f0307..1babc55b 100644 --- a/neo/sys/sys_imgui.cpp +++ b/neo/sys/sys_imgui.cpp @@ -187,6 +187,11 @@ static float GetDefaultDPI() static float GetDefaultScale() { + if ( glConfig.winWidth != glConfig.vidWidth ) { + // in HighDPI mode, the font sizes are already scaled (to window coordinates), apparently + return 1.0f; + } + // TODO: different reference DPI on mac? also, doesn't work that well on my laptop.. float ret = GetDefaultDPI() / 96.0f; ret = round(ret*2.0)*0.5; // round to .0 or .5 return ret; @@ -464,8 +469,8 @@ bool ShouldShowCursor() // in a black bar (Doom3 cursor is not drawn there), show the ImGui cursor if ( r_scaleMenusTo43.GetBool() ) { ImVec2 mousePos = ImGui::GetMousePos(); - float w = renderSystem->GetScreenWidth(); - float h = renderSystem->GetScreenHeight(); + float w = glConfig.winWidth; + float h = glConfig.winHeight; float aspectRatio = w/h; static const float virtualAspectRatio = float(VIRTUAL_WIDTH)/float(VIRTUAL_HEIGHT); // 4:3 = 1.333 if(aspectRatio > 1.4f) { diff --git a/neo/ui/UserInterface.cpp b/neo/ui/UserInterface.cpp index 8c149628..7cbed1ed 100644 --- a/neo/ui/UserInterface.cpp +++ b/neo/ui/UserInterface.cpp @@ -36,6 +36,8 @@ If you have questions concerning this license or the applicable additional terms #include "ui/UserInterfaceLocal.h" +#include "renderer/tr_local.h" // glConfig for winWidth/winHeight + extern idCVar r_skipGuiShaders; // 1 = don't render any gui elements on surfaces extern idCVar r_scaleMenusTo43; // DG: for the "scale menus to 4:3" hack @@ -355,12 +357,14 @@ const char *idUserInterfaceLocal::HandleEvent( const sysEvent_t *event, int _tim // DG: this is a fullscreen GUI, scale the mousedelta added to cursorX/Y // by 640/w, because the GUI pretends that everything is 640x480 // even if the actual resolution is higher => mouse moved too fast - float w = renderSystem->GetScreenWidth(); - float h = renderSystem->GetScreenHeight(); + float w = glConfig.winWidth; + float h = glConfig.winHeight; if( w <= 0.0f || h <= 0.0f ) { w = VIRTUAL_WIDTH; h = VIRTUAL_HEIGHT; } + const float realW = w; + const float realH = h; if(r_scaleMenusTo43.GetBool()) { // in case we're scaling menus to 4:3, we need to take that into account @@ -387,8 +391,8 @@ const char *idUserInterfaceLocal::HandleEvent( const sysEvent_t *event, int _tim // Note: In case of scaling to 4:3, w and h are already scaled down // to the 4:3 size that fits into the real resolution. // Otherwise xOffset/yOffset will just be 0 - float xOffset = (renderSystem->GetScreenWidth() - w) * 0.5f; - float yOffset = (renderSystem->GetScreenHeight() - h) * 0.5f; + float xOffset = (realW - w) * 0.5f; + float yOffset = (realH - h) * 0.5f; // offset the mouse coordinates into 4:3 area and scale down to 640x480 // yes, result could be negative, doesn't matter, code below checks that anyway cursorX = (event->evValue - xOffset) * (float(VIRTUAL_WIDTH)/w);