So MonoGame is a great technology that allows us write common C# code and deploy our games to Windows-Desktop,  Windows-Metro, Windows-Phone, MacOS, Linux, IOS, Android, Ouya and even more. Being able to write portable code is great for us indie developers, though still your code may need some platform specific parts.

With our latest game Frenzied (which can run on Windows, Windows-Metro, Windows Phone 7, Windows Phone 8, Android, MacOS and Linux atm), we introduced our in-house platform-managment concept which we decided to blog about, so that others can also utilize.

Here’s how we are managing multiple-platforms for Frenzied;

First of all we created per-platform solution files;

Solution Files

Each project defines it’s own conditional compilation symbols so we can distinguish them;

Conditional Symbols

Then we defined some enums, the first one is Platforms;

    /// <summary>
    /// Platforms.
    /// </summary>
    public enum Platforms
    {
        Windows,
        WindowsMetro,
        WindowsPhone7,
        WindowsPhone8,
        Linux,
        MacOS,
        Android,
        IOS,
        PSP,
        Ouya,
    }

GraphicsAPI enum;

    /// <summary>
    /// Graphics API's.
    /// </summary>
    public enum GraphicsAPI
    {
        DirectX9,
        DirectX11,
        OpenGL,
    }

Frameworks enums;

    /// <summary>
    /// .Net frameworks.
    /// </summary>
    public enum NetFrameworks
    {
        DotNet,
        Mono
    }

    /// <summary>
    /// Game frameworks.
    /// </summary>
    public enum GameFrameworks
    {
        XNA,
        MonoGame
    }

Then we implemented our static PlatformManager class, that will be responsible for identifying current platform and running appropriate code.

using System;
using Microsoft.Xna.Framework;

namespace Frenzied.Platforms
{
    /// <summary>
    /// Platform Manager that identifies platforms & manages them.
    /// </summary>
    public static class PlatformManager
    {
        /// <summary>
        /// The current platform.
        /// </summary>
        public static Platforms Platform { get; private set; }

        /// <summary>
        /// Current .Net framework.
        /// </summary>
        public static NetFrameworks DotNetFramework { get; private set; }

        /// <summary>
        /// Current .Net framework's version.
        /// </summary>
        public static Version DotNetFrameworkVersion { get; private set; }

        /// <summary>
        /// Current game framework.
        /// </summary>
        public static GameFrameworks GameFramework { get; private set; }

        /// <summary>
        /// Current game framework's version.
        /// </summary>
        public static Version GameFrameworkVersion { get; private set; }

        /// <summary>
        /// Current graphics api.
        /// </summary>
        public static GraphicsAPI GraphicsApi { get; private set; }

        /// <summary>
        /// Handler for current platform.
        /// </summary>
        public static PlatformHandler PlatformHandler { get; private set; }

        /// <summary>
        /// Helper for current platform.
        /// </summary>
        public static PlatformHelper PlatformHelper { get; private set; }

        static PlatformManager()
        {
            IdentifyPlatform();
        }

        /// <summary>
        /// Should be called by platform-specific startup code.
        /// </summary>
        public static void Startup()
        {
            PlatformHandler.PlatformEntrance(); // run the appropriate platform entrace code.
        }

        /// <summary>
        /// Should be called by actual game code's Initialize() method.
        /// </summary>
        /// <param name="graphicsDeviceManager">The <see cref="GraphicsDeviceManager"/>.</param>
        public static void Initialize(GraphicsDeviceManager graphicsDeviceManager)
        {
            PlatformHandler.Initialize(graphicsDeviceManager); // rtun the appropriate platform initialization code.
        }

        /// <summary>
        /// Identifies the current platform and used frameworks.
        /// </summary>
        private static void IdentifyPlatform()
        {
            // find base platform.
            #if WINDOWS && DESKTOP
                Platform = Platforms.Windows;
                PlatformHandler = new Windows.WindowsPlatform();
                PlatformHelper = new Windows.WindowsHelper();
            #elif WINDOWS && METRO
                Platform = Platforms.WindowsMetro;
                PlatformHandler = new WindowsMetro.WindowsMetroPlatform();
                PlatformHelper = new WindowsMetro.WindowsMetroHelper();
			#elif LINUX && DESKTOP
				Platform = Platforms.Linux;
				PlatformHandler = new Linux.LinuxPlatform();
			#elif MACOS && DESKTOP
				Platform=Platforms.MacOS;
				PlatformHandler = new MacOS.MacOSPlatform();
            #elif WINPHONE7
                Platform = Platforms.WindowsPhone7;
                PlatformHandler = new WindowsPhone7.WindowsPhone7Platform();
            PlatformHelper = new WindowsPhone7.WindowsPhone7Helper();
            #elif WINPHONE8
                Platform = Platforms.WindowsPhone8;
                PlatformHandler = new WindowsPhone8.WindowsPhone8Platform();
                PlatformHelper = new WindowsPhone8.WindowsPhone8Helper();
            #elif ANDROID
                Platform = Platforms.Android;
                PlatformHandler = new Android.AndroidPlatform();
			#elif IOS
				Platform = Platforms.IOS;
				PlatformHandler = new IOS.IOSPlatform();
            #endif

            if (PlatformHandler == null)
                throw new Exception("Unsupported platform!");

            // find dot.net framework.
            DotNetFramework = IsRunningOnMono() ? NetFrameworks.Mono : NetFrameworks.DotNet;

            // find dot.net framework and game framework version.
            #if METRO
                DotNetFrameworkVersion = System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(Object)).Assembly.GetName().Version;
                GameFrameworkVersion = System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(Microsoft.Xna.Framework.Game)).Assembly.GetName().Version;
            #else
                DotNetFrameworkVersion = Environment.Version;
                #if WINPHONE7 || WINPHONE8
                    GameFrameworkVersion = new Version(typeof(Microsoft.Xna.Framework.Game).Assembly.FullName.Split(',')[1].Split('=')[1]);
                #else
                    GameFrameworkVersion = System.Reflection.Assembly.GetAssembly(typeof(Microsoft.Xna.Framework.Game)).GetName().Version;
                #endif
            #endif

            // find game framework & graphics-api.
            #if XNA
                GameFramework = GameFrameworks.XNA;
                GraphicsApi = GraphicsAPI.DirectX9;
            #elif MONOGAME
                GameFramework = GameFrameworks.MonoGame;
                #if DIRECTX11
                    GraphicsApi = GraphicsAPI.DirectX11;
                #elif OPENGL
                    GraphicsApi = GraphicsAPI.OpenGL;
                #endif
            #endif
        }

        /// <summary>
        /// Returns true if code runs over Mono framework.
        /// </summary>
        /// <returns>true if running over Mono, false otherwise.</returns>
        public static bool IsRunningOnMono()
        {
            return Type.GetType("Mono.Runtime") != null;
        }
    }
}

To get PlatformManager working we have to make two basic calls, the PlatformManager.Startup() and PlatformManager.Initialize(). Startup() call should be made from Program.cs for platforms that have a Main() entrance point where you run the game.

using Frenzied.Platforms;

namespace Frenzied
{
    /// <summary>
    /// The main class.
    /// </summary>
    public static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
		static void Main()
        {
            PlatformManager.Startup();
        }
    }
}

And in Game.cs (or what-ever you call the Game class) within Initialize() function you have to call PlatformManager.Initialize().

namespace Frenzied
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class FrenziedGame : Game
    {        
        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            ...

            PlatformManager.Initialize(this._graphicsDeviceManager);       

            ...

            base.Initialize();
        }
    }
}

So PlatformManager will be doing all the hard-work for us. Note that how we handle it specific PlatformHandler and PlatformHelpers instances for each platform.

PlatformHandler is the base class that we can extend to implement our platform specific initialization code and so;

using Microsoft.Xna.Framework;

namespace Frenzied.Platforms
{
    public class PlatformHandler
    {
        protected GraphicsDeviceManager GraphicsDeviceManager;

        public PlatformConfig PlatformConfig;

        public virtual void PlatformEntrance() 
        { }

        public virtual void Initialize(GraphicsDeviceManager graphicsDeviceManager)
        { }
    }
}

Note the PlatformConfig field that allows us to use platform-specific configuration. So you can for example enable mouse for a platform and disable it for mobiles.

namespace Frenzied.Platforms
{
    public class PlatformConfig
    {
        /// <summary>
        /// Gets or sets if mouse is visible for the platform.
        /// </summary>
        public bool IsMouseVisible { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether to use fixed time steps.
        /// </summary>
        public bool IsFixedTimeStep { get; set; }

        public GraphicsConfig Graphics { get; private set; }

        /// <summary>
        /// Creates a new instance of platform-config.
        /// </summary>
        public PlatformConfig()
        {
            // init. sub-configs.
            this.Graphics = new GraphicsConfig();

            // set the defaults.
            this.IsMouseVisible = false;
            this.IsFixedTimeStep = false;
        }
    }
}

PlatformHelper is the base class that we can extend to implement common functionality that needs platform specific code. The best example that comes our mind probably is launching default web-browser.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Frenzied.Platforms
{
    public class PlatformHelper
    {
        public virtual void LaunchURI(string url)
        { }
    }
}

Let’s start implementing our handlers and helpers for each platforms;

Windows-Desktop

using Microsoft.Xna.Framework;

namespace Frenzied.Platforms.Windows
{
    public class WindowsPlatform : PlatformHandler
    {
        public WindowsPlatform()
        {
            this.PlatformConfig = new PlatformConfig()
                {
                    IsMouseVisible = true,
                    IsFixedTimeStep = false,
                    Graphics = {ExtendedEffects = true},
                };
        }

        public override void PlatformEntrance()
        {
            using (var game = new FrenziedGame())
            {
                game.Run();
            }
        }

        public override void Initialize(GraphicsDeviceManager graphicsDeviceManager)
        {
            this.GraphicsDeviceManager = graphicsDeviceManager;

            this.GraphicsDeviceManager.PreferredBackBufferWidth = 1280;
            this.GraphicsDeviceManager.PreferredBackBufferHeight = 720;
            this.GraphicsDeviceManager.ApplyChanges();
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Frenzied.Platforms.Windows
{
    public class WindowsHelper : PlatformHelper
    {
        public override void LaunchURI(string url)
        {
            System.Diagnostics.Process.Start(url);
        }
    }
}

Windows-Metro

using Microsoft.Xna.Framework;

namespace Frenzied.Platforms.WindowsMetro
{
    public class WindowsMetroPlatform : PlatformHandler
    {
        public WindowsMetroPlatform()
        {
            this.PlatformConfig = new PlatformConfig()
            {
                IsMouseVisible = true,
                IsFixedTimeStep = false,
                Graphics = { ExtendedEffects = true },
            };
        }

        public override void PlatformEntrance()
        {
            var factory = new MonoGame.Framework.GameFrameworkViewSource<FrenziedGame>();
            Windows.ApplicationModel.Core.CoreApplication.Run(factory);
        }

        public override void Initialize(GraphicsDeviceManager graphicsDeviceManager)
        {
            this.GraphicsDeviceManager = graphicsDeviceManager;
        }
    }
}
using System;
using Windows.System;

namespace Frenzied.Platforms.WindowsMetro
{
    public class WindowsMetroHelper : PlatformHelper
    {
        public async override void LaunchURI(string url)
        {
            await Launcher.LaunchUriAsync(new Uri(url));
        }
    }
}

 Windows Phone 7

using Microsoft.Xna.Framework;

namespace Frenzied.Platforms.WindowsPhone7
{
    public class WindowsPhone7Platform : PlatformHandler
    {
        public WindowsPhone7Platform()
        {
            this.PlatformConfig = new PlatformConfig
                {
                    IsMouseVisible = false,
                    IsFixedTimeStep = false,
                    Graphics = {ExtendedEffects = false},
                };
        }

        public override void PlatformEntrance()
        {
            using (var game = new FrenziedGame())
            {
                game.Run();
            }
        }

        public override void Initialize(GraphicsDeviceManager graphicsDeviceManager)
        {
            this.GraphicsDeviceManager = graphicsDeviceManager;

            this.GraphicsDeviceManager.SupportedOrientations = DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight;
            this.GraphicsDeviceManager.PreferredBackBufferWidth = 800;
            this.GraphicsDeviceManager.PreferredBackBufferHeight = 480;
            this.GraphicsDeviceManager.IsFullScreen = true;
            this.GraphicsDeviceManager.ApplyChanges();
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Phone.Tasks;

namespace Frenzied.Platforms.WindowsPhone7
{
    public class WindowsPhone7Helper : PlatformHelper
    {
        public override void LaunchURI(string url)
        {
            var task = new WebBrowserTask {Uri = new Uri(url)};
            task.Show();
        }
    }
}

 Windows Phone 8

using Microsoft.Xna.Framework;

namespace Frenzied.Platforms.WindowsPhone8
{
    public class WindowsPhone8Platform : PlatformHandler
    {
        public WindowsPhone8Platform()
        {
            this.PlatformConfig = new PlatformConfig()
            {
                IsMouseVisible = false,
                IsFixedTimeStep = false,
                Graphics = { ExtendedEffects = true },
            };
        }

        public override void Initialize(GraphicsDeviceManager graphicsDeviceManager)
        {
            this.GraphicsDeviceManager = graphicsDeviceManager;

            this.GraphicsDeviceManager.IsFullScreen = true;
            this.GraphicsDeviceManager.SupportedOrientations = DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight;            
            this.GraphicsDeviceManager.PreferredBackBufferWidth = 1280;
            this.GraphicsDeviceManager.PreferredBackBufferHeight = 720;
            this.GraphicsDeviceManager.ApplyChanges();
        }
    }
}
using System;
using Windows.System;

namespace Frenzied.Platforms.WindowsPhone8
{
    public class WindowsPhone8Helper : PlatformHelper
    {
        public async override void LaunchURI(string url)
        {
            await Launcher.LaunchUriAsync(new Uri(url));
        }
    }
}

Android

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Microsoft.Xna.Framework;

namespace Frenzied.Platforms.Android
{
    public class AndroidPlatform : PlatformHandler
    {
        public AndroidPlatform()
        {
            this.PlatformConfig = new PlatformConfig()
                {
                    IsMouseVisible = false,
                    IsFixedTimeStep = false,
                };
        }

        public override void Initialize(GraphicsDeviceManager graphicsDeviceManager)
        {
            this.GraphicsDeviceManager = graphicsDeviceManager;

            this.GraphicsDeviceManager.PreferredBackBufferWidth = 800;
            this.GraphicsDeviceManager.PreferredBackBufferHeight = 480;
            this.GraphicsDeviceManager.SupportedOrientations = DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight;
            this.GraphicsDeviceManager.ApplyChanges();
        }
    }
}

 IOS

using System;
using Microsoft.Xna.Framework;

namespace Frenzied.Platforms.IOS
{
	public class IOSPlatform : PlatformHandler 
	{
		public IOSPlatform()
		{
			this.PlatformConfig = new PlatformConfig()
			{
				IsMouseVisible = false,
				IsFixedTimeStep = false,
			};
		}

		public override void PlatformEntrance()
		{
			FrenziedGame game;

			game = new FrenziedGame ();
			game.Run ();
		}

		public override void Initialize(GraphicsDeviceManager graphicsDeviceManager)
		{
			this.GraphicsDeviceManager = graphicsDeviceManager;

			this.GraphicsDeviceManager.PreferredBackBufferWidth = 1280;
			this.GraphicsDeviceManager.PreferredBackBufferHeight = 720;
			this.GraphicsDeviceManager.ApplyChanges();
		}
	}
}

 Linux

using System;
using Microsoft.Xna.Framework;

namespace Frenzied.Platforms.Linux
{
	public class LinuxPlatform : PlatformHandler 
	{
        public LinuxPlatform()
        {
            this.PlatformConfig = new PlatformConfig()
                {
                    IsMouseVisible = true,
                    IsFixedTimeStep = false,
                };
        }

        public override void PlatformEntrance()
        {
            using (var game = new FrenziedGame())
            {
                game.Run();
            }
        }

        public override void Initialize(GraphicsDeviceManager graphicsDeviceManager)
        {
            this.GraphicsDeviceManager = graphicsDeviceManager;

            this.GraphicsDeviceManager.PreferredBackBufferWidth = 1280;
            this.GraphicsDeviceManager.PreferredBackBufferHeight = 720;
            this.GraphicsDeviceManager.ApplyChanges();
        }
	}
}

 MacOS

using System;
using Microsoft.Xna.Framework;
using MonoMac.AppKit;
using MonoMac.Foundation;

namespace Frenzied.Platforms.MacOS
{
	public class MacOSPlatform : PlatformHandler 
	{
		public MacOSPlatform()
		{
			this.PlatformConfig = new PlatformConfig()
			{
				IsMouseVisible = true,
				IsFixedTimeStep = false,
			};
		}

		public override void PlatformEntrance()
		{
			NSApplication.Init ();

			using (var p = new NSAutoreleasePool ()) {
				NSApplication.SharedApplication.Delegate = new AppDelegate ();
				NSApplication.Main (null);
			}
		}

		public override void Initialize(GraphicsDeviceManager graphicsDeviceManager)
		{
			this.GraphicsDeviceManager = graphicsDeviceManager;

			this.GraphicsDeviceManager.PreferredBackBufferWidth = 1280;
			this.GraphicsDeviceManager.PreferredBackBufferHeight = 720;
			this.GraphicsDeviceManager.ApplyChanges();
		}
	}

	class AppDelegate : NSApplicationDelegate
	{
		FrenziedGame game;

		public override void FinishedLaunching (MonoMac.Foundation.NSObject notification)
		{
			game = new FrenziedGame ();
			game.Run ();
		}

		public override bool ApplicationShouldTerminateAfterLastWindowClosed (NSApplication sender)
		{
			return true;
		}
	}  
}

That’s all! Now we can;

  • Manage our target platforms all together and have seperate platform specific code.
  • Have platform specific configuration (ie, hiding the mouse for specific platforms).
  • Have platform specific code to run behind scenes to implement common functionality (like opening a given URI in default web-browser).

I hope you enjoyed the article, if so please spread/tweet the word 🙂 If you have any questions please don’t hesitate to send a comment!

Note: I did not implement PlatformHelpers for IOS, Android, MacOS, Linux yet but should be pretty easy to do so!

We have decided to use the awesome gource to visualize the history of MonoGame‘s github repository and the output is fascinating!

So here is how we did;

Step 1 – fetching avatars

To download available avatars for commiters, we used the gravatar script provided in gource’s related wiki page.

#!/usr/bin/perl
#fetch Gravatars

use strict;
use warnings;

use LWP::Simple;
use Digest::MD5 qw(md5_hex);
use Thread::Pool::Simple;

my $size       = 90;
my $output_dir = '.git/avatar';

system("no .git/ directory found in current path\n") unless -d '.git';

mkdir($output_dir) unless -d $output_dir;

open(GITLOG, q/git log --pretty=format:"%ae|%an" |/) or die("failed to read git-log: $!\n");

my %processed_authors;

sub fetch
{
    #try and fetch image
    my ($name, $email, $author_image_file) = @_;
    my $grav_url = "http://www.gravatar.com/avatar/".md5_hex(lc $email)."?d=404&s=".$size;

    warn "fetching image for '$name' $email ($grav_url)...\n";

    my $rc = getstore($grav_url, $author_image_file);
    if($rc != 200) {
        unlink($author_image_file);
    }
}

my $pool = Thread::Pool::Simple->new(min => 10, max => 20, do => [\&fetch]);

while() {
    chomp;
    my($email, $author) = split(/\|/, $_);

    next if $processed_authors{$author}++;

    my $author_image_file = $output_dir . '/' . $author . '.png';
    #skip images we have
    next if -e $author_image_file;

    $pool->add(($author, $email, $author_image_file));
}
$pool->join();

close GITLOG;

 Step 2 – run the gource over git repository

E:\dev-software\gource\gource --hide filenames --title "MonoGame Git Visualization" --user-image-dir E:\Source\[email protected]\.git\avatar --logo logo.png --seconds-per-day 0.01 -1280x720 -e 0.5 -o monogame.ppm E:\Source\[email protected]

Step 3 – render the video with ffmpeg

E:\dev-software\gource\ffmpeg\bin\ffmpeg -y -r 60 -f image2pipe -vcodec ppm -i monogame.ppm -vcodec libx264 -preset ultrafast -crf 18 -pix_fmt yuv420p -threads 0 -bf 0 monogame.avi

Bingo!

 

If you aren’t aware yet, those great guys over Mono project works on creating an OpenGL based implementation of XNA framework which can target MacOS, Linux, Android and IOS (those latter two of course will require MonoDroid and MonoTouch). So their website already mentions the ever first 3d game powered by MonoGame and I couldn’t resist giving it a try to get Voxeliq running over MonoGame! Here goes the story;

It took me a few hours to figure things out. At the very first I tried my chances on wrong branch – MonoGame/develop – which at the time being is the furthest branch. Though I got my answer on irc.gnome.org #monogame, which ‘develop’ did not have support for MojoShader – which allows HLSL shaders to run over GL (it converts them actually).

So I checked out the develop3d branch as mentioned in irc channel, which was the correct branch to go for 3d games. Within my initial try, I got lots of error messages from compiler complaining about missing HalfVector2.cs. So I put in the XNA implementation, fixed a few bits here and there and all the errors were gone! When I hit run button, things started up but I got an exception where MonoGame was complaining about threaded creation of GPU resources were not supported yet (which will be fixed later). Bingo! It was all okay and I was able to run Voxeliq over MonoGame using OpenGL. So Voxeliq is not the first 3d game/engine running over MonoGame but I guess it’s one of the very earliest ones

Oh and I had to disable few bits / components (bloom effect, my music manager implementation and console which uses Digital Rune libraries that was compiled against XNA4) of Voxeliq which may need fixes – though that’s for another story I’ll be updating you guys on the progress.

PS: If you need HalfVector2 fix, you can find my patch here.

So basically, Microsoft doesn’t give a single word on future of XNA especially when you think about it within upcoming Windows 8’s context. There are two different views over people’s blogs where few of them claims that XNA reached end of it’s life where others claim that next big XNA update will be coming with new Xbox.

So I dived further in. First I’ve installed Visual Studio 11 Beta, just to see it does not support XNA projects anymore. So at least for now forget about upgrading to VS 11 Beta if you’re still developing for XNA – and I’m really not sure if Visual Studio 2010 will support targeting .Net 4.5 but I hope so.

There is a missing project subtype.
Subtype: ‘{6D335F3A-9D43-41b4-9D22-F6F17C4BE596}’ is unsupported by this installation.

After a few hours of reading, I’ve found these regarding XNA;

  • XNA apps will run perfectly in Windows 8 but only in Desktop mode.
  • You can still have an icon in Metro view to launch your app.
  • Only partial Windows 8 marketplace support, i.e. Marketplace will only provide a web link to an installer that you must host yourself and (as yet) there are many unknowns about handling transactions and security.
  • XNA apps will run on x86 and x64 but not ARM processors.
  • You can reuse your code between XBox360, Windows 7/8 and Windows Phone 7
  • Unknown whether XNA apps will run on Windows Phone Tango/Apollo

Voxeliq’s future
So basically Voxeliq owns a huge codebase which I don’t have any willing/time to port native C++/DirectX any soon. So I’ll be staying within lands of mysterious C# game development. Options available are;

  • Stay within XNA as is.
  • Port to an XNA alternative like ANX (which isn’t any complete yet)
  • Switch to a managed DirectX wrapper – ie, sharpdx or slimdx.

I’ve previously played with slimdx a lot but the bad news is that it doesn’t support upcoming DirectX 11.1 or Windows 8 Platform and it really lacks a sample game framework which I had to implement on my own (too bad I deleted the sources thinking I would never need it!).

SharpDX on the other hand is ready for DirectX 11.1 and Windows 8 platform already. Even better, there’s a good game framework sample itself – the ANX framework which is actually based on SharpDX.

So I guess I’ll be taking SharpDX way though will not be starting to port my code any near before we can get more detailed info on what’s going with XNA.