Sunday, February 15, 2009

XNA / C# – How To Create Full-Screen Graphics Device / Exclusive Mode On Multiple Monitors

When making an XNA game application, it is pretty easy to make your game fullscreen one of two ways:

  1. The easy way is simply call GraphicsDeviceManager.ToggleFullScreen (http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphicsdevicemanager.togglefullscreen.aspx)
  2. A little bit less easy is to initialize youre GraphicsDeviceInformation.PresentationParameters.IsFullScreen property to true (http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.presentationparameters.isfullscreen.aspx)

But what if you're making a PC game / Flight Sim / Driving Sim / Dizzying shoot-em-up or whatever and you want to use two or more monitors. A single game window can't span multiple monitors without a deal-breaker of a performance hit (the reason why is a long answer). Does Xbox support multiple monitors anyway?

What you can do is create multiple Game instances, each with their own GraphicsDeviceManager instance, each using a different graphics device. That's the "hard" part – see below.
But wait? For some reason, only one game window will create in exclusive mode. What gives?
Truthfully, I dunno, yet. (.NET Reflector won't expose any answers either)

I made it work this way:

  • First I force all graphics devices to create in windowed mode, mapped to different graphics devices
  • Second, on the first Game.Update I call GraphicsDeviceManager.ToggleFullScreen()
  • Viola! Full-screen XNA presentation on every monitor.

Some may say why bother with full-screen mode if I can just make a windowed mode graphics device big enough to cover the entire screen on both monitors? IMO (without any real proof right now) your 3D Graphics card will perform better in exclusive mode than in windows mode at the same resolution. Probably because the hardware has a lot fewer bits to move around. Even if your $100 nVidia card can move 30GB / sec in graphics RAM, it's gotta be slowing things down somewhere to let windows figure out what to paint.

How to make multiple game windows in the first place

(I'm not posting the code just yet)

  1. Override the GraphicsDeviceManager class
    1. In the constructor, hook into the PreparingDeviceSettings event
    2. In the event handler for PreparingDeviceSettings, set the presentation parameters; be sure IsFullScreen = false and FullScreenRefreshRateInHz is 0 (zero)
    3. Override RankDevices; remove all devices where the Adapter.DeviceName is not the window you want this graphics device to render on
  2. Override the Game class
    1. In the constructor, create your custom GraphicsDeviceManager; be sure you can instruct your GraphicsDeviceManager which graphics device name to use
  3. Each Game Window monitors requires its own thread
    1. In your program startup / main, for each monitor create UI a thread. A couple of good ideas
      1. Set the thread's IsBackground = false before you start the thread
      2. Create a ManualResetEvent for each thread and do a try / catch / finally block in the thread function such that in the finally block you set event. This is so that…
      3. After creating all of the threads, don't let you app quit just yet. Call WaitHandle.WaitAll() with an array of the ManualResetEvent handles for each of the monitor threads
    2. Create your custom Game object in this Game Window worker function
    3. The Game object will create a GameWindow (WinForms). You gotta have this thread to handle the message loop. Each form for each window needs its own message loop
    4. Yes, this means that the GameTime for each of the game windows will be slightly different

2 comments:

  1. Hey John, Scott here. Long time no see. I just came across your blog here and wanted to comment on XNA. I love it! I have a great side-side project that incorporates F# to build meshing dynamically. Later man, check out my blog on here too, vesticator.blogspot.com

    ReplyDelete
  2. Hello John.

    I am trying to build a multiple monitor application. I'm following your instructions, but being new to XNA I'm having some trouble with then. Could you provide more information about each step (either code or helpful documentation) would be great.

    Thank you, Ricardo Jota

    ReplyDelete