per-monitor DPI awareness

Is there any way to detect current display DPI? For example, I have one 4K monitor with 200% scale and 300dpi and a 1080p with 100% scale with 96dpi. I want to know which monitor I am currently on. I can detect dpi of the primary display, but that’s about it.

I’m not sure about a platform independent way, but with Windows you can query these things from the win32 API. It looks like you can get the dpi for a monitor from the following…

Now, as you pointed out, the question becomes how to find which monitor you’re on. That I’m not sure about… you might be able to do it from window screen coordinates. I wrote something many years ago that would build up a map of your screens, relative to each other, similar to what you see when you go to your display settings, but I can’t remember the specifics. I think I was getting the bulk of the info from .NET calls, but I think there were others… maybe this one?

Anyway, I know that doesn’t answer directly, but maybe it gives you some things to check out, if you haven’t found them already.

Welp, this is the solution. Incredibly hacky, but seems to work.

public void Refresh()
	{
		var _dpiList = new List<int>();
		var _boundsList = new List<System.Drawing.Rectangle>();

		// There is no good way of determining the dpi pf the current display.
		// So, we are taking the dpi data from ALL the available diaplys and then checking
		// the window position against each screen's bounds to emulate the dpi. Holy shit. 
		foreach (var screen in System.Windows.Forms.Screen.AllScreens)
		{
			GetDpi(screen, DpiType.Effective, out var dpiX, out _);
			_dpiList.Add((int)dpiX);
			_boundsList.Add(screen.Bounds);
		}

		var windowPos = Monofoxe.Engine.GameMgr.WindowManager.WindowPosision.ToVector2()
			+ Monofoxe.Engine.GameMgr.WindowManager.CanvasSize / 2;

		for (var i = 0; i < _dpiList.Count; i += 1)
		{
			var p = new Vector2(_boundsList[i].X, _boundsList[i].Y);
			var s = new Vector2(_boundsList[i].Width, _boundsList[i].Height);
			if (GameMath.PointInRectangle(windowPos, p, p + s))
			{
				_dpi = _dpiList[i];
				return;
			}
		}
		_dpi = _dpiList[0];
	}

Yeeeep sounds about right, but there probably isn’t much you can do about it. You can only do what the API lets you do.

The only thing I might suggest is, if you want to release on other platforms, you might consider abstracting the windows bullshit away to a wrapper API, then interacting with an interface that describes that API. If, somewhere down the road, you decide you want to release for Linux, you can just create an implementation that buries what I can only assume is the Linux bullshit and your general code will presumably still work.

Might also not be worth your effort if you don’t have immediate cross-platform concerns though :wink:

Pretty much what I’ve done right from the start.

2 Likes

Nice. I’ve never implemented on Linux, how do you find stuff like the above? Is it a pain compared to getting this stuff for Windows, or more straight forward? Also, do you interact with a consistent API, or is there one for each distribution? I haven’t used Linux in forever, but back when I did each one had its own version of Windows.

Also, I was looking at your above code. Maybe you need the lists for other reasons not shown here, but I’m wondering if you can just do it all in one loop and break when you find the screen that has your window in it?

var windowPos = ...;
foreach (Screen screen in Screen.AllScreens)
{
    if (screen.WorkingArea.Contains(windowPos))
    {
        _dpi = GetDpi(...);
        break;
    }
}

I dunno, I just implemented WIndows version and left the possibility to add other platforms.

The other thing you may want to try is to let the user decide at what scale the user wants to render in your game, one thing that may not seem intuitive is that if you have 4k in a big monitor close to your face even if your text is small it is readable and seems ok, while if you have 4k screen in a mobile phone it won’t be legible at all.
For some young players a small font will be ok, while for older players will not, and bigger font will be better.

When I was a teenager I used to think it was a great idea to have a 1280x1024 screen resolution on a 15" CRT monitor. Now I appreciate larger monitors. Getting old is bullcrap :frowning:

I am at this exact step in my game now, deciding how to display font in my game, what scale to use and it has been a pain to decide, so i guess it depends on player’s monitor, how far/close you are, resolution, dpi, etc… so I think the easiest way is to setup at a standard size and let the player decide if he wants bigger or smaller font.

One thing I remember is … if you ever played the original Mass Effect 1, it had a big issue with fonts being too small in High resolution monitors or playing in very large monitors and many people complained, so i guess adding a multiplier to the UI will solve all issues.

Oh font scaling… lol. I did a smooth font scaling prototype a while ago, so feel free to cannibalize if it helps at all. Either way, good luck!

I have to give a try to your smooth font scaling and see.

I was implementing a similar solution with spritefonts to support multiple languages at multiple resolutions, so if I want to render at 1.0f or 1.25f i will use the same font but If i want something like 1.3f I will have another bigger font ready that will work from 1.2501f to 1.75f , well the 2nd font will be at 1.5f scale native and will range from 1.2501 to 1.75f , and will keep increasing fonts as needed depends on how good it looks, same for reductions.

Boy, in all honesty, 1x and 2x is enough.

Martenfur, in most of the cases I guess it may be enough, but I’ve been looking for a way to scale fonts that work everywhere without extra shaders.

Trinith, I checked your font scaling prototype and it was almost the same thing I wrote in general terms. Just one improvement I did was to cache all font heights so I can estimate the real scale from spritefont, using this I ended up using a lot less fonts than needed and got almost perfect fonts scaling. I noticed that in most cases doubling the font size like 12, 24, 36, 48 , 72, 144 gave me near perfect fonts. I added a few in betwens to make it sharper at those ranges but overall with just 6 fonts i got a wide acceptable range, I am very happy with it.

1 Like

Yea, I noticed in my experiments that lots of fonts in the low end didn’t really help much. I found I liked the look of a small font (like 6 or something) because things got messy down in that range, but once you started scaling high the font size mattered less and bitmap scaling handled the rest.