Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ToggleFullScreen in XNA causes lost device like errors

I'm trying to write my first XNA game, and I'd like to include ALT+ENTER functionality to toggle between full screen and windowed modes. My game's extremely basic right now, nothing fancy. I can get it to run in either windowed (default) or full screen (by calling ToggleFullScreen) in my LoadContent function, and everything works great.

However, when I call ToggleFullScreen in my Update function, using the exact same code, my game fails. If I start windowed and switch to full screen, it seems to freeze, displaying only one frame and not accepting keyboard input (I have to CTRL+ALT+DEL). If I start fullscreen and switch to windowed, it errors on DrawIndexedPrimitive with the message "The current vertex declaration does not include all the elements required by the current vertex shader. Normal0 is missing." (which is not true; my vertex buffer is VertexPositionNormalTexture and contains data, and the GraphicsDevice status is Normal).

Both of these issues seem to be pointing to there being a lost connection with the device somehow, but I can't figure out why. Every resource I've found online says toggling full screen is as easy as calling the ToggleFullScreen function, with no mention of resetting devices or buffers. Any ideas?

Edit: Here's some code, with extraneous stuff left out:

    protected override void LoadContent()
    {
        graphics.PreferredBackBufferWidth = 800; 
        graphics.PreferredBackBufferHeight = 600;
        graphics.ToggleFullScreen();

        basicEffect = new BasicEffect(graphics.GraphicsDevice);
        // etc.
    }

    protected override void Update(GameTime gameTime)
    {
        if (k.IsKeyDown(Keys.Enter) && (k.IsKeyDown(Keys.LeftAlt) || k.IsKeyDown(Keys.RightAlt)) && !oldKeys.Contains(Keys.Enter)) {
            graphics.ToggleFullScreen();
            gameWorld.Refresh();
        }
         // update the world's graphics; this fills my buffers
        GraphicsBuffers gb = gameWorld.Render(graphics.GraphicsDevice);
        graphics.GraphicsDevice.SetVertexBuffer(gb.vb, 0);
        graphics.GraphicsDevice.Indices = gb.ib;
   }

    protected override void Draw(GameTime gameTime)
    {
        // buffer reference again; these are persistent once filled in Update, they don't get created anew
        GraphicsBuffers gb = gameWorld.Render(graphics.GraphicsDevice);

        foreach (EffectPass effectPass in basicEffect.CurrentTechnique.Passes)
        {
            effectPass.Apply();

            basicEffect.Texture = textures;
            graphics.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, gb.vb.VertexCount, 0, gb.ib.IndexCount / 3);
        }
    }
like image 490
tloflin Avatar asked Apr 21 '26 10:04

tloflin


2 Answers

The XNA Game class and the associated GraphicsDeviceManager are built around the concept that the Update function is for updating the state of your game and the Draw function is for rendering that state. When you set the state of the graphics device during the Update function you break that assumption.

When you toggle to fullscreen the device is lost. There is a lot of magic happening behind the scenes in the Framework to hide the complexity of this from you. The resources associated with the device have to be recreated etc. The framework is assuming you won't do any rendering operations again until the next Draw function and so it doesn't guarantee a consistent state until that happens.

So... Don't set your buffers on the graphics device during the update function. Do this at the start of the drawing function.

I'm not sure that this is explicitly documented anywhere. It is more implied by the method documentation. The Game.Draw documentation says:

Called when the game determines it is time to draw a frame. Override this method with game-specific rendering code.

and the update method says:

Called when the game has determined that game logic needs to be processed. This might include the management of the game state, the processing of user input, or the updating of simulation data. Override this method with game-specific logic.

like image 190
Empyrean Avatar answered Apr 25 '26 02:04

Empyrean


According to one answer on the App Hub forums you have to make sure you rebuild your projection matrix.

http://forums.create.msdn.com/forums/p/31773/182231.aspx

from that link....

Also, be careful about different aspect ratios when toggling full screen. If you're in 3D, you'll need to rebuild your projection matrix. If you're in 2D, you'll probably need to pass a scaling matrix to your SpriteBatch.Begin functions. This article has information about doing this in 2D.

So you'll want to make sure you're doing that (unfortunately the article that post links to was on Ziggyware and that site has been gone for quite a while so to find the 2D example they linked to you might have to use some archive site like the WaybackMachine if you're interested in looking at it, since you're doing 3D I figured it might not be of interest).

like image 31
George Clingerman Avatar answered Apr 25 '26 02:04

George Clingerman



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!