XNA 3.1 - 4.0 requires constant redrawing or displays a purple screen

For the menus in my game, I draw them once on the screen and then redraw them if they are considered dirty. This is done using a logical set equal to true when the user performs an action that should trigger a redraw, and then the drawing cycle checks this value before drawing the menu. This logic worked fine in 3.1, but in 4.0 the menu flickers (drawn on 1 frame) and then shows a purple screen until it is drawn again.

I created a very simple test game in 4.0 to demonstrate the problem shown below. You will notice that the screen just looks purple. If you remove the _isDirty line setting to false, you will see the blue color of the cornflower.

public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; bool _isDirty = true; public Game1() { graphics = new GraphicsDeviceManager(this); } protected override void Draw(GameTime gameTime) { if (_isDirty) { GraphicsDevice.Clear(Color.CornflowerBlue); _isDirty = false; } base.Draw(gameTime); } } 

How can I get behavior from XNA 3.1? I have seen a few people mention PreserveContents, but that doesn't look like effect 4.0 unless I apply it incorrectly.

+6
source share
3 answers

Here's a rough overview of what gets called up in an XNA game:

 Update BeginDraw Draw EndDraw 

Default EndDraw by default calls GraphicsDevice.Present . When double buffering is enabled, this replaces the rear and front buffers.

So what happens:

  • First time: you draw your scene onto the back buffer, and then let XNA swap it to the front.

  • The second time: you do not draw anything, leaving the surface filled with purple, which DirectX initializes these surfaces and replaces them in front!

  • Subsequent points: you are not drawing anything, so you will see the display flicker between the two surfaces.

There are several ways to suppress drawing in XNA. I consider them in this answer to a similar question . As in this answer, I recommend you override BeginDraw, for example:

 protected override bool BeginDraw() { if(_isDirty) return base.BeginDraw(); else return false; } 

When BeginDraw returns false, Draw and EndDraw (and therefore Present ) will not be called this frame. Nothing will be drawn, and the front / rear buffers will not change.

+13
source share

Recently, I also came across this; I don't think this is a problem with flipping buffers. This should be the same in 3.1 and 4.0. I looked at GraphicsDevice using ILSpy and found out about this:

What has changed is that in 4.0, the current render target is first cleared in GraphicsDevice.Present () if a private flag is set. By default, this flag will be set initially (target rendering is clear). Drawing to the render target clears this flag (now the render target is dirty). After executing this function, GraphicsDevice.Present () sets the flag again (the rendering object has been "reset" to your window and is now considered clean again).

To get the results from GraphicsDevice.Present (), you will need to call Draw () and EndDraw () in strict order.

If you call EndDraw () without first calling Draw (), you will not get the contents of the rendering target (which is cleared), instead there will only be a purple screen.

Unfortunately, the flag is closed and there is no clean way to get to it. You can use reflection to clear a flag like this, making the render object dirty without drawing anything in it:

graphicsDevice.GetType (). GetField ("lazyClearFlags", BindingFlags.NonPublic | BindingFlags.Instance) .SetValue (graphicsDevice, 0);

(graphicsDevice is your current instance of GraphicsDevice)

Put the above line before calling EndDraw (), and the behavior will revert to what it was in 3.1

+2
source share

In my particular case, I have a state machine that switches between the states responsible for drawing. Therefore, when I switch between the two game states, there is nothing to draw, and the screen blinks purple until the next game state is activated.

What I did to solve it was just in updating, if I have no state, call the SuppressDraw () method in the game object. This prevents the drawing from being executed until the next update.

I believe that there is nothing that would prevent you from always calling it in Update, unless you set the isDirty flag. But you should be prepared to handle other events / cases where the screen may be damaged.

0
source share

All Articles