HighDPI support, hopefully

This commit is contained in:
Daniel Gibson 2024-06-03 11:55:49 +02:00
parent 28d81ed5f5
commit 5376c6d74c
6 changed files with 84 additions and 16 deletions

View file

@ -1,5 +1,7 @@
#ifndef IMGUI_DISABLE
#include <SDL.h> // 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];

View file

@ -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

View file

@ -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();

View file

@ -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);

View file

@ -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) {

View file

@ -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);