stride3d / stride

Stride (formerly Xenko), a free and open-source cross-platform C# game engine.
https://stride3d.net
MIT License
6.6k stars 953 forks source link

[Engine] Calling game.Exit() in a game running in Fullscreen mode causes an unhandled exception error on exit. #2008

Open SoulRider opened 1 year ago

SoulRider commented 1 year ago

Release Type: Official Release

Version: 4.1.0.1948

Platform(s): Windows

Describe the bug Calling game.Exit() in a game running in Fullscreen mode causes an unhandled exception error on exit. It appears the render device is resetting back to it's default windowed resolution on exit of fullscreen, and is trying to reload assets which have been unloaded by the game as it exited.

To Reproduce Steps to reproduce the behavior:

  1. Set game.Window.PreferredFullscreenSize to a size different to windowed resolution. I use:

        GraphicsOutput[] array = game.GraphicsDevice.Adapter.Outputs;
        game.Window.PreferredFullscreenSize = new Int2(array[0].CurrentDisplayMode.Height, array[0].CurrentDisplayMode.Width);
  2. Set game.Window.IsFullscreen = true;

  3. Call game.Exit()

Expected behavior Game to exit silently and successfully.

Screenshots SameErrorOtherGame

Log and callstacks N/A

Additional context I have a simple project available which shows this issue here - SimpleFullscreenCrashTest.zip.

I have confirmed the issue using both normal fullscreen and 'FullscreenIsBorderlessWindow'. Issue occurs whether you run from editor, Visual Studio or from a build file directly.

I upgraded a game from 4.0 which did not have this issue to the version 4.1.0.1948, and am now experiencing this in that game. I also have a game that has only had this issue since upgrading to 4.1.0.1948 from the previous patch suggesting this is a new issue in 4.1.0.1948

SoulRider commented 11 months ago

In trying to write a workaround for this bug, I narrowed the bug down and can confirm it happens whenever you exit fullscreen mode, you do not have to be exiting the game. Exiting the game is disabling fullscreen as well. Exactly what in the disabling fullscreen process is causing the issue I haven't yet figured out.

SoulRider commented 11 months ago

I have been investigating running the game in different contexts to see if it makes a difference. Desktop and WPF both work but still have the same fullscreen crash bug, however SDL context doesn't work at all. I get an error on launch. Attached is the error screenshot.

SDLContext error

The inner text reads: TypeLoadException: Method 'get_Windows64' in type 'Silk.NET.SDL.SDLLibraryNameContainer' from assembly 'Silk.NET.SDL, Version=2.15.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.

SoulRider commented 11 months ago

I've downloaded the source and built the version 4.1 master and it has the same issue with Fullscreen and SDL context. My app is DX11 win64 only. Not sure if this makes any difference.

SoulRider commented 11 months ago

If I run a new project from self-built master-4.1 version of Stride with no code, all contexts work as expected. When I add code to set the game to fullscreen, Desktop, WPF etc context works, but SDL context displays a blank transparent window where I can see my desktop, there are no errors.

If I further add code to after 10 seconds exit fullscreen I then induce the DX11 SwapChainGraphicsPresenter error in all contexts except SDL. SDL reverts back to windowed without any error and is now displaying correctly.

So I amended the test code to flip between fullscreen and windowed every 10 seconds. As expected the other contexts all failed on the first switch to windowed mode, invoking the DX11 SwapChainGraphicsPresenter error. For the SDL context, it does the 'no display fullscreen', then switches to windowed and is visible. On the next switch back to fullscreen SDL context falls over with a DX11 SwapChainGraphicsPresenter error. This is the error generated:

SDL Fullscreen switch error

The test projects settings are Direct3D11, Windowsx64 only, tested with target versons from Windows 7 to Windows 10.0.20348 and all produce the same result.

Attached is a video showing the SDL behaviour.

https://github.com/stride3d/stride/assets/1388684/9937eed9-66ec-4c9c-b63e-a10740caab59

Eideren commented 11 months ago

It throws in the resize there: https://github.com/stride3d/stride/blob/ffd71acc8a8a26a80e5275f833b6e1442e98d01c/sources/engine/Stride.Games/GraphicsDeviceManager.cs#L1051-L1065 Forcing a CreateDevice as the logic never sets needToCreateNewDevice back to false because of the exception.

Here's the exception that's ignored.

SharpDX.SharpDXException: HRESULT: [0x80070005], Module: [General], ApiCode: [E_ACCESSDENIED/General access denied error], Message: Access is denied.

   at SharpDX.Result.CheckError() in C:\projects\sharpdx\Source\SharpDX\Result.cs:line 195
   at SharpDX.DXGI.SwapChain.ResizeBuffers(Int32 bufferCount, Int32 width, Int32 height, Format newFormat, SwapChainFlags swapChainFlags) in C:\projects\sharpdx\Source\SharpDX.DXGI\Generated\REFERENCE\Interfaces.cs:line 4670
   at Stride.Graphics.SwapChainGraphicsPresenter.ResizeBackBuffer(Int32 width, Int32 height, PixelFormat format)
   at Stride.Graphics.GraphicsPresenter.Resize(Int32 width, Int32 height, PixelFormat format)
   at Stride.Games.GraphicsDeviceManager.ChangeOrCreateDevice(Boolean forceCreate)

So two issues in one, the one about serialization should be fixed as that serves as a failsafe to restore the game when driver crashes.

SoulRider commented 11 months ago

It throws in the resize there:

https://github.com/stride3d/stride/blob/ffd71acc8a8a26a80e5275f833b6e1442e98d01c/sources/engine/Stride.Games/GraphicsDeviceManager.cs#L1051-L1065

Forcing a CreateDevice as the logic never sets needToCreateNewDevice back to false because of the exception. So two issues in one, the one about serialization should be fixed as that serves as a failsafe to restore the game when driver >crashes.

I am just checking I have the issue straight in my head :)

We have two issues. The first issue is the exception which causes the rest of the function, including setting the bool to false, to not run. This forces a CreateDevice.

The second issue is that CreateDevice should succesfully create a new device, and not itself throw an error about the content serializer not being able to load textures.

I understand the priority of fixing the CreateDevice issue, but if we do not also deal with the other issue, wouldn't we be in a situation where whenever there is a change from fullscreen a new graphics device will be created? I understand a fullscreen swap isn't generally something that occurs very often, but recreating a device would be quite expensive on resources each time wouldn't it?

Eideren commented 11 months ago

Yes, both of them should be fixed, with the swallowed exception being higher priority.

SoulRider commented 5 months ago

Any news on this? My 2nd game was supposed to release in December but still isn't released because of this issue, and my originally released game on Steam is currently broken because of this issue. Any chance of this getting fixed? Switching my games to DX12 just causes a whole heap of other errors & problems so I want to know if this will be fixed or if my games are now consigned to the rubbish bin because of this bug?

Utlimately it's just a progression of a bug I first posted about in 2020 - https://github.com/stride3d/stride/issues/932 but I just want to know is this going to be fixed?

Eideren commented 5 months ago

I believe you know that this engine is community driven, people contributing to it are doing so on their free time. I don't have time to look into this further, and it doesn't look like anyone else is interested in tackling it, so your best bet is to fix it yourself or actively find someone who would do it for you

MeharDT commented 5 months ago

I can replicate both the fullscreen SDL and game.Exit() crashes in the attached sample and a new project (Alt + Enter to fullscreen and Alt + F4 to close are enough to replicate the latter crash).

This is a pretty critical issue as it means fullscreen support is effectively broken in Stride 4.2 on Windows (possibly other platforms as well). We should consider a bounty.

Doprez commented 5 months ago

Just some added info the error seems to be Directx at least from what I have tested. I dont kow if opengl/vulkan works as expected. And it happens both when exiting the game from fullscreen and changing from fullscreen to windowed but with different errors.

Crash message when exiting from fullscreen: image

SharpDX.SharpDXException: 'HRESULT: [0x80070005], Module: [General], ApiCode: [E_ACCESSDENIED/General access denied error], Message: Access is denied.

Crash message when going back to windowed: image

SharpDX.SharpDXException: 'HRESULT: [0x887A0022], Module: [SharpDX.DXGI], ApiCode: [DXGI_ERROR_NOT_CURRENTLY_AVAILABLE/NotCurrentlyAvailable], Message: A resource is not available at the time of the call, but may become available later.

Ethereal77 commented 5 months ago

In the next few days, if life and work allows, I plan to update my Silk DX branch. There we can test if it is DX related, or SharpDX-specific, because while converting from SharpDX I've found several places where we were using the API wrong. In fact, one of the reasons I've not yet finished that PR is because although the conversion is almost finished and compiles, the DX debug runtime logs hundreds of warnings of resources not released correctly, buffers not transitioned correctly, etc.

Doprez commented 5 months ago

a temporary work around is the below code it seems.

if (Game.Window.IsFullscreen)
{
    Game.Window.Visible = false;
    Game.Window.IsFullscreen = false;
    Game.Window.Visible = true;
}
(Game as Game).Exit();

i also have a couple PRs that should solve the Texture issue and one to check for if the game is exiting from fullscreen to avoid trying to reload. Part of the problem seems to come from the fullscreen state change which is being called on destroy due to what was a previous work around.

image

SoulRider commented 5 months ago

a temporary work around is the below code it seems.

if (Game.Window.IsFullscreen)
{
  Game.Window.Visible = false;
  Game.Window.IsFullscreen = false;
  Game.Window.Visible = true;
}
(Game as Game).Exit();

I have tested and confirmed the workaround works. You are an absolute life saver, thank you!! ❤️ Now to get my head back into game development to fix the original and release my 2nd game. I am so happy. 😸

Although I should point out the SDL context still doesn't work at all with fullscreen. This just fixes the issue for all other contexts.

Doprez commented 2 months ago

One more thing to add for SDL. you can create windowed fullscreen by using the below code:

// within an inherited Game class in the BeginRun overridden method

var desktopBounds = GraphicsDevice.Adapter.Outputs[0].DesktopBounds;
Window.SetSize(new Int2(desktopBounds.Right, desktopBounds.Bottom));
Window.Position = new Int2(0, 0);

This puts the banner of the window above the screen and looks correct to the user at least. There is some funkiness still but this could be a temporary work around for at least a fullscreen affect. Proper fullscreen still has benefits of course but the SDL code in Stride needs a lot of love as its pretty unstable right now compared to the WinForms implementation.

~I am also looking into some possible scaling issues but I cant confirm if its an issue with SDL or ImGui right now. Unrelated to this specific issue but I thought I would mention it somewhere in case someone else has more info.~ it was not SDL.