580 lines
20 KiB
C#
580 lines
20 KiB
C#
// MonoGame - Copyright (C) The MonoGame Team
|
|
// This file is subject to the terms and conditions defined in
|
|
// file 'LICENSE.txt', which is part of this source code package.
|
|
|
|
using System;
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
using Microsoft.Xna.Framework.Input.Touch;
|
|
|
|
namespace Microsoft.Xna.Framework
|
|
{
|
|
/// <summary>
|
|
/// Used to initialize and control the presentation of the graphics device.
|
|
/// </summary>
|
|
public partial class GraphicsDeviceManager : IGraphicsDeviceService, IDisposable, IGraphicsDeviceManager
|
|
{
|
|
private readonly Game _game;
|
|
private GraphicsDevice _graphicsDevice;
|
|
private bool _initialized = false;
|
|
|
|
private int _preferredBackBufferHeight;
|
|
private int _preferredBackBufferWidth;
|
|
private SurfaceFormat _preferredBackBufferFormat;
|
|
private DepthFormat _preferredDepthStencilFormat;
|
|
private bool _preferMultiSampling;
|
|
private DisplayOrientation _supportedOrientations;
|
|
private bool _synchronizedWithVerticalRetrace = true;
|
|
private bool _drawBegun;
|
|
private bool _disposed;
|
|
private bool _hardwareModeSwitch = true;
|
|
private bool _wantFullScreen;
|
|
private GraphicsProfile _graphicsProfile;
|
|
// dirty flag for ApplyChanges
|
|
private bool _shouldApplyChanges;
|
|
|
|
/// <summary>
|
|
/// The default back buffer width.
|
|
/// </summary>
|
|
public static readonly int DefaultBackBufferWidth = 800;
|
|
|
|
/// <summary>
|
|
/// The default back buffer height.
|
|
/// </summary>
|
|
public static readonly int DefaultBackBufferHeight = 480;
|
|
|
|
/// <summary>
|
|
/// Optional override for platform specific defaults.
|
|
/// </summary>
|
|
partial void PlatformConstruct();
|
|
|
|
/// <summary>
|
|
/// Associates this graphics device manager to a game instances.
|
|
/// </summary>
|
|
/// <param name="game">The game instance to attach.</param>
|
|
public GraphicsDeviceManager(Game game)
|
|
{
|
|
if (game == null)
|
|
throw new ArgumentNullException("game", "Game cannot be null.");
|
|
|
|
_game = game;
|
|
|
|
_supportedOrientations = DisplayOrientation.Default;
|
|
_preferredBackBufferFormat = SurfaceFormat.Color;
|
|
_preferredDepthStencilFormat = DepthFormat.Depth24;
|
|
_synchronizedWithVerticalRetrace = true;
|
|
|
|
// Assume the window client size as the default back
|
|
// buffer resolution in the landscape orientation.
|
|
var clientBounds = _game.Window.ClientBounds;
|
|
if (clientBounds.Width >= clientBounds.Height)
|
|
{
|
|
_preferredBackBufferWidth = clientBounds.Width;
|
|
_preferredBackBufferHeight = clientBounds.Height;
|
|
}
|
|
else
|
|
{
|
|
_preferredBackBufferWidth = clientBounds.Height;
|
|
_preferredBackBufferHeight = clientBounds.Width;
|
|
}
|
|
|
|
// Default to windowed mode... this is ignored on platforms that don't support it.
|
|
_wantFullScreen = false;
|
|
|
|
// XNA would read this from the manifest, but it would always default
|
|
// to reach unless changed. So lets mimic that without the manifest bit.
|
|
GraphicsProfile = GraphicsProfile.Reach;
|
|
|
|
// Let the plaform optionally overload construction defaults.
|
|
PlatformConstruct();
|
|
|
|
if (_game.Services.GetService(typeof(IGraphicsDeviceManager)) != null)
|
|
throw new ArgumentException("A graphics device manager is already registered. The graphics device manager cannot be changed once it is set.");
|
|
_game.graphicsDeviceManager = this;
|
|
|
|
_game.Services.AddService(typeof(IGraphicsDeviceManager), this);
|
|
_game.Services.AddService(typeof(IGraphicsDeviceService), this);
|
|
}
|
|
|
|
~GraphicsDeviceManager()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
|
|
private void CreateDevice()
|
|
{
|
|
if (_graphicsDevice != null)
|
|
return;
|
|
|
|
try
|
|
{
|
|
if (!_initialized)
|
|
Initialize();
|
|
|
|
var gdi = DoPreparingDeviceSettings();
|
|
CreateDevice(gdi);
|
|
}
|
|
catch (NoSuitableGraphicsDeviceException)
|
|
{
|
|
throw;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new NoSuitableGraphicsDeviceException("Failed to create graphics device!", ex);
|
|
}
|
|
}
|
|
|
|
private void CreateDevice(GraphicsDeviceInformation gdi)
|
|
{
|
|
if (_graphicsDevice != null)
|
|
return;
|
|
|
|
_graphicsDevice = new GraphicsDevice(gdi);
|
|
_shouldApplyChanges = false;
|
|
|
|
// hook up reset events
|
|
GraphicsDevice.DeviceReset += (sender, args) => OnDeviceReset(args);
|
|
GraphicsDevice.DeviceResetting += (sender, args) => OnDeviceResetting(args);
|
|
|
|
// update the touchpanel display size when the graphicsdevice is reset
|
|
_graphicsDevice.DeviceReset += UpdateTouchPanel;
|
|
_graphicsDevice.PresentationChanged += OnPresentationChanged;
|
|
|
|
OnDeviceCreated(EventArgs.Empty);
|
|
}
|
|
|
|
void IGraphicsDeviceManager.CreateDevice()
|
|
{
|
|
CreateDevice();
|
|
}
|
|
|
|
public bool BeginDraw()
|
|
{
|
|
if (_graphicsDevice == null)
|
|
return false;
|
|
|
|
_drawBegun = true;
|
|
return true;
|
|
}
|
|
|
|
public void EndDraw()
|
|
{
|
|
if (_graphicsDevice != null && _drawBegun)
|
|
{
|
|
_drawBegun = false;
|
|
_graphicsDevice.Present();
|
|
}
|
|
}
|
|
|
|
#region IGraphicsDeviceService Members
|
|
|
|
public event EventHandler<EventArgs> DeviceCreated;
|
|
public event EventHandler<EventArgs> DeviceDisposing;
|
|
public event EventHandler<EventArgs> DeviceReset;
|
|
public event EventHandler<EventArgs> DeviceResetting;
|
|
public event EventHandler<PreparingDeviceSettingsEventArgs> PreparingDeviceSettings;
|
|
public event EventHandler<EventArgs> Disposed;
|
|
|
|
protected void OnDeviceDisposing(EventArgs e)
|
|
{
|
|
EventHelpers.Raise(this, DeviceDisposing, e);
|
|
}
|
|
|
|
protected void OnDeviceResetting(EventArgs e)
|
|
{
|
|
EventHelpers.Raise(this, DeviceResetting, e);
|
|
}
|
|
|
|
internal void OnDeviceReset(EventArgs e)
|
|
{
|
|
EventHelpers.Raise(this, DeviceReset, e);
|
|
}
|
|
|
|
internal void OnDeviceCreated(EventArgs e)
|
|
{
|
|
EventHelpers.Raise(this, DeviceCreated, e);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This populates a GraphicsDeviceInformation instance and invokes PreparingDeviceSettings to
|
|
/// allow users to change the settings. Then returns that GraphicsDeviceInformation.
|
|
/// Throws NullReferenceException if users set GraphicsDeviceInformation.PresentationParameters to null.
|
|
/// </summary>
|
|
private GraphicsDeviceInformation DoPreparingDeviceSettings()
|
|
{
|
|
var gdi = new GraphicsDeviceInformation();
|
|
PrepareGraphicsDeviceInformation(gdi);
|
|
var preparingDeviceSettingsHandler = PreparingDeviceSettings;
|
|
|
|
if (preparingDeviceSettingsHandler != null)
|
|
{
|
|
// this allows users to overwrite settings through the argument
|
|
var args = new PreparingDeviceSettingsEventArgs(gdi);
|
|
preparingDeviceSettingsHandler(this, args);
|
|
|
|
if (gdi.PresentationParameters == null || gdi.Adapter == null)
|
|
throw new NullReferenceException("Members should not be set to null in PreparingDeviceSettingsEventArgs");
|
|
}
|
|
|
|
return gdi;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IDisposable Members
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (!_disposed)
|
|
{
|
|
if (disposing)
|
|
{
|
|
if (_graphicsDevice != null)
|
|
{
|
|
_graphicsDevice.Dispose();
|
|
_graphicsDevice = null;
|
|
}
|
|
}
|
|
_disposed = true;
|
|
EventHelpers.Raise(this, Disposed, EventArgs.Empty);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
partial void PlatformApplyChanges();
|
|
|
|
partial void PlatformPreparePresentationParameters(PresentationParameters presentationParameters);
|
|
|
|
private void PreparePresentationParameters(PresentationParameters presentationParameters)
|
|
{
|
|
presentationParameters.BackBufferFormat = _preferredBackBufferFormat;
|
|
presentationParameters.BackBufferWidth = _preferredBackBufferWidth;
|
|
presentationParameters.BackBufferHeight = _preferredBackBufferHeight;
|
|
presentationParameters.DepthStencilFormat = _preferredDepthStencilFormat;
|
|
presentationParameters.IsFullScreen = _wantFullScreen;
|
|
presentationParameters.HardwareModeSwitch = _hardwareModeSwitch;
|
|
presentationParameters.PresentationInterval = _synchronizedWithVerticalRetrace ? PresentInterval.One : PresentInterval.Immediate;
|
|
presentationParameters.DisplayOrientation = _game.Window.CurrentOrientation;
|
|
#if WINDOWS && DIRECTX
|
|
presentationParameters.DeviceWindowHandle = _game.Window.Hwnd;
|
|
#else
|
|
presentationParameters.DeviceWindowHandle = _game.Window.Handle;
|
|
#endif
|
|
|
|
if (_preferMultiSampling)
|
|
{
|
|
// always initialize MultiSampleCount to the maximum, if users want to overwrite
|
|
// this they have to respond to the PreparingDeviceSettingsEvent and modify
|
|
// args.GraphicsDeviceInformation.PresentationParameters.MultiSampleCount
|
|
presentationParameters.MultiSampleCount = GraphicsDevice != null
|
|
? GraphicsDevice.GraphicsCapabilities.MaxMultiSampleCount
|
|
: 32;
|
|
}
|
|
else
|
|
{
|
|
presentationParameters.MultiSampleCount = 0;
|
|
}
|
|
|
|
PlatformPreparePresentationParameters(presentationParameters);
|
|
}
|
|
|
|
private void PrepareGraphicsDeviceInformation(GraphicsDeviceInformation gdi)
|
|
{
|
|
gdi.Adapter = GraphicsAdapter.DefaultAdapter;
|
|
gdi.GraphicsProfile = GraphicsProfile;
|
|
var pp = new PresentationParameters();
|
|
PreparePresentationParameters(pp);
|
|
gdi.PresentationParameters = pp;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applies any pending property changes to the graphics device.
|
|
/// </summary>
|
|
public void ApplyChanges()
|
|
{
|
|
// If the device hasn't been created then create it now.
|
|
if (_graphicsDevice == null)
|
|
CreateDevice();
|
|
|
|
if (!_shouldApplyChanges)
|
|
return;
|
|
|
|
_shouldApplyChanges = false;
|
|
|
|
_game.Window.SetSupportedOrientations(_supportedOrientations);
|
|
|
|
// Allow for optional platform specific behavior.
|
|
PlatformApplyChanges();
|
|
|
|
// populates a gdi with settings in this gdm and allows users to override them with
|
|
// PrepareDeviceSettings event this information should be applied to the GraphicsDevice
|
|
var gdi = DoPreparingDeviceSettings();
|
|
|
|
if (gdi.GraphicsProfile != GraphicsDevice.GraphicsProfile)
|
|
{
|
|
// if the GraphicsProfile changed we need to create a new GraphicsDevice
|
|
DisposeGraphicsDevice();
|
|
CreateDevice(gdi);
|
|
return;
|
|
}
|
|
|
|
GraphicsDevice.Reset(gdi.PresentationParameters);
|
|
}
|
|
|
|
private void DisposeGraphicsDevice()
|
|
{
|
|
_graphicsDevice.Dispose();
|
|
EventHelpers.Raise(this, DeviceDisposing, EventArgs.Empty);
|
|
_graphicsDevice = null;
|
|
}
|
|
|
|
partial void PlatformInitialize(PresentationParameters presentationParameters);
|
|
|
|
private void Initialize()
|
|
{
|
|
_game.Window.SetSupportedOrientations(_supportedOrientations);
|
|
|
|
var presentationParameters = new PresentationParameters();
|
|
PreparePresentationParameters(presentationParameters);
|
|
|
|
// Allow for any per-platform changes to the presentation.
|
|
PlatformInitialize(presentationParameters);
|
|
|
|
_initialized = true;
|
|
}
|
|
|
|
private void UpdateTouchPanel(object sender, EventArgs eventArgs)
|
|
{
|
|
TouchPanel.DisplayWidth = _graphicsDevice.PresentationParameters.BackBufferWidth;
|
|
TouchPanel.DisplayHeight = _graphicsDevice.PresentationParameters.BackBufferHeight;
|
|
TouchPanel.DisplayOrientation = _graphicsDevice.PresentationParameters.DisplayOrientation;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Toggles between windowed and fullscreen modes.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Note that on platforms that do not support windowed modes this has no affect.
|
|
/// </remarks>
|
|
public void ToggleFullScreen()
|
|
{
|
|
IsFullScreen = !IsFullScreen;
|
|
ApplyChanges();
|
|
}
|
|
|
|
private void OnPresentationChanged(object sender, PresentationEventArgs args)
|
|
{
|
|
_game.Platform.OnPresentationChanged(args.PresentationParameters);
|
|
}
|
|
|
|
/// <summary>
|
|
/// The profile which determines the graphics feature level.
|
|
/// </summary>
|
|
public GraphicsProfile GraphicsProfile
|
|
{
|
|
get
|
|
{
|
|
return _graphicsProfile;
|
|
}
|
|
set
|
|
{
|
|
_shouldApplyChanges = true;
|
|
_graphicsProfile = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the graphics device for this manager.
|
|
/// </summary>
|
|
public GraphicsDevice GraphicsDevice
|
|
{
|
|
get
|
|
{
|
|
return _graphicsDevice;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates the desire to switch into fullscreen mode.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// When called at startup this will automatically set fullscreen mode during initialization. If
|
|
/// set after startup you must call ApplyChanges() for the fullscreen mode to be changed.
|
|
/// Note that for some platforms that do not support windowed modes this property has no affect.
|
|
/// </remarks>
|
|
public bool IsFullScreen
|
|
{
|
|
get { return _wantFullScreen; }
|
|
set
|
|
{
|
|
_shouldApplyChanges = true;
|
|
_wantFullScreen = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the boolean which defines how window switches from windowed to fullscreen state.
|
|
/// "Hard" mode(true) is slow to switch, but more effecient for performance, while "soft" mode(false) is vice versa.
|
|
/// The default value is <c>true</c>.
|
|
/// </summary>
|
|
public bool HardwareModeSwitch
|
|
{
|
|
get { return _hardwareModeSwitch;}
|
|
set
|
|
{
|
|
_shouldApplyChanges = true;
|
|
_hardwareModeSwitch = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates the desire for a multisampled back buffer.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// When called at startup this will automatically set the MSAA mode during initialization. If
|
|
/// set after startup you must call ApplyChanges() for the MSAA mode to be changed.
|
|
/// </remarks>
|
|
public bool PreferMultiSampling
|
|
{
|
|
get
|
|
{
|
|
return _preferMultiSampling;
|
|
}
|
|
set
|
|
{
|
|
_shouldApplyChanges = true;
|
|
_preferMultiSampling = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates the desired back buffer color format.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// When called at startup this will automatically set the format during initialization. If
|
|
/// set after startup you must call ApplyChanges() for the format to be changed.
|
|
/// </remarks>
|
|
public SurfaceFormat PreferredBackBufferFormat
|
|
{
|
|
get
|
|
{
|
|
return _preferredBackBufferFormat;
|
|
}
|
|
set
|
|
{
|
|
_shouldApplyChanges = true;
|
|
_preferredBackBufferFormat = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates the desired back buffer height in pixels.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// When called at startup this will automatically set the height during initialization. If
|
|
/// set after startup you must call ApplyChanges() for the height to be changed.
|
|
/// </remarks>
|
|
public int PreferredBackBufferHeight
|
|
{
|
|
get
|
|
{
|
|
return _preferredBackBufferHeight;
|
|
}
|
|
set
|
|
{
|
|
_shouldApplyChanges = true;
|
|
_preferredBackBufferHeight = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates the desired back buffer width in pixels.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// When called at startup this will automatically set the width during initialization. If
|
|
/// set after startup you must call ApplyChanges() for the width to be changed.
|
|
/// </remarks>
|
|
public int PreferredBackBufferWidth
|
|
{
|
|
get
|
|
{
|
|
return _preferredBackBufferWidth;
|
|
}
|
|
set
|
|
{
|
|
_shouldApplyChanges = true;
|
|
_preferredBackBufferWidth = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates the desired depth-stencil buffer format.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The depth-stencil buffer format defines the scene depth precision and stencil bits available for effects during rendering.
|
|
/// When called at startup this will automatically set the format during initialization. If
|
|
/// set after startup you must call ApplyChanges() for the format to be changed.
|
|
/// </remarks>
|
|
public DepthFormat PreferredDepthStencilFormat
|
|
{
|
|
get
|
|
{
|
|
return _preferredDepthStencilFormat;
|
|
}
|
|
set
|
|
{
|
|
_shouldApplyChanges = true;
|
|
_preferredDepthStencilFormat = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates the desire for vsync when presenting the back buffer.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Vsync limits the frame rate of the game to the monitor referesh rate to prevent screen tearing.
|
|
/// When called at startup this will automatically set the vsync mode during initialization. If
|
|
/// set after startup you must call ApplyChanges() for the vsync mode to be changed.
|
|
/// </remarks>
|
|
public bool SynchronizeWithVerticalRetrace
|
|
{
|
|
get
|
|
{
|
|
return _synchronizedWithVerticalRetrace;
|
|
}
|
|
set
|
|
{
|
|
_shouldApplyChanges = true;
|
|
_synchronizedWithVerticalRetrace = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates the desired allowable display orientations when the device is rotated.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This property only applies to mobile platforms with automatic display rotation.
|
|
/// When called at startup this will automatically apply the supported orientations during initialization. If
|
|
/// set after startup you must call ApplyChanges() for the supported orientations to be changed.
|
|
/// </remarks>
|
|
public DisplayOrientation SupportedOrientations
|
|
{
|
|
get
|
|
{
|
|
return _supportedOrientations;
|
|
}
|
|
set
|
|
{
|
|
_shouldApplyChanges = true;
|
|
_supportedOrientations = value;
|
|
}
|
|
}
|
|
}
|
|
}
|