stride icon indicating copy to clipboard operation
stride copied to clipboard

Exception when switching window from fullscreen to windowed mode

Open RedKorshun opened this issue 3 years ago • 10 comments

Release Type: Official Release

Version: 4.1.0.1734

Platform: Windows

Describe the bug Exception during switching to windowed mode.

To Reproduce Steps to reproduce the behavior:

  1. Create a new game project using default settings
  2. Add a SyncScript asset with the following code:
using Stride.Engine;
using Stride.Input;

public class FullScreenToggler : SyncScript
{
    public override void Update()
    {
        if (Input.Keyboard.IsKeyDown(Keys.F))
        {
            Game.Window.IsFullscreen = !Game.Window.IsFullscreen;
        }
    }
}
  1. Add a ScriptComponent with the script from step 2 to the Camera (or any other) entity.
  2. Run the game using F5 hotkey (or in any other way).
  3. Toggle fullscreen mode using the "F" or "Alt+Enter" hotkey.
  4. Toggle windowed mode using exactly "F" hotkey (handled by the script from step 2).

Expected behavior Game window is switched to windowed mode like it was before step 5 execution.

Screenshots ContentSerializationExceptions

Log and callstacks

Additional context

  1. Using the "Alt+Enter" hotkey in step 6 (for switching back from fullscreen mode to windowed) works as expected.
  2. Even an empty scene with the only empty entity on it which has the ScriptComponent with the script from step 2 works as described.
  3. There are also similar issues, but they all belongs to a different kind of exception.

RedKorshun avatar Aug 06 '22 03:08 RedKorshun

Since "Alt+Enter" is working as expected this may not be a big issue, but there is a common practice to add a fullscreen toggler in game video options and it won't work there.

RedKorshun avatar Aug 06 '22 03:08 RedKorshun

I'll try to repro this later, but the exceptions you're showing are related to serialization which should have nothing to do with changing window mode, which either should be very worrying or there's something else going on here.

manio143 avatar Aug 06 '22 08:08 manio143

I'm not so sure if related, but I get a very similar exception when I try to change window size while in fullscreen mode (different asset but same serializer can't be found).

mmujic2 avatar Aug 06 '22 10:08 mmujic2

Ok, so I was able to repro this easily. Basically whenever things change for the GraphicsDevice such that it has to recreate everything it calls ResumeManager.OnReload which invokes reloading of all visual assets.

The exception message is thrown in the Reload callback of TextureContentSerializer L45, which performs a Load<Image> on the asset associated to the graphicsResource, but that graphics resource is of type Texture, which causes ContentManager to fail to retrieve it from cache of loaded objects and then try to deserialize it from the raw data, which fails again because there's no serializer compatible for a Texture that would return an Image.

While the underlying serialized data of a texture can be deserialized as an Image it seems that the way Reload callbacks are set up isn't taking all the details into account and would require deeper investigation to understand why this is happening.

The other issue is why this full reload happens in the first place. Looks like there may be an exception thrown during resizing from FullScreen in GraphicsDeviceManager.ChangeOrCreateDevice L1053. (Error 0x80004005 is a type of unspecified Windows error code)

External component has thrown an exception.
   at System.Runtime.InteropServices.Marshal.Release(IntPtr pUnk)
   at SharpDX.ComObject.SharpDX.IUnknown.Release()
   at SharpDX.ComObject.Dispose(Boolean disposing)
   at SharpDX.DisposeBase.CheckAndDispose(Boolean disposing)
   at Stride.Graphics.SwapChainGraphicsPresenter.OnDestroyed() in C:\stride\sources\engine\Stride.Graphics\Direct3D\SwapChainGraphicsPresenter.Direct3D.cs:line 184
   at Stride.Graphics.SwapChainGraphicsPresenter.set_IsFullScreen(Boolean value) in C:\stride\sources\engine\Stride.Graphics\Direct3D\SwapChainGraphicsPresenter.Direct3D.cs:line 116
   at Stride.Games.GraphicsDeviceManager.ChangeOrCreateDevice(Boolean forceCreate) in C:\stride\sources\engine\Stride.Games\GraphicsDeviceManager.cs:line 1053

manio143 avatar Aug 06 '22 22:08 manio143

@manio143 , thanks for a quick feedback. I wonder if the Alt+Enter hotkey uses a different code, because it causes no exceptions. If so, is this the expected behavior?

RedKorshun avatar Aug 06 '22 23:08 RedKorshun

I was able to trace the Alt+Enter behavior to GameForm.cs and seems to be doing the exactly same thing, except in a different moment in the game loop (before calling any of the game's systems, it happens in WindowsMessageLoop that process OS messages before calling Game.Tick() to process another frame) - this may be a better moment to modify the graphics device, but I'm not fully sure why.

manio143 avatar Aug 07 '22 07:08 manio143

It'll work if you try it like this.

if (Input.Keyboard.IsKeyDown(Keys.E))
{
    if (Game.Window.IsFullscreen)
    {
        Game.Window.Visible = false;
        Game.Window.IsFullscreen = false;
        Game.Window.Visible = true;
    }
    else
    {
        Game.Window.SetSize(new Int2(1920, 1080));
        Game.Window.PreferredFullscreenSize = new Int2(1920, 1080);
        Game.Window.IsFullscreen = true;
    }
}

artems37rus avatar Nov 12 '23 15:11 artems37rus

It'll work if you try it like this.

if (Input.Keyboard.IsKeyDown(Keys.E))
{
    if (Game.Window.IsFullscreen)
    {
        Game.Window.Visible = false;
        Game.Window.IsFullscreen = false;
        Game.Window.Visible = true;
    }
    else
    {
        Game.Window.SetSize(new Int2(1920, 1080));
        Game.Window.PreferredFullscreenSize = new Int2(1920, 1080);
        Game.Window.IsFullscreen = true;
    }
}

That's a nice workaround, tested it myself and it works like a charm. I'll leave one more note here: when changing resolution in fullscreen, these two ways worked for me:

// First way. I prefer this one because it causes less flickering
Game.Window.Visible = false;

Game.Window.SetSize(resolution);
Game.Window.PreferredFullscreenSize = resolution;

Game.Window.IsFullscreen = true;
Game.Window.Visible = true;

// Second way (switching to windowed and back to fullscreen)
gameSettings.Window.Visible = false;
gameSettings.Window.IsFullscreen = false;
gameSettings.Window.Visible = true;

gameSettings.Window.SetSize(resolution);
gameSettings.Window.PreferredFullscreenSize = resolution;
gameSettings.Window.IsFullscreen = true;

mmujic2 avatar Dec 02 '23 19:12 mmujic2