I’ve been looking at Windows 8 Metro, wondering what issues may arise when developers try to port existing games to Metro and DX11.1, as well as converting to Visual Studio 11. As we’ve seen already, some old favorites have disappeared from DirectX so I started looking at porting my “half an engine” (we all have one, right?) over to Metro to see what would happen. I’m going to describe some of the hoops I jumped through, and some of the epiphanies I had while getting it all working.
What started me off thinking this would be a port, not just a recompile was that D3DX has gone, and I used it a lot. I compiled all my shaders at runtime using D3DXCompileShaderFromFile(), so that promised to be a headache. I use a fairly simple and flexible technique for making shaders. I have a main() for most shaders in one file, then some #defines which control what is included in that main. Features are bought in as fragments from other files as required so you can build just the shader(s) you need at runtime. My initial reaction was that with no D3DXCompileShaderFromFile(), you will need to compile every possible shader at compile time and include them in the app!
Luckily, that’s not to be. Although D3DXCompileShaderFromFile() has gone, you can still use D3DCompile() as long as you jump through a couple of hoops. To start with, You can replace existing calls to D3DXCompileShaderFromFile() with calls to D3DCompile() fairly trivially. The only change is you have to preload the source for the shader, which you then pass to D3DCompile(). Pretty easy, and there is lots of samples about how to load files on Metro, don’t get into a panic when you add CreateFile() and it comes up as unresolved extern!.
Loading Shader source and other data files.
An interesting problem is where do you load your shader source from? When you run an app you’ve compiled to run on Metro in Visual Studio 11, you quickly realize the app is actually installed on the Metro side in its sandbox before its run. This means your shader source has to be installed in the sandbox as well so that the app can find the files. The easy way I found to do this is to add the shader source file to the project, open the properties dialog for the file, change the “General / Content” field to “Yes” and the file is magically added to the sandbox so that you can open it at runtime.
Next problem is in order to use D3DCompile() you need to add d3dcompiler.lib to your app. This lib, of course, needs to find D3DCompiler_44.dll in the sandbox. It turns out you can do the same with the dll as you did with the source: add it to the project and mark it as content. In fact, you can do this with all your data files and it’s a quick and dirty way to get things up and running when you have data files you need to load at runtime.
What happened to D3DX Math then?
It turns out that for a good while, Microsoft have been advocating using xnamath and leaving D3DX math behind. This is pretty good advice, in fact, xnamath uses a lot of SSE and is way more efficient than using floats. Thats what we all want, right?
The first thing I noticed when I started the port was that xnamath.h is no longer available – millions of compile errors. On the MSDN site on porting to Metro I saw that Microsoft now advocate using something called DirectXMath, “Oh great,” I thought, “let’s go port the math again…”.
Well, it turns out the whole problem is a non issue. You can port from xnamath.h to DirectXMath just by using the DirectX namespace. All the XMxxxx functions & types etc are there in the DirectX namespace. I personally added “using namespace DirectX;” to a strategically placed include file and all the compiler errors vanished. If you want to play by the book, you should really use the namespace just in the files where you need it, of course. I went global with it in the end because I have a lot of math in header files (lazy me ).
GUIDs, and other animals.
So DXGUID.lib has gone. The rather obvious symptom during porting is that you get unresolved externs for all the DX interfaces that use GUIDs, like the shader reflection interfaces for example. I use reflections to extract which constant buffers are used by a given shader. I was getting quite frustrated over the fact that you can use IID_ID3D11ShaderReflection and all its related GUIDs in your code, and you can right click and “Go to Definition”, but the linker insists that it’s still unresolved.
Shame on me: the solution was pretty obvious in the end. Does anyone remember the days before DXGUID.lib? All you need to do is #define INITGUID somewhere in the code (preferably before you include all the directx includes) and all the GUIDs magically appear.
What do I do about the “Windows loop”?
Well, I don’t know what your solution will need to look like; this depends very much on how you’ve written your engine. My engine has learned, over the last few ports it’s been through, that the smaller the interface to OS specific stuff is, the better. I have basically three entry points: Init(), Render(), and Destroy(). Render() will draw one frame from the scene contained in the engine.
When you call Init() to start up the engine, you pass in an interface to a set of functions which the engine can call for machine specific actions. Things like reading files, getting Input, Getting timer data, things like that. The upshot is that when you port to the new OS, you implement these functions and pass the pointers to the engine.
This interface means that you can just build the basic DX sample from Visual Studio 11, add calls to Init() Render() and Destroy() in appropriate places, build the machine specific functions you need and that’s it. Job done.
All in all the port to Metro doesn’t look that bad. There may still be some gotcha’s waiting for me, but the process looks fairly painless at the moment. A nice bonus is that changes you make to the DirectX pipeline in your game, if you’re careful, will still work on a Win32 desktop app! Win – win!
As I said at the start, mine is really only half an engine, and hasn’t been used in anger. I use mine to keep my coding hand up to scratch and to investigate new OS’s and such. It started out on Win32, It’s been ported to Linux, Meego, Android, and now back via Win32 to Metro and lots of little bits have been dropped along the way. It’s gone from DirectX 9, to OGL, to GLES, and finally now to DX11.1 via DX10.