A BepInEx patcher that fixes small IMGUI UI on HiDPI screen.;
Demo image of smooth scaling of BepInEx.ConfigurationManager UI to 2 times of the original(right).
Actually it's very straightforward.
So the IMGUI utility actually come with a transformation matrix as GUI.matrix
, and with
var scale = 2.0f;
GUI.matrix *= Matrix4x4.Scale(new Vector3(scale, scale, 1.0f));
in OnGUI()
callback, all following UI draws would have UI scaled by 2.0
automatically.
And GUI events like current mouse position would still works. It's that simple.
However if UI component draws elements with absolute size referencing screen rectangle, those elements would probably overflow out of the screen.
To address this issue, we hook on Screen.width
and Screen.height
to return scale downed screen size by the same factor we used for scaling up UI elements. And such UI component would draw elements in this scale downed region, and when re-scaled up, every element would stays in the same relative screen position just like without scaling but with pixels-per-UI added up.
- Issue: Mouse inspection is broken in RuntimeUnityEditor as it uses absolute mouse position which this patcher also overrides.
- Workaround: Add
RuntimeUnityEditor
toOptOutNsRegex
in config file or mouse inspect on screen resolution 1920x1080 only .
If you are a plugin author, it should be easy to adapt this method to your OnGUI
loop.
And this doesn't requires you any structural change for UI drawing.
Here are the basic steps to implement HiDPI scaling for IMGUI.
-
Calculate DPI
scale
withMath.Max(Screen.height / 1080.0f, Screen.width / 1920.0f)
, your UI is presumably designed for 1920x1080 so it's reasonable to just calculate scale factor from that. -
Scale
GUI.matrix
byscale
using transformation matrix in the head ofMonoBehaviour.OnGUI
callback, all UI elements draw after this would be scaled up transparently from top-left to bottom-right. -
Instead of referencing screen border with
Screen.width
andScreen.height
, reference scale downed version of them, i.e.Screen.width/scale
andScreen.height/scale
. This also applies to other absolute position like Input mouse position that referenced. And with UI scaling in step 2, the final rendering result would have your UI element referencing to actual screen border just like there was no scaling. -
If you use coordinate conversion utilities like
GUIUtility.ScreenToGUIPoint
andGUIUtility.GUIToScreenPoint
, setGUI.matrix
toMatrix4x4.identity
otherwise you would get scaled results. Just like other drawing operations, they are transparent to final scaling, so does these coordinate should also remain unscaled. Note make sure to save and revert back to the previous "scaling"GUI.matrix
. -
If your plugin needs to coexist with this patcher, edit the config file of
IMGUI.HiDPI.Patcher.{Mono,IL2CPP}.cfg
and add the namespace matcher of your UI component intoOptOutNsRegex
in[Opt-out]
.
And that's it!