|
|
#region License
|
|
|
/* FNA - XNA4 Reimplementation for Desktop Platforms
|
|
|
* Copyright 2009-2020 Ethan Lee and the MonoGame Team
|
|
|
*
|
|
|
* Released under the Microsoft Public License.
|
|
|
* See LICENSE for details.
|
|
|
*/
|
|
|
#endregion
|
|
|
|
|
|
#region Using Statements
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
|
#endregion
|
|
|
|
|
|
namespace Microsoft.Xna.Framework
|
|
|
{
|
|
|
public class GraphicsDeviceManager : IGraphicsDeviceService, IDisposable, IGraphicsDeviceManager
|
|
|
{
|
|
|
#region Public Properties
|
|
|
|
|
|
public GraphicsProfile GraphicsProfile
|
|
|
{
|
|
|
get;
|
|
|
set;
|
|
|
}
|
|
|
|
|
|
public GraphicsDevice GraphicsDevice
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
return graphicsDevice;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private bool INTERNAL_isFullScreen;
|
|
|
public bool IsFullScreen
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
return INTERNAL_isFullScreen;
|
|
|
}
|
|
|
set
|
|
|
{
|
|
|
INTERNAL_isFullScreen = value;
|
|
|
prefsChanged = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private bool INTERNAL_preferMultiSampling;
|
|
|
public bool PreferMultiSampling
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
return INTERNAL_preferMultiSampling;
|
|
|
}
|
|
|
set
|
|
|
{
|
|
|
INTERNAL_preferMultiSampling = value;
|
|
|
prefsChanged = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private SurfaceFormat INTERNAL_preferredBackBufferFormat;
|
|
|
public SurfaceFormat PreferredBackBufferFormat
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
return INTERNAL_preferredBackBufferFormat;
|
|
|
}
|
|
|
set
|
|
|
{
|
|
|
INTERNAL_preferredBackBufferFormat = value;
|
|
|
prefsChanged = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private int INTERNAL_preferredBackBufferHeight;
|
|
|
public int PreferredBackBufferHeight
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
return INTERNAL_preferredBackBufferHeight;
|
|
|
}
|
|
|
set
|
|
|
{
|
|
|
INTERNAL_preferredBackBufferHeight = value;
|
|
|
prefsChanged = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private int INTERNAL_preferredBackBufferWidth;
|
|
|
public int PreferredBackBufferWidth
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
return INTERNAL_preferredBackBufferWidth;
|
|
|
}
|
|
|
set
|
|
|
{
|
|
|
INTERNAL_preferredBackBufferWidth = value;
|
|
|
prefsChanged = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private DepthFormat INTERNAL_preferredDepthStencilFormat;
|
|
|
public DepthFormat PreferredDepthStencilFormat
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
return INTERNAL_preferredDepthStencilFormat;
|
|
|
}
|
|
|
set
|
|
|
{
|
|
|
INTERNAL_preferredDepthStencilFormat = value;
|
|
|
prefsChanged = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private bool INTERNAL_synchronizeWithVerticalRetrace;
|
|
|
public bool SynchronizeWithVerticalRetrace
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
return INTERNAL_synchronizeWithVerticalRetrace;
|
|
|
}
|
|
|
set
|
|
|
{
|
|
|
INTERNAL_synchronizeWithVerticalRetrace = value;
|
|
|
prefsChanged = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private DisplayOrientation INTERNAL_supportedOrientations;
|
|
|
public DisplayOrientation SupportedOrientations
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
return INTERNAL_supportedOrientations;
|
|
|
}
|
|
|
set
|
|
|
{
|
|
|
INTERNAL_supportedOrientations = value;
|
|
|
prefsChanged = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region Private Variables
|
|
|
|
|
|
private Game game;
|
|
|
private GraphicsDevice graphicsDevice;
|
|
|
private bool drawBegun;
|
|
|
private bool disposed;
|
|
|
private bool prefsChanged;
|
|
|
private bool supportsOrientations;
|
|
|
private bool useResizedBackBuffer;
|
|
|
private int resizedBackBufferWidth;
|
|
|
private int resizedBackBufferHeight;
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region Public Static Fields
|
|
|
|
|
|
public static readonly int DefaultBackBufferWidth = 800;
|
|
|
public static readonly int DefaultBackBufferHeight = 480;
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region Public Events
|
|
|
|
|
|
public event EventHandler<EventArgs> Disposed;
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region IGraphicsDeviceService Events
|
|
|
|
|
|
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;
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region Public Constructor
|
|
|
|
|
|
public GraphicsDeviceManager(Game game)
|
|
|
{
|
|
|
if (game == null)
|
|
|
{
|
|
|
throw new ArgumentNullException("The game cannot be null!");
|
|
|
}
|
|
|
|
|
|
this.game = game;
|
|
|
|
|
|
INTERNAL_supportedOrientations = DisplayOrientation.Default;
|
|
|
|
|
|
INTERNAL_preferredBackBufferHeight = DefaultBackBufferHeight;
|
|
|
INTERNAL_preferredBackBufferWidth = DefaultBackBufferWidth;
|
|
|
|
|
|
INTERNAL_preferredBackBufferFormat = SurfaceFormat.Color;
|
|
|
INTERNAL_preferredDepthStencilFormat = DepthFormat.Depth24;
|
|
|
|
|
|
INTERNAL_synchronizeWithVerticalRetrace = true;
|
|
|
|
|
|
INTERNAL_preferMultiSampling = false;
|
|
|
|
|
|
if (game.Services.GetService(typeof(IGraphicsDeviceManager)) != null)
|
|
|
{
|
|
|
throw new ArgumentException("Graphics Device Manager Already Present");
|
|
|
}
|
|
|
|
|
|
game.Services.AddService(typeof(IGraphicsDeviceManager), this);
|
|
|
game.Services.AddService(typeof(IGraphicsDeviceService), this);
|
|
|
|
|
|
prefsChanged = true;
|
|
|
useResizedBackBuffer = false;
|
|
|
supportsOrientations = FNAPlatform.SupportsOrientationChanges();
|
|
|
game.Window.ClientSizeChanged += INTERNAL_OnClientSizeChanged;
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region Deconstructor
|
|
|
|
|
|
~GraphicsDeviceManager()
|
|
|
{
|
|
|
Dispose(false);
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region Dispose Methods
|
|
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
|
{
|
|
|
if (!disposed)
|
|
|
{
|
|
|
if (disposing)
|
|
|
{
|
|
|
if (graphicsDevice != null)
|
|
|
{
|
|
|
graphicsDevice.Dispose();
|
|
|
graphicsDevice = null;
|
|
|
}
|
|
|
}
|
|
|
if (Disposed != null)
|
|
|
{
|
|
|
Disposed(this, EventArgs.Empty);
|
|
|
}
|
|
|
disposed = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void IDisposable.Dispose()
|
|
|
{
|
|
|
Dispose(true);
|
|
|
GC.SuppressFinalize(this);
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region Public Methods
|
|
|
|
|
|
public void ApplyChanges()
|
|
|
{
|
|
|
/* Calling ApplyChanges() before CreateDevice() forces CreateDevice.
|
|
|
* We can then return early since CreateDevice basically does all of
|
|
|
* this work for us anyway.
|
|
|
*
|
|
|
* Note that if you hit this block, it's probably because you called
|
|
|
* ApplyChanges in the constructor. The device created here gets
|
|
|
* destroyed and recreated by the game, so maybe don't do that!
|
|
|
*
|
|
|
* -flibit
|
|
|
*/
|
|
|
if (graphicsDevice == null)
|
|
|
{
|
|
|
(this as IGraphicsDeviceManager).CreateDevice();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// ApplyChanges() calls with no actual changes should be ignored.
|
|
|
if (!prefsChanged && !useResizedBackBuffer)
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// Recreate device information before resetting
|
|
|
GraphicsDeviceInformation gdi = new GraphicsDeviceInformation();
|
|
|
gdi.Adapter = graphicsDevice.Adapter;
|
|
|
gdi.PresentationParameters = graphicsDevice.PresentationParameters.Clone();
|
|
|
INTERNAL_CreateGraphicsDeviceInformation(gdi);
|
|
|
|
|
|
// Prepare the window...
|
|
|
if (supportsOrientations)
|
|
|
{
|
|
|
game.Window.SetSupportedOrientations(
|
|
|
INTERNAL_supportedOrientations
|
|
|
);
|
|
|
}
|
|
|
game.Window.BeginScreenDeviceChange(
|
|
|
gdi.PresentationParameters.IsFullScreen
|
|
|
);
|
|
|
game.Window.EndScreenDeviceChange(
|
|
|
gdi.Adapter.DeviceName,
|
|
|
gdi.PresentationParameters.BackBufferWidth,
|
|
|
gdi.PresentationParameters.BackBufferHeight
|
|
|
);
|
|
|
|
|
|
// FIXME: Everything below should be before EndScreenDeviceChange! -flibit
|
|
|
|
|
|
// Reset!
|
|
|
graphicsDevice.Reset(
|
|
|
gdi.PresentationParameters,
|
|
|
gdi.Adapter
|
|
|
);
|
|
|
prefsChanged = false;
|
|
|
}
|
|
|
|
|
|
public void ToggleFullScreen()
|
|
|
{
|
|
|
IsFullScreen = !IsFullScreen;
|
|
|
ApplyChanges();
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region Protected Methods
|
|
|
|
|
|
protected virtual void OnDeviceCreated(object sender, EventArgs args)
|
|
|
{
|
|
|
if (DeviceCreated != null)
|
|
|
{
|
|
|
DeviceCreated(sender, args);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
protected virtual void OnDeviceDisposing(object sender, EventArgs args)
|
|
|
{
|
|
|
if (DeviceDisposing != null)
|
|
|
{
|
|
|
DeviceDisposing(sender, args);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
protected virtual void OnDeviceReset(object sender, EventArgs args)
|
|
|
{
|
|
|
if (DeviceReset != null)
|
|
|
{
|
|
|
DeviceReset(sender, args);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
protected virtual void OnDeviceResetting(object sender, EventArgs args)
|
|
|
{
|
|
|
if (DeviceResetting != null)
|
|
|
{
|
|
|
DeviceResetting(sender, args);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
protected virtual void OnPreparingDeviceSettings(
|
|
|
object sender,
|
|
|
PreparingDeviceSettingsEventArgs args
|
|
|
) {
|
|
|
if (PreparingDeviceSettings != null)
|
|
|
{
|
|
|
PreparingDeviceSettings(sender, args);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
protected virtual bool CanResetDevice(
|
|
|
GraphicsDeviceInformation newDeviceInfo
|
|
|
) {
|
|
|
throw new NotImplementedException();
|
|
|
}
|
|
|
|
|
|
protected virtual GraphicsDeviceInformation FindBestDevice(
|
|
|
bool anySuitableDevice
|
|
|
) {
|
|
|
throw new NotImplementedException();
|
|
|
}
|
|
|
|
|
|
protected virtual void RankDevices(
|
|
|
List<GraphicsDeviceInformation> foundDevices
|
|
|
) {
|
|
|
throw new NotImplementedException();
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region Private Methods
|
|
|
|
|
|
private void INTERNAL_OnClientSizeChanged(object sender, EventArgs e)
|
|
|
{
|
|
|
Rectangle size = (sender as GameWindow).ClientBounds;
|
|
|
resizedBackBufferWidth = size.Width;
|
|
|
resizedBackBufferHeight = size.Height;
|
|
|
if (Environment.GetEnvironmentVariable("FNA_GRAPHICS_ENABLE_HIGHDPI") == "1")
|
|
|
{
|
|
|
resizedBackBufferWidth *= 2;
|
|
|
resizedBackBufferHeight *= 2;
|
|
|
}
|
|
|
useResizedBackBuffer = true;
|
|
|
ApplyChanges();
|
|
|
}
|
|
|
|
|
|
private void INTERNAL_CreateGraphicsDeviceInformation(
|
|
|
GraphicsDeviceInformation gdi
|
|
|
) {
|
|
|
/* Apply the GraphicsDevice changes to the new Parameters.
|
|
|
* Note that PreparingDeviceSettings can override any of these!
|
|
|
* -flibit
|
|
|
*/
|
|
|
if (useResizedBackBuffer)
|
|
|
{
|
|
|
gdi.PresentationParameters.BackBufferWidth =
|
|
|
resizedBackBufferWidth;
|
|
|
gdi.PresentationParameters.BackBufferHeight =
|
|
|
resizedBackBufferHeight;
|
|
|
useResizedBackBuffer = false;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if (!supportsOrientations)
|
|
|
{
|
|
|
gdi.PresentationParameters.BackBufferWidth =
|
|
|
PreferredBackBufferWidth;
|
|
|
gdi.PresentationParameters.BackBufferHeight =
|
|
|
PreferredBackBufferHeight;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
/* Flip the backbuffer dimensions to scale
|
|
|
* appropriately to the current orientation.
|
|
|
*/
|
|
|
int min = Math.Min(PreferredBackBufferWidth, PreferredBackBufferHeight);
|
|
|
int max = Math.Max(PreferredBackBufferWidth, PreferredBackBufferHeight);
|
|
|
|
|
|
if (gdi.PresentationParameters.DisplayOrientation == DisplayOrientation.Portrait)
|
|
|
{
|
|
|
gdi.PresentationParameters.BackBufferWidth = min;
|
|
|
gdi.PresentationParameters.BackBufferHeight = max;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
gdi.PresentationParameters.BackBufferWidth = max;
|
|
|
gdi.PresentationParameters.BackBufferHeight = min;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
gdi.PresentationParameters.BackBufferFormat =
|
|
|
PreferredBackBufferFormat;
|
|
|
gdi.PresentationParameters.DepthStencilFormat =
|
|
|
PreferredDepthStencilFormat;
|
|
|
gdi.PresentationParameters.IsFullScreen =
|
|
|
IsFullScreen;
|
|
|
gdi.PresentationParameters.PresentationInterval =
|
|
|
SynchronizeWithVerticalRetrace ?
|
|
|
PresentInterval.One :
|
|
|
PresentInterval.Immediate;
|
|
|
if (!PreferMultiSampling)
|
|
|
{
|
|
|
gdi.PresentationParameters.MultiSampleCount = 0;
|
|
|
}
|
|
|
else if (gdi.PresentationParameters.MultiSampleCount == 0)
|
|
|
{
|
|
|
/* XNA4 seems to have an upper limit of 8, but I'm willing to
|
|
|
* limit this only in GraphicsDeviceManager's default setting.
|
|
|
* If you want even higher values, Reset() with a custom value.
|
|
|
* -flibit
|
|
|
*/
|
|
|
int maxMultiSampleCount = 0;
|
|
|
if (graphicsDevice != null)
|
|
|
{
|
|
|
maxMultiSampleCount = graphicsDevice.GLDevice.MaxMultiSampleCount;
|
|
|
}
|
|
|
gdi.PresentationParameters.MultiSampleCount = Math.Min(
|
|
|
maxMultiSampleCount,
|
|
|
8
|
|
|
);
|
|
|
}
|
|
|
gdi.GraphicsProfile = GraphicsProfile;
|
|
|
|
|
|
// Give the user a chance to override the above settings.
|
|
|
OnPreparingDeviceSettings(
|
|
|
this,
|
|
|
new PreparingDeviceSettingsEventArgs(gdi)
|
|
|
);
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region IGraphicsDeviceManager Methods
|
|
|
|
|
|
void IGraphicsDeviceManager.CreateDevice()
|
|
|
{
|
|
|
// This function can recreate the device from scratch!
|
|
|
if (graphicsDevice != null)
|
|
|
{
|
|
|
graphicsDevice.Dispose();
|
|
|
graphicsDevice = null;
|
|
|
}
|
|
|
|
|
|
// Set the default device information
|
|
|
GraphicsDeviceInformation gdi = new GraphicsDeviceInformation();
|
|
|
gdi.Adapter = GraphicsAdapter.DefaultAdapter;
|
|
|
gdi.PresentationParameters = new PresentationParameters();
|
|
|
gdi.PresentationParameters.DeviceWindowHandle = game.Window.Handle;
|
|
|
INTERNAL_CreateGraphicsDeviceInformation(gdi);
|
|
|
|
|
|
// Prepare the window...
|
|
|
if (supportsOrientations)
|
|
|
{
|
|
|
game.Window.SetSupportedOrientations(
|
|
|
INTERNAL_supportedOrientations
|
|
|
);
|
|
|
}
|
|
|
game.Window.BeginScreenDeviceChange(
|
|
|
gdi.PresentationParameters.IsFullScreen
|
|
|
);
|
|
|
game.Window.EndScreenDeviceChange(
|
|
|
gdi.Adapter.DeviceName,
|
|
|
gdi.PresentationParameters.BackBufferWidth,
|
|
|
gdi.PresentationParameters.BackBufferHeight
|
|
|
);
|
|
|
|
|
|
// FIXME: Everything below should be before EndScreenDeviceChange! -flibit
|
|
|
|
|
|
// Create the GraphicsDevice, hook the callbacks
|
|
|
graphicsDevice = new GraphicsDevice(
|
|
|
gdi.Adapter,
|
|
|
gdi.GraphicsProfile,
|
|
|
gdi.PresentationParameters
|
|
|
);
|
|
|
graphicsDevice.Disposing += OnDeviceDisposing;
|
|
|
graphicsDevice.DeviceResetting += OnDeviceResetting;
|
|
|
graphicsDevice.DeviceReset += OnDeviceReset;
|
|
|
|
|
|
// Call the DeviceCreated Event
|
|
|
OnDeviceCreated(this, EventArgs.Empty);
|
|
|
}
|
|
|
|
|
|
bool IGraphicsDeviceManager.BeginDraw()
|
|
|
{
|
|
|
if (graphicsDevice == null)
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
graphicsDevice.GLDevice.BeginFrame();
|
|
|
drawBegun = true;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void IGraphicsDeviceManager.EndDraw()
|
|
|
{
|
|
|
if (graphicsDevice != null && drawBegun)
|
|
|
{
|
|
|
drawBegun = false;
|
|
|
graphicsDevice.Present();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
}
|
|
|
}
|
|
|
|