Skip to content

Commit

Permalink
[DX] Improve window handling (MonoGame#5585)
Browse files Browse the repository at this point in the history
* [DX] Improve window handling

* Update back buffer size when switching to full screen

* Fix dirty flag on early exit in GDM.ApplyChanges

* Restore full screen after minimizing the window

* Allow mode switching from the start

* Fix DesktopGL build

* Fix ClientSize raised before back buffer resize

* Ignore resize events after switching soft full screen

* VS done too much renamin'

* Fix UWP build

* Fix intializing in soft full screen mode

* Resizing in hw full screen and cleanup things a bit

* Fix hard full screen on startup and disposal

Hardware full screen failed when set in the game constructor because there
was no swapchain yet to get the output from. If there is no swapchain yet,
the current method gets the primary output by enumerating adapters and
getting the first, then getting the first output from that adapter.

When exiting a game while in full screen I sometimes got stuck with a
black screen. That was fixed by making sure we exit full screen when
disposing a GraphicsDevice.

* Remove unused event

* Clamp backbuffer size to display size

* Make OnPresentationChanged for WinForms internal

* Fix DesktopGL build

* Fix Web build

* Fix mobile build

* Only correct back buffer size for desktop platforms

* Don't change DesktopGL to make this more mergeable

* Don't limit back buffer size

* Update Adapter when display changes

Added a check after a window is moved (or resized) that updates the
GraphicsAdapter of the GraphicsDevice to the adapter that is rendering the
display that the window is on (because that might have changed).

* Detect window size change through Win10 hotkeys

* Catch ContainingOutput exception on headless devices

* Don't center window after move with hotkeys

* Fix resize through code raising ClientSizeChanged
  • Loading branch information
Jjagg authored and KonajuGames committed Aug 14, 2017
1 parent e2cb5dd commit 1a9466e
Show file tree
Hide file tree
Showing 15 changed files with 385 additions and 102 deletions.
1 change: 1 addition & 0 deletions Build/Projects/MonoGame.Framework.definition
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,7 @@
<Compile Include="Graphics\PackedVector\Rgba1010102.cs" />
<Compile Include="Graphics\PackedVector\Short2.cs" />
<Compile Include="Graphics\PackedVector\Short4.cs" />
<Compile Include="Graphics\PresentationEventArgs.cs" />
<Compile Include="Graphics\PresentationParameters.cs" />
<Compile Include="Graphics\PresentInterval.cs" />
<Compile Include="Graphics\RenderTarget2D.cs" />
Expand Down
11 changes: 6 additions & 5 deletions MonoGame.Framework/Game.cs
Original file line number Diff line number Diff line change
Expand Up @@ -535,9 +535,11 @@ protected virtual void LoadContent() { }
protected virtual void UnloadContent() { }

protected virtual void Initialize()
{
// TODO: This should be removed once all platforms use the new GraphicsDeviceManager
{
// TODO: This should be removed once all platforms use the new GraphicsDeviceManager
#if !(WINDOWS && DIRECTX)
applyChanges(graphicsDeviceManager);
#endif

// According to the information given on MSDN (see link below), all
// GameComponents in Components at the time Initialize() is called
Expand Down Expand Up @@ -635,24 +637,23 @@ private void Platform_ApplicationViewChanged(object sender, ViewStateChangedEven
// break entirely the possibility that additional platforms could
// be added by third parties without changing MonoGame itself.

#if !(WINDOWS && DIRECTX)
internal void applyChanges(GraphicsDeviceManager manager)
{
Platform.BeginScreenDeviceChange(GraphicsDevice.PresentationParameters.IsFullScreen);

#if !(WINDOWS && DIRECTX)

if (GraphicsDevice.PresentationParameters.IsFullScreen)
Platform.EnterFullScreen();
else
Platform.ExitFullScreen();
#endif
var viewport = new Viewport(0, 0,
GraphicsDevice.PresentationParameters.BackBufferWidth,
GraphicsDevice.PresentationParameters.BackBufferHeight);

GraphicsDevice.Viewport = viewport;
Platform.EndScreenDeviceChange(string.Empty, viewport.Width, viewport.Height);
}
#endif

internal void DoUpdate(GameTime gameTime)
{
Expand Down
14 changes: 8 additions & 6 deletions MonoGame.Framework/GamePlatform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// file 'LICENSE.txt', which is part of this source code package.

using System;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;

Expand Down Expand Up @@ -244,14 +245,15 @@ public virtual void ResetElapsedTime() {}
public virtual void Present() { }

protected virtual void OnIsMouseVisibleChanged() {}


/// <summary>
/// Used by the GraphicsDeviceManager to update the platform window
/// after the graphics device has changed the presentation.
/// Called by the GraphicsDeviceManager to notify the platform
/// that the presentation parameters have changed.
/// </summary>
internal virtual void OnPresentationChanged()
{
}
/// <param name="pp">The new presentation parameters.</param>
internal virtual void OnPresentationChanged(PresentationParameters pp)
{
}

#endregion Methods

Expand Down
1 change: 0 additions & 1 deletion MonoGame.Framework/Graphics/GraphicsAdapter.DirectX.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ private bool PlatformIsProfileSupported(GraphicsProfile graphicsProfile)
{
if(UseReferenceDevice)
return true;

switch(graphicsProfile)
{
case GraphicsProfile.Reach:
Expand Down
109 changes: 105 additions & 4 deletions MonoGame.Framework/Graphics/GraphicsDevice.DirectX.cs
Original file line number Diff line number Diff line change
Expand Up @@ -674,8 +674,78 @@ protected virtual void CreateDeviceResources()

internal void SetHardwareFullscreen()
{
// This force to switch to fullscreen mode when hardware mode enabled(working in WindowsDX mode).
_swapChain.SetFullscreenState(PresentationParameters.IsFullScreen, null);
_swapChain.SetFullscreenState(PresentationParameters.IsFullScreen && PresentationParameters.HardwareModeSwitch, null);
}

internal void ResizeTargets()
{
var format = SharpDXHelper.ToFormat(PresentationParameters.BackBufferFormat);
var descr = new ModeDescription
{
Format = format,
#if WINRT
Scaling = DisplayModeScaling.Stretched,
#else
Scaling = DisplayModeScaling.Unspecified,
#endif
Width = PresentationParameters.BackBufferWidth,
Height = PresentationParameters.BackBufferHeight,
};

_swapChain.ResizeTarget(ref descr);
}

internal void GetModeSwitchedSize(out int width, out int height)
{
Output output = null;
if (_swapChain == null)
{
// get the primary output
using (var factory = new SharpDX.DXGI.Factory1())
using (var adapter = factory.GetAdapter1(0))
output = adapter.Outputs[0];
}
else
{
try
{
output = _swapChain.ContainingOutput;
}
catch (SharpDXException) { /* ContainingOutput fails on a headless device */ }
}

var format = SharpDXHelper.ToFormat(PresentationParameters.BackBufferFormat);
var target = new ModeDescription
{
Format = format,
#if WINRT
Scaling = DisplayModeScaling.Stretched,
#else
Scaling = DisplayModeScaling.Unspecified,
#endif
Width = PresentationParameters.BackBufferWidth,
Height = PresentationParameters.BackBufferHeight,
};

if (output == null)
{
width = PresentationParameters.BackBufferWidth;
height = PresentationParameters.BackBufferHeight;
}
else
{
ModeDescription closest;
output.GetClosestMatchingMode(_d3dDevice, target, out closest);
width = closest.Width;
height = closest.Height;
output.Dispose();
}
}

internal void GetDisplayResolution(out int width, out int height)
{
width = Adapter.CurrentDisplayMode.Width;
height = Adapter.CurrentDisplayMode.Height;
}

internal void CreateSizeDependentResources()
Expand Down Expand Up @@ -773,7 +843,8 @@ internal void CreateSizeDependentResources()
Usage = SharpDX.DXGI.Usage.RenderTargetOutput,
BufferCount = 2,
SwapEffect = SharpDXHelper.ToSwapEffect(PresentationParameters.PresentationInterval),
IsWindowed = true
IsWindowed = true,
Flags = SwapChainFlags.AllowModeSwitch
};

// Once the desired swap chain description is configured, it must be created on the same adapter as our D3D Device
Expand All @@ -785,6 +856,7 @@ internal void CreateSizeDependentResources()
using (var dxgiFactory = dxgiAdapter.GetParent<SharpDX.DXGI.Factory1>())
{
_swapChain = new SwapChain(dxgiFactory, dxgiDevice, desc);
RefreshAdapter();
dxgiFactory.MakeWindowAssociation(PresentationParameters.DeviceWindowHandle, WindowAssociationFlags.IgnoreAll);
// To reduce latency, ensure that DXGI does not queue more than one frame at a time.
// Docs: https://msdn.microsoft.com/en-us/library/windows/desktop/ff471334(v=vs.85).aspx
Expand Down Expand Up @@ -842,7 +914,32 @@ internal void CreateSizeDependentResources()
};
}


internal void RefreshAdapter()
{
if (_swapChain == null)
return;

Output output = null;
try
{
output = _swapChain.ContainingOutput;
}
catch (SharpDXException) { /* ContainingOutput fails on a headless device */ }

if (output != null)
{
foreach (var adapter in GraphicsAdapter.Adapters)
{
if (adapter.DeviceName == output.Description.DeviceName)
{
Adapter = adapter;
break;
}
}

output.Dispose();
}
}

internal void OnPresentationChanged()
{
Expand Down Expand Up @@ -929,6 +1026,10 @@ private void PlatformClear(ClearOptions options, Vector4 color, float depth, int

private void PlatformDispose()
{
// make sure to release full screen or this might cause issues on exit
if (_swapChain.IsFullScreen)
_swapChain.SetFullscreenState(false, null);

SharpDX.Utilities.Dispose(ref _renderTargetView);
SharpDX.Utilities.Dispose(ref _depthStencilView);

Expand Down
26 changes: 26 additions & 0 deletions MonoGame.Framework/Graphics/GraphicsDevice.OpenGL.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1354,5 +1354,31 @@ public BufferBindingInfo(VertexDeclaration.VertexDeclarationAttributeInfo attrib
InstanceFrequency = instanceFrequency;
}
}

#if DESKTOPGL
private void GetModeSwitchedSize(out int width, out int height)
{
var mode = new Sdl.Display.Mode
{
Width = PresentationParameters.BackBufferWidth,
Height = PresentationParameters.BackBufferHeight,
Format = 0,
RefreshRate = 0,
DriverData = IntPtr.Zero
};
Sdl.Display.Mode closest;
Sdl.Display.GetClosestDisplayMode(0, mode, out closest);
width = closest.Width;
height = closest.Height;
}

private void GetDisplayResolution(out int width, out int height)
{
Sdl.Display.Mode mode;
Sdl.Display.GetCurrentDisplayMode(0, out mode);
width = mode.Width;
height = mode.Height;
}
#endif
}
}
41 changes: 35 additions & 6 deletions MonoGame.Framework/Graphics/GraphicsDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ private bool PixelShaderDirty
public event EventHandler<ResourceDestroyedEventArgs> ResourceDestroyed;
public event EventHandler<EventArgs> Disposing;

internal event EventHandler<EventArgs> PresentationChanged;
internal event EventHandler<PresentationEventArgs> PresentationChanged;

private int _maxVertexBufferSlots;
internal int MaxTextureSlots;
Expand Down Expand Up @@ -220,8 +220,13 @@ public GraphicsDevice(GraphicsAdapter adapter, GraphicsProfile graphicsProfile,
_graphicsProfile = graphicsProfile;
Setup();
GraphicsCapabilities = new GraphicsCapabilities();
GraphicsCapabilities.Initialize(this);
Initialize();
GraphicsCapabilities.Initialize(this);

#if WINDOWS
CorrectBackBufferSize();
#endif

Initialize();
}

private void Setup()
Expand Down Expand Up @@ -606,17 +611,41 @@ public void Present(Rectangle? sourceRectangle, Rectangle? destinationRectangle,

partial void PlatformValidatePresentationParameters(PresentationParameters presentationParameters);

#if WINDOWS
private void CorrectBackBufferSize()
{
// Window size can be modified when we're going full screen, we need to take that into account
// so the back buffer has the right size.
if (PresentationParameters.IsFullScreen)
{
int newWidth, newHeight;
if (PresentationParameters.HardwareModeSwitch)
GetModeSwitchedSize(out newWidth, out newHeight);
else
GetDisplayResolution(out newWidth, out newHeight);

PresentationParameters.BackBufferWidth = newWidth;
PresentationParameters.BackBufferHeight = newHeight;
}
}
#endif

public void Reset()
{
PlatformValidatePresentationParameters(PresentationParameters);
#if WINDOWS
CorrectBackBufferSize();
#endif

PlatformValidatePresentationParameters(PresentationParameters);

EventHelpers.Raise(this, DeviceResetting, EventArgs.Empty);

// Update the back buffer.
OnPresentationChanged();

EventHelpers.Raise(this, PresentationChanged, EventArgs.Empty);
EventHelpers.Raise(this, PresentationChanged, new PresentationEventArgs(PresentationParameters));
EventHelpers.Raise(this, DeviceReset, EventArgs.Empty);
}
}

public void Reset(PresentationParameters presentationParameters)
{
Expand Down
18 changes: 18 additions & 0 deletions MonoGame.Framework/Graphics/PresentationEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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;

namespace Microsoft.Xna.Framework.Graphics
{
internal class PresentationEventArgs : EventArgs
{
public PresentationParameters PresentationParameters { get; private set; }

public PresentationEventArgs(PresentationParameters presentationParameters)
{
PresentationParameters = presentationParameters;
}
}
}
8 changes: 4 additions & 4 deletions MonoGame.Framework/GraphicsDeviceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,8 @@ public void ApplyChanges()
if (!_shouldApplyChanges)
return;

_shouldApplyChanges = false;

_game.Window.SetSupportedOrientations(_supportedOrientations);

// Allow for optional platform specific behavior.
Expand All @@ -319,8 +321,6 @@ public void ApplyChanges()
}

GraphicsDevice.Reset(gdi.PresentationParameters);

_shouldApplyChanges = false;
}

private void DisposeGraphicsDevice()
Expand Down Expand Up @@ -364,9 +364,9 @@ public void ToggleFullScreen()
ApplyChanges();
}

private void OnPresentationChanged(object sender, EventArgs args)
private void OnPresentationChanged(object sender, PresentationEventArgs args)
{
_game.Platform.OnPresentationChanged();
_game.Platform.OnPresentationChanged(args.PresentationParameters);
}

/// <summary>
Expand Down
Loading

0 comments on commit 1a9466e

Please sign in to comment.