The Strategies for Independence
For this assignment we set out to take the shader code we had written for both Direct3D and OpenGL platforms, and to make them as platform independent as possible. To do, we’re using two important tactics to do so, macros and ifdef blocks.
On the usage of Macros (Declarations)
By capitalizing on the fact that shader code is essentially C, we have a number of tools at our disposal. Macros are a small fragment of code that, when used, essentially replaces the contents of the usage of that Macro with the defined lines of text or code behind it. For example, we may want to preserve the naming of a given function, but would like to quickly refer to it for some reason, this could be done with a macro like so:
In this example, we’re going to replace the longer function call of HardToRememberFunctionName() with a shorter name, EasyFunctionName(). When EasyFunctionName() is used in code, the exact line following the macro keyword is replaced in the document at compile time. For example, when declaring a texture for use in our shader, we do so by calling the following macro:
This line is valid in both the GL and D3D compiled version of the code, due to how the macro works. At compile time, this line is replaced with the actual declaration of a texture object following the syntax of either platform. We extend this approach to sampling a texture as well:
Here, to make sure we use the appropriate platform specific implementation of sampling a color, we use a macro that takes in a texture, it’s UV, and sampler state. This specific line is then replaced with the pertinent call to sample a color properly.
Note the usage of the float4 type here, a type that is used in D3D but not OpenGL. To explain this anomaly fully, we’ll do so in the following section on ifdef blocks.
On the Usage of ifdef
In the event that you’d like a certain block of code to run in place when a condition is met, we can achieve this goal using an ifdef block. In our code, we may want to only include code for a given platform when we’re building for that platform, the Direct3D platform for example:
Note, the EAE6320_PLATFORM_D3D macro is defined in our code for when the environment is detected as x64. When it’s detected, the macro is defined, and the code situated between the blocks are included in for compilation. This along with our macro usage discussed in the previous section, are the tools we used to implement our shader code into a single copy of a file, broken apart by a small use of #ifdef and macros. For ease of readability and sensibility, we still aim to keep this type of platform specific implementation sparse, and used when only necessary.
Recalling the previous point on the usage of float4 in shader code, a type that is used only in D3D. With ifdef blocks we can now do something like the following:
Now, we are able to use either type interchangeably and can assure that our program will reliably switch out the appropriate declarations where we use them in our code. This is the exact trick that we use to standardize the usage of gl_Position vs o_position.
You can try out these games via the below links. The only difference is that Direct3D will be used in the x64 version, with OpenGL in the other. They have been built and verified to work on Windows. For this post, the space bar is used to swap the textures on screen.