Porting to Windows 10 UWP
I’m currently in the process of porting my PSP emulator PPSSPP to work under the Universal Windows Platform (UWP), or Windows 10 native apps. The advantage of porting applications to this framework is that once you’ve done so, your app will work on Windows 10, Windows Phone 10 and Xbox One, and presumably other future Microsoft platforms, at least for the next few years…
For this new Windows platform, Microsoft has included a framework based on a lot of the Windows Phone 8 platform as the application model (the usual “app lifecycle” stuff similar to Android and iOS). This is programmed using C++/CX, a distant cousin of the venerable C++/CLI. In my case, only a thin layer of code needs to be written using this framework to set up and manage the application window, then my app will run as a plain Direct3D application inside, so even if it’s a little painful with all the asynchronicity, it’s quite managable.
In addition to the new stuff, a fairly decent subset of the Win32 API is also available. Similar to Windows CE, a lot of legacy has been removed, plus all of the classic shell and windowing APIs. Getting rid of most legacy usage was no big deal, but once that was done, the real work started. Some dude called perneky had actually already done all of this but mostly in a quite hacky way (they used ANGLE to run the OpenGL backend on top of D3D, for example) so I decided to do things my way, and do them right.
3D API support
There is no support for DirectX 9 or OpenGL, my primary API targets, and no Vulkan support. No biggie, I “simply” had to add a Direct3D 11 backend. Doing that forced me to do some refactoring I really should have done a long time ago and generally improved the code, so I’m quite fine with this. Getting D3D 11 to work without simultaneously dropping support for Windows XP was a bit of a hassle (had to make a private copy of the headers from the newer SDK while still building with the old one) but that’s done now.
File system access
File system access is … lacking. You can only use “normal” libc and Win32 file access functions like fopen/fread/fclose, GetFileAttributes and similar inside your own “local folder”. Accessing anything outside requires strange asynchronous UWP APIs: StorageFile and friends. The existing code and the PSP itself does not make use of asynchonrous I/O with callbacks, so to be able to read from ISO files outside the app’s private directory, I had to create a threaded abomination that simulates synchronicity around a core of asynchronicity. Suffice to say that it’s ugly. In perneky’s port, he solved this by simply copying your game files into the local folder before running them - I feel that’s not an appropriate solution, both because it requires a lot of storage and because it’s simply slow.
For the memory stick emulation I decided to simply use the local app storage though, which removes the need for these tricks. The main screen game browser I haven’t tackled yet, but it will be a challenge.
Runtime code generation
Runtime code generation turned out to be a smaller problem than expected. Unlike Windows Phone 8, Windows 10 lets you declare that you need the “Code Generation” permission. With that, you can VirtualAlloc a block of R/W memory and then VirtualProtectFromApp it over to R/X. R/W/X is not permitted but we already had full support for living with that restriction - we simply switch pages back to writable when needed, then make them executable again (for example when patching branches between JIT blocks). Some variants of execution on iOS has the same restriction. So we’re all good, and this worked on the first try.
Memory map emulation
In a previous post I was annoyed with Apple’s shenanigans limiting the virtual address space. UWP does not have the same crippling address space restriction, but it has a different problem.
The PSP has an address space, that, simplified, looks like this:
| Start address | Size | Access | Description | |---------------|------|---------------------------------------------------------------| | 0x00004000 | 4k | N/A | Scratchpad RAM ("locked cache") | | 0x04000000 | 2MB | Cached | Fast video memory | | 0x08000000 | 32MB | Cached | Split into 8MB of kernel, 24 MB of user memory | | 0x44000000 | 2MB | Uncached | Fast video memory | | 0x48000000 | 32MB | Uncached | Split into 8MB of kernel, 24 MB of user memory | | 0x88000000 | 32MB | Kernel | Kernel-space access to kernel/user memory |
This does not mean that it has 4MB of VRAM and 96MB of RAM - no, 0x44000000 is a “mirror” of 0x04000000, and 0x88000000 and 0x48000000 are mirrors of 0x08000000. This means that if I write 0xff to 0x49000000 for example, I can read it at 0x09000000, after invalidating the data cache at that location.
To simulate that quickly, in the past I’ve used CreateFileMapping to grab a bunch of anonymous memory, found myself a base pointer somewhere up in space, and then used MapViewOfFile to dole out chunks of the anonymous memory at relative offsets from that pointer matching the address space seen above. The same chunk of the anonymous memory was placed multiple times at the appropriate offsets in case of a mirror. Then the game simply accesses PSP memory by applying the base pointer offset, and all just works. Of course I ignore caching and simply treats the whole address space as coherent, but games generally don’t have a problem with that. Exactly the same thing can be done on Unix using shared memory + mmap or on Android using ashmem + mmap, and Mac/iOS also have a similar mechanism.
In UWP, CreateFileMapping and MapViewOfFile has been replaced by CreateFileMappingFromApp and MapViewOfFileFromApp, and the latter has gained a hideous restriction - suddenly we can’t specify where in the address space we want the mapping anymore!
This breaks the whole scheme, so I gave up on my clever hardware-accelerated mirroring.
Instead, I’m simply going to have to do the same workaround I do on 32-bit platforms: Simply mask off the top 4 bits of every pointer before adding the offset and performing the access. Due to how the address space is organized, this works fine, but imposes a small overhead on every memory access. So if you want top performance on a PC, you’ll still want to use the desktop version of PPSSPP.
Anyway, the UWP or Windows 10 Universal version of PPSSPP will be out in a few weeks, for both PC and Windows 10 for phones, and the adventurous might even be able to compile and run it on Xbox One though it will not be released on the Windows Store for that platform - other emulators are regularly taken down from the Xbox One section of the store.
Any comments? e-mail me at firstname.lastname@example.org .