I recently upgraded my phone from the One Plus 7T Pro 5G McLaren (that’s a mouthful) to the Samsung Galaxy S21 Ultra (slightly less of a mouthful) because the phone was damaged and I kind of wanted to get a new flagship device. The base specs like CPU, GPU, RAM, etc. are decent enough upgrades, but things like display and photo quality are miles ahead of the 7T Pro. One thing that’s nearly identical though is the screen resolution. The S21 Ultra sports a resolution of 3200 x 1440 pixels, while the 7T Pro has 3120 x 1440 pixels. These are both effectively QHD/1440p screens, but something is a little weird on this S21 Ultra. Let’s take a look…
While I no longer have the 7T Pro, I do have an LG G8 ThinQ which also conveniently has a 1440p screen. Here are some key specs for each of the test devices:
|G8 ThinQ||S21 Ultra|
|CPU||Snapdragon 855||Snapdragon 888|
|GPU||Adreno 640||Adreno 660|
|Resolution||3120 x 1440 px||3200 x 1440 px|
Each are set to the max resolution and will be used landscape mode to make the calculations easier to work with. All tests are being performed with the same versions of each application, installed via Google Play.
Also be aware that I’ll be referring to the QHD resolutions as 1440p, and FHD as 1080p, since the number part of those names represent the max vertical resolution.
For the uninitiated, DraStic is a popular Nintendo DS emulator for Android. It has great performance on all kinds of devices, and a ton of options to enhance the experience. One of those options being screen layout management. Using this feature is how I first noticed an issue between the S21 and the other phones. The DraStic tests were all done with Filtering set to None and High-Resolution 3D Rendering On.
Notice the integer scaling options available, referring to the “1x”, “2x”, etc. These options are calculated based on the detected resolution of the device. The Nintendo DS native resolution is 256 x 192 px for each screen, so the maximum integer scale on a 1440p screen is 7x, or 1344 px (7x scale * 192 px). While the LG G8 shows us all options up to 7x, the S21 Ultra curiously stops at 5x, which just so happens to be the max integer scale for 1080p resolution. Interesting.
Let’s take a closer look at what’s happening by looking at the moon on the Castlevania Dawn of Sorrow title screen (ignore the right screen, left screen has scaling set):
When zooming in at 7x integer scale, you can see each pixel is rendered as a perfect square with crisp edges on the G8, but not the S21 Ultra:
Each “DS pixel” is being evenly scaled to a 7 x 7 grid of real pixels with a clearly defined dither pattern on the G8 display, whereas the S21 Ultra uses a 5 x 5 grid which is upscaled a bit with some linear filtering that smooths out the “DS pixel” edges and the dither pattern. On a high density display the end result is a slightly softer looking image.
The whole reason I’m using a 1440p screen is because of the analysis from my Retro Console Emulation and Integer Scaling post. To summarize, I believe 1440p is the ideal resolution for a retro gaming device where integer scaling will be preferred, because it has excellent screen coverage for most retro console resolutions. Everyone knows Super Mario World, so let’s use that for the next test. I’ll be using RetroArch 1.9.1 and the latest available Snes9x core for this one.
Super Mario World has a native resolution of 256 x 224px, so an integer scale of 6x, or 1344 px height is optimal for a 1440p display. We can see from the first picture that’s exactly what we get on the G8, with small black bars at the top and bottom of the screen. Zooming in 4x again we see the “SNES pixels” are perfectly square (let’s not debate aspect ratio here), and have those crispy edges I crave, yet the Samsung is again capping at 1080p and only taking up 83% of the screen at 1196 px high (224 vertical * 4x scale * 1.33 upscale to 1440p).
Not only do we lose 10% vertical height because of this mysterious upscaling, the interpolation monster has struck again, leaving the carnage that is the blurry edges of the title logo in his wake.
At this point I’m thoroughly disappointed in the image quality I’m getting compared to the 2 year old phone the S21 Ultra is up against. Well, hopefully this is only affecting emulation, and native Android games aren’t being upscaled without my consent…right?
For my final test, I’m using one of my favorite games of the last few years, and maybe of all time: Dead Cells. It’s an amazingly fast and fluid playing game with some of the most beautiful in-game music ever. I highly suggest everyone on the planet buy it since it can basically run on a potato. Let’s take a look at the opening scene of the game.
Again we can see the S21 is rendering at 1080p and upscaling the output to 1440p. Here this is evident by comparing the font size to the 1080p G8 screenshot, and that oh-so-awesome interpolation:
Notice the zoomed 1080p image from the G8 is razor sharp and has a native 1080p resolution (2340 x 1080 px), unlike the S21 Ultra’s upscaled 1440p image. If the S21 Ultra was actually running at 1080p, the screenshot would also be razor sharp like the G8, so that begs the question…
There currently seems to be an issue (feature?) with the Samsung Galaxy S21 Ultra that is causing these applications to render at 1080p internally, to then be upscaled for the native 1440p output.
I don’t have a solution for this issue right now, as I haven’t determined the root cause. It could be a bug in Android 11 with devices that support multiple resolutions, but that will require testing other Android 11 devices I don’t have access to.
Whatever the cause, this is an immensely frustrating discovery that I hope to find answers on soon.
I was able to manually trick the OS into natively outputting 1440p. This has been tested with RetroArch and DraStic:
Set device resoultion to FHD via ADB:
adb shell wm size 1080x2400
Open the application you want to scale properly, in my case RetroArch (make sure everything is already configured for integer scaling)
When running, the app/game should be natively 1080p
Set device resolution to WQHD+ via ADB:
adb shell wm size 1440x3200
The screen resolution will update in realtime and now be properly scaled to 1440p
The app will continue to scale properly until it is closed (no longer in memory). Any other applications opened after this hack will still have the incorrect scaling, and the steps will need to be repeated. I’m still digging further into this.
Was doing some logging with the following command while messing with the ADB commands from Update #1:
adb shell dumpsys window displays
And noticed something interesting the logs:
I am unable to reproduce the scaling issue with a simple OpenGL test app: DisplayScalingTest, 1440p output renders fine with this. Maybe it’s something to do with app manifest configuration? Going to look into the RetroArch repo and see if I find anything there.