Search Unity

UWP resolution and presentation (store rejects everything)

Discussion in 'Windows' started by RandomCharacters, Oct 9, 2019.

  1. RandomCharacters

    RandomCharacters

    Joined:
    Nov 29, 2012
    Posts:
    262
    .
     
    Last edited: May 21, 2020
  2. timke

    timke

    Joined:
    Nov 30, 2017
    Posts:
    408
    So, the simple response is: you're correct, this is missing functionality for UWP and it's something we've been thinking of addressing. However, the work item is currently low priority.

    The longer answer is: with Fullscreen in UWP, the app always runs in the OS display resolution regardless of what you set. If you do specify a different "Resolution" in Unity, Windows will just up/down scale or letterbox the output to match the native resolution and aspect ratio. UWP does not allow you to change the actual resolution as with WindowsStandalone, i.e. ExclusiveFullscreen mode.

    Since a scaled and/or letterbox output looks bad, you're better off leaving it in the native resolution and adapting the game's visual to match the screen size. Of course you can always change the output "Resolution" at runtime in scripts or set width/height in PlayerPrefs yourself if you really need this functionality.

    From our perspective, the inclusion of the Width/Height option is more useful for Windowed mode, allowing you to set the initial size of the Window when the app launches.
     
  3. timke

    timke

    Joined:
    Nov 30, 2017
    Posts:
    408
    So let me comment on each of these points:
    Yes, UWP apps are required to support 800x600 as the minimum display size but that doesn't mean the app has to always run like that. I don't know how this is check in Cert, but I suspect the app is launched onto an 800x600 display just to make sure it activates and displays properly.

    So, if you check "fullscreen" in the build settings and the app is forced to run on an 800x600 display, by default Unity will use that native resolution/aspect ratio (first time app is launched). If the app changes the aspect ratio after launch, e.g. 1920x1080, then letter boxes will automatically be added. This is actually performed by the Windows Compositor and not really by Unity.
    Yes, you can always change the display size at runtime via the UnityEngine.Screen APIs. For example you could create a simple "start-up" script with your first scene to force 16:9 aspect ratio:

    Code (CSharp):
    1. public class Example : MonoBehaviour
    2. {
    3.     void Start()
    4.     {
    5.         // Switch to Fullscreen 16:9 aspect ratio
    6.         UnityEngine.Screen.SetResolution(1920, 1080, true);
    7.     }
    8. }
    If you make this change at runtime, the setting will be saved to PlayerPrefs and the app will automatically start in this mode.
    Sure, for movies and video playback it's fine; I'm guessing this is your intent. However, IMO for gameplay itself I definitely don't want letterboxes and instead want the game to use my native aspect ratio.
    I agree this is annoying but it's also a useful for games that allow you to change your display resolution in the options menu. For example, I have a native 4K monitor but my graphics card cannot deliver 60FPS at that resolution, so I go into the in-game options and select 1080p instead. Now, I want the game to remember that setting the next time I launch it. So, saving display size in PlayerPrefs automatically satisfies this expectation.

    Sadly, this isn't way you always want (especially during development), and of course it doesn't really make sense for UWP since you cannot actually change the resolution. However, your app can always change the display to whatever aspect ratio you need at runtime.

    So, let's say you want your opening cinematic to be in 19x9, you could do something like this in you scripts:
    1. App launches in native resolution
    2. Save current native resolution
    3. Change to 16:9 aspect ratio
    3. Play cinematic
    4. Restore original resolution
    5. Display Title Screen or Main Menu

    I hope this is helpful.
     
  4. timke

    timke

    Joined:
    Nov 30, 2017
    Posts:
    408
    For UWP it shouldn't matter what "resolution" values you pass in for the SetResolution API. Since Windows will just scale the output; I don't think there's any "unsupported" values in UWP. For Win32, if SetResolution fails, then Unity will try to restore the previous width/height values, or if that's not possible, fall back to the native resolution.

    Still, to be safe, I'd recommend using the UnityEngine.Screen.Resolutions API (returns a list of supported resolutions) and simply find one from that list that has 16:9 aspect ratio. To be honest, you should be fine with 1920x1080.

    Win32 resolution and presentation settings should not carry over to UWP, as each platform should track settings separately. If you do see this occurring then let us know because that's a bug.
     
  5. timke

    timke

    Joined:
    Nov 30, 2017
    Posts:
    408
    This is the "Build and Run" option, right? That's pretty bizarre for the issue to only repro with your VS build of the app, because "Build and Run" for UWP executes MSBuild from your VS installation and generates an APPX similar to VS.

    It sounds like the UI or mouse input isn't "seeing" the screen changes and isn't scaling the mouse coordinates appropriately. This may be a Unity bug which we'll need to investigate further, and so could you please file a bug report (from the Bug Reporter tool) for this issue?

    A couple follow-up questions:

    Are you using uGUI for your UI or some other framework?
    What "Build type" are you using: "D3D Project", "XAML Project" or "Executable Only"?
    Do you have multiple versions of VS installed and which version are you using?

    There's something different between the Editor and VS builds, I like to figure out what that difference is as a work-around for the bug.
     
  6. timke

    timke

    Joined:
    Nov 30, 2017
    Posts:
    408
    Thanks for the clarification. The issue is almost certainly a bug in the mouse coordinate scaling that occurs after changing the Screen mode, and so please submit a bug for this problem. I don't think this is a known issue.

    If you don't mind sharing a Unity project to this forum post that repros the problem (e.g. Google Drive, OneDrive, Dropbox, etc.) I might be able to find a work-around. It doesn't have to be your real project, but something simple that demonstrates the broken UWP behavior.
     
  7. timke

    timke

    Joined:
    Nov 30, 2017
    Posts:
    408
    Update on this.

    I debugged and root-caused the problem; it's a somewhat complex problem and I'll spare you the details (unless you want them).

    The short of it is: setting the screen resolution larger than your native display triggers bad logic in the UI handling where it thinks the mouse is on the 2nd display. This causes the RayCast to fail to hit UI controls on the right/bottom of the screen.

    To be clear, in UWP this is perfectly fine because the Windows 10 Compositor will just downscale to your system's native display. This issue does not occur in WindowsStandalone because we don't allow you to set a Resolution that's larger than the native display (I believe this is a restriction on Windows 7). Therefore, the bug doesn't repro there because Unity is ignoring the high resolution values and defaulting to the native resolution. You can confirm in the repro project that WindowsStandalone build is running at a lower resolution that UWP.

    Anyway, the workaround to this problem is: don't call Screen.SetResolution with values larger than your PC's native display. Here's some code which allows you to find and set the highest 16:9 aspect ratio resolution that's supported by the system.

    I hope this helps.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class test : MonoBehaviour
    6. {
    7.     // Look-up table for standard 16:9 aspect ratio resolutions (obtained from Wikipedia)
    8.     Vector2[] lookup16_9 =
    9.     {
    10.             new Vector2Int(15360, 8640),
    11.             new Vector2Int(7680, 4320),
    12.             new Vector2Int(5120, 2880),
    13.             new Vector2Int(4096, 2304),
    14.             new Vector2Int(3840, 2160),
    15.             new Vector2Int(3200, 1800),
    16.             new Vector2Int(2880, 1620),
    17.             new Vector2Int(2560, 1440),
    18.             new Vector2Int(2048, 1152),
    19.             new Vector2Int(1920, 1080),
    20.             new Vector2Int(1600, 900),
    21.             new Vector2Int(1366, 768),
    22.             new Vector2Int(1280, 720),
    23.             new Vector2Int(1024, 576),
    24.             new Vector2Int(960, 540),
    25.  
    26.             // Probably can exclude anything smaller than 960x540
    27.             //new Vector2Int(854, 480),
    28.             //new Vector2Int(848, 480),
    29.             //new Vector2Int(800, 450),
    30.             //new Vector2Int(768, 432),
    31.             //new Vector2Int(640, 360),
    32.             //new Vector2Int(426, 240),
    33.             //new Vector2Int(256, 144),
    34.     };
    35.  
    36.     private bool IsResolution16_9(Resolution res)
    37.     {
    38.         foreach (var item in lookup16_9)
    39.         {
    40.             if (item.x == res.width && item.y == res.height)
    41.                 return true;
    42.         }
    43.         return false;
    44.     }
    45.  
    46.     // Start is called before the first frame update
    47.     void Awake()
    48.     {
    49.         // Default 16:9 aspect ratio (qHD) if we fail to find one at a higher resolution
    50.         int width = 960;
    51.         int height = 540;
    52.  
    53.         try
    54.         {
    55.             // Get all supported resolutions
    56.             var resolutions = Screen.resolutions;
    57.  
    58.             // Resolution list is sorted from lowest to highest so walk backwards through the array
    59.             for (int i = resolutions.Length - 1; i >= 0; i--)
    60.             {
    61.                 var res = resolutions[i];
    62.  
    63.                 // Exclude any resolutions higher than the native display
    64.                 // NOTE: Resolutions array shouldn't return anything larger than the native display, but just in case...
    65.                 if (res.width > Display.main.systemWidth || res.height > Display.main.systemHeight)
    66.                     continue;
    67.  
    68.                 // A look-up table is the simpilist and most reliable way to check for 16:9, since it doesn't depend on floating point
    69.                 // math wonkiness and allows you to support standard resolutions that aren't technically 16:9, e.g. 1366x768
    70.                 // See StackOverflow for more context on this: https://stackoverflow.com/questions/1186414/whats-the-algorithm-to-calculate-aspect-ratio
    71.  
    72.                 if (IsResolution16_9(res))
    73.                 {
    74.                     width = res.width;
    75.                     height = res.height;
    76.                     break;
    77.                 }
    78.             }
    79.         }
    80.         catch {; }
    81.  
    82.         // Switch to Fullscreen 16:9 aspect ratio
    83.         UnityEngine.Screen.SetResolution(width, height, true);
    84.     }
    85. }
    86.  
     
  8. timke

    timke

    Joined:
    Nov 30, 2017
    Posts:
    408
    I just recently fixed the bug, although it hasn't landed to a release yet (still in review). The plan is to backport the fix to 2019.3 and 2019.2.

    I'll try to expedite the backports as best I can, but it may still take some weeks before the fix makes it into a patch release.

    The work-around should be fine for both UWP and Win32, but I don't know about Android. I think it'll be OK since the script is just making sure we're using a supported 16:9 aspect ratio, but you'll want to test that.