mono/MonoGame — pull request #2243 by JamesLupiani (2014-02-21 21:54:19+00:00)
Title: Split GraphicsDevice on a per-API basis

This request cleans up the branching ifdefs and moves API-specific code from GraphicsDevice.cs into partial class definitions for each render API.

There are three goals at work here:
1. Eliminate confusing #ifdef-elif-elif branching
2. Separate platform-specific code while maintaining asserts/exceptions common to all
3. Avoid the performance hit of a base class and virtual methods

The first thing I did was to go through and eliminate nested #elif and catchall #else directives, with each API side by side in its own #if-#endif block. Since you would never have both OPENGL and DIRECTX defined, this is fine and it'd rightly fail to compile if you tried. This is probably worth doing even if we didn't go through with the other steps.

Next I isolated all in-method #if'd logic into methods called Platform{MethodName}. For example, PlatformDrawIndexedPrimitives. Assertions and exceptions common to all APIs stay in the main file to ensure runtime consistency.

Finally, I copied the file out three times, one for each API. Then I eliminated all platform-specific code from the main file, and all non-platform code from the copies. After switching the definition to a partial class in all files, everything compiles normally.

Using partial classes and chaining allows us to completely separate the APIs with only the overhead of a regular (non-virtual) method call. The process of porting is simply a matter of creating a new partial class file with the Platform* methods in it and adding it to the project file. If the ported file is missing or a Platform method isn't defined, the build fails and it's very easy to see where.

I recommend fetching the changes locally to inspect them side-by-side so it's easier to see what's going on.