Universal Windows Platform
TL;DR: it’s a polished turd.
I spent a weekend and some evenings porting Oryol over to UWP (Universal Windows Platform) to get an idea what the noise is all about. The port is mostly working (except I/O). There remain problems in 3rd party libs I’m depending on in the Oryol Extension Samples which I can’t fix without messing around in those libs’ source files.
I’m leaving the UWP port unfinished for now because of a combination of annoying problems with the UWP APIs, and a lack of motivation to spend my precious sparetime with a platform that ultimately I don’t care for since I neither own an Xbox One nor a Windows Phone. May be I’ll pick up the pieces later.
Here’s a quick rundown of the UWP-specific things in Oryol, and after that some general thoughts:
- buildsystem changes (very similar to iOS or Android):
- cmake needs to know that it should generate an UWP Visual Studio project with CMAKE_SYSTEM_NAME=WindowsStore and CMAKE_SYSTEM_VERSION=10.0
- instead of linking against the various Win32 system libs, link against WindowsApp.lib
- you cannot link against the static MSVC runtime libs, only dynamic linking is allowed (/MD instead of /MT)
- the C++/CX dialect must be enabled by passing the /ZW compiler option, at least for the files that talk to the UWP APIs, I have simply enabled this for all source files (C++/CX has extensions that enable it to talk to .NET APIs, it’s a bit like the C++/ObjC scenario in the Apple world)
- the /WINMD linker flag must be set
- UWP projects need a Package.appxmanifest XML file, similar to Info.plist on OSX/iOS or AndroidManifest.xml on Android. I’m generating a dummy manifest from within cmake, and copy some required placeholder images into the right place (splashscreen, icon etc)
- UWP apps must be packaged into an .appx installer before they can be started from the Windows desktop, I don’t support this yet in the build system, but it can be done manually through Visual Studio
- UWP apps must be signed, but it looks like Visual Studio creates a dummy self-signing certificate during packaging if none is there (this is also similar to iOS or Android)
-
Windows.h still exists but only offers a subset of the Win32 API, likewise with the CRT. Look out for functions that compile and link, but are just empty compatibility stubs (like CoInitializeEx()). Some 3rd-party libs might compile, but still not work because of this (in my case: the XAudio2 backend in soloud). This is especially annoying because UWP defines all the usual preprocessor defines for identifying Win32, but without offering a real Win32 environment, this will be terrible to debug in a project with a bigger Win32 API footprint (Oryol reduces platform-specific code to an absolute minimum, so it isn’t too bad)
-
Most platform-agnostic code, and some Win32-specific code will compile without changes, there are a couple of new warnings and errors which I fixed in my own code, and supressed in external dependencies. The Win32 parts that don’t compile must be replaced with UWP specific code, which sometimes is straight-forward, but most often has lots of little problems because of UWP’s mobile roots that don’t quite fit into a desktop environment.
- Win32 areas that don’t exist anymore and must be removed or replaced with UWP
code (at least the parts that I encountered):
- WinMain
- low-level stuff like getting a stack backtrace
- anything dealing with the window system, clipboard, WinProc etc…
- message polling via PeekMessage/DispatchMessage this is done by registering event callbacks written in C++/CX
- WinHttp
- D3D11 swap-chains are more restricted than their Win32 counterpart
- weird stuff, annoyances and things that are worse than Win32:
- the application window already exists before the main function is called, and there are some mindboggling restrictions, for instance it is possible to listen for size-changed events, but there doesn’t seem to be a way to change the size of an existing window programmatically, may be it’s possible but if so it’s damn hard to find
- C++/CX has new warnings and errors which may be a problem with 3rd-party-code (I don’t expect that most open source libs will be fixed for UWP anytime soon)
- the whole UI runs on a different thread than UWP’s main() function, this may be a problem for static initializers; in Oryol I’m delaying all initialization until the UI thread is running, the entire Oryol application is running on the UWP UI thread
- there is now an UWP application object, similar to iOS or OSX, and the user-code runs completely from inside callback functions
- there are app-lifecycle-events that must be handled, like suspend/resume, and the application is expected to free up memory if asked to do so
- the end of the main() function is never reached, instead quitting an application by clicking the top-right close button just calls the suspend event handler, and then the application just ‘disappears’ from the debugger, I thought at first that must be a bug in my code, but the UWP D3D samples behave the same :/
- I am not sure yet whether app-lifecycle event handler run on another thread than UI event handlers, if that is the case it might complicate things further
- mouse input is mashed together with touch input, and the API designers didn’t seem to have thought of the fact that multiple mouse buttons could be pressed at the same time, tracking multiple mouse buttons is really awkward and has been hacked into the touch API as an afterthought
- D3D11 swapchain buffers cannot simply have MSAA anti-aliasing enabled
- key up/down events with virtual keycodes exist, but the VK enumeration lacks a number of keys which makes it useless (casting the enum to an integer works, and then the missing keys are there)
- debugging kinda works (take that, Android NDK!), but sometimes the app just disappears during debugging (I also had this in the official samples, so I guess it’s not my code)
- things that are nice^H^H^H not completely broken:
- the C++/CX dialect is pretty weird, but not as weird as Objective-C
- .appx package files can be quite small (the Oryol core samples are between 100 and 150 kBytes, and this includes the dummy PNG images for splash screen and icons), and they are installed and started with 3 clicks
- uhm, yeah, not much else I think, but I wanted to say at least something positive ;)
And here’s my general take on UWP (as always all IMHO):
I think for game development, UWP creates more new problems than it fixes because it is essentially a WinRT-v2 dragged onto the desktop with an incomplete Win32 compatibility layer thrown in.
Win32 isn’t exactly a good API, but at least it has a very small set of functions that are relevant for game development (open a window and get keyboard/mouse input). Writing window system glue code is pretty terrible on every OS, but UWP sucks more than Win32 in this area because of UWP’s mobile roots.
The UWP mobile application model is outdated and from a time when mobile devices had a lot less RAM and were much slower than notebooks or desktop computers. Microsoft would have been wise to not simply copy the nearly 10 years old iOS or Android application models.
With the whole Microsoft mobile strategy dead in the water, UWP looks like a relict of the past looking for a new platform. The whole mobile part of UWP no longer matters, but it’s exactly those mobile parts that make UWP such a mess for game development (iOS or Android are not good operating systems for games either, but these are too big platforms to ignore).
I spent a lot of time thinking about UWP’s place in the world while writing the code for it, and I have come to the conclusion that it is a pure marketing fabrication, a vision of a ‘grand unified theory’ of how we shall use computers onward, born in the ivory towers of ‘old Microsoft’. Why UWP has survived the Windows8 MetroUI meltdown, I don’t know. I think it’s just the momentum of thousands of people slowly kicking a dead horse forward.
I’m happy to accept and support pull requests for Oryol though, as long as they don’t mess up the existing Win32 code too much ;)