2
votes

I'm writing a 2D game in the latest XNA (C#). I'm doing a bit of an overhaul of old code and have just put back in buttons to change resolution. However, after either fullscreen mode being toggled or the window size/resolution being changed the screen is just black (the color of my clear screen call) forever, although the game still runs, as I can quit it with a keypress. This didn't happen in my previous version - it all worked fine - but there's no difference in the relevant code I can find. However I did redo my graphics loading to use my own Texture loader instead of the Content Loader - could this be the issue?

If no other option, is there a simple way to make the game restart, as the graphics are ok and in the selected format after a restart?

My code is:

 public override void Click()
    {
        base.Click();
        Settings.Default.screenWidth = resolution[0];
        Settings.Default.screenHeight = resolution[1];
        Settings.Default.Save();
        Variables.screenWidth = Settings.Default.screenWidth;
        Variables.screenHeight = Settings.Default.screenHeight;

        Game1.graphics.PreferredBackBufferWidth = Variables.screenWidth;
        Game1.graphics.PreferredBackBufferHeight = Variables.screenHeight;
        Game1.graphics.ApplyChanges();
    }

Thanks!

Edit: My texture loadign code - loads all files with a name included in an enum as Texture2Ds..

public static void LoadAll(string subFolder)
    {
        List<string> s = Directory.GetFiles(path + "\\" + subFolder, "*.png", SearchOption.AllDirectories).ToList<string>();
        foreach (string S in s)
        {
            if (Enum.IsDefined(typeof(T), Path.GetFileNameWithoutExtension(S)))
            {
                FileStream stream = new FileStream(S, FileMode.Open);
                Texture2D t = Texture2D.FromStream(Game1.graphics.GraphicsDevice, stream);
                RenderTarget2D result = null;

                //Setup a render target to hold our final texture which will have premulitplied alpha values
                result = new RenderTarget2D(Game1.graphics.GraphicsDevice, t.Width, t.Height);

                Game1.graphics.GraphicsDevice.SetRenderTarget(result);
                Game1.graphics.GraphicsDevice.Clear(Microsoft.Xna.Framework.Color.Black);

                //Multiply each color by the source alpha, and write in just the color values into the final texture
                BlendState blendColor = new BlendState();
                blendColor.ColorWriteChannels = ColorWriteChannels.Red | ColorWriteChannels.Green | ColorWriteChannels.Blue;

                blendColor.AlphaDestinationBlend = Blend.Zero;
                blendColor.ColorDestinationBlend = Blend.Zero;

                blendColor.AlphaSourceBlend = Blend.SourceAlpha;
                blendColor.ColorSourceBlend = Blend.SourceAlpha;


                Game1.spriteBatch.Begin(SpriteSortMode.Immediate, blendColor);
                Game1.spriteBatch.Draw(t, t.Bounds, Microsoft.Xna.Framework.Color.White);
                Game1.spriteBatch.End();

                //Now copy over the alpha values from the PNG source texture to the final one, without multiplying them
                BlendState blendAlpha = new BlendState();
                blendAlpha.ColorWriteChannels = ColorWriteChannels.Alpha;

                blendAlpha.AlphaDestinationBlend = Blend.Zero;
                blendAlpha.ColorDestinationBlend = Blend.Zero;

                blendAlpha.AlphaSourceBlend = Blend.One;
                blendAlpha.ColorSourceBlend = Blend.One;

                Game1.spriteBatch.Begin(SpriteSortMode.Immediate, blendAlpha);
                Game1.spriteBatch.Draw(t, t.Bounds, Microsoft.Xna.Framework.Color.White);
                Game1.spriteBatch.End();

                //Release the GPU back to drawing to the screen

                Game1.graphics.GraphicsDevice.SetRenderTarget(null);


                t = result;

                textureDictionary.Add(Path.GetFileNameWithoutExtension(S), t);
            }
           // else
            //  Console.WriteLine("Did not load -- " + Path.GetFileNameWithoutExtension(S) + " -- (add to enum to enable loading)");
        }
    }

Edit: The working code from following below advice - probably not the most efficient but it works!

public static void LoadAll(string subFolder)
    {
        List<string> s = Directory.GetFiles(path + "\\" + subFolder, "*.png", SearchOption.AllDirectories).ToList<string>();
        foreach (string S in s)
        {
            if (Enum.IsDefined(typeof(T), Path.GetFileNameWithoutExtension(S)))
            {
                FileStream stream = new FileStream(S, FileMode.Open);
                Texture2D t = Texture2D.FromStream(Game1.graphics.GraphicsDevice, stream);
                RenderTarget2D result = null;
                Texture2D resultTexture;

                //Setup a render target to hold our final texture which will have premulitplied alpha values
                result = new RenderTarget2D(Game1.graphics.GraphicsDevice, t.Width, t.Height);

                Game1.graphics.GraphicsDevice.SetRenderTarget(result);
                Game1.graphics.GraphicsDevice.Clear(Microsoft.Xna.Framework.Color.Black);

                //Multiply each color by the source alpha, and write in just the color values into the final texture
                BlendState blendColor = new BlendState();
                blendColor.ColorWriteChannels = ColorWriteChannels.Red | ColorWriteChannels.Green | ColorWriteChannels.Blue;

                blendColor.AlphaDestinationBlend = Blend.Zero;
                blendColor.ColorDestinationBlend = Blend.Zero;

                blendColor.AlphaSourceBlend = Blend.SourceAlpha;
                blendColor.ColorSourceBlend = Blend.SourceAlpha;


                Game1.spriteBatch.Begin(SpriteSortMode.Immediate, blendColor);
                Game1.spriteBatch.Draw(t, t.Bounds, Microsoft.Xna.Framework.Color.White);
                Game1.spriteBatch.End();

                //Now copy over the alpha values from the PNG source texture to the final one, without multiplying them
                BlendState blendAlpha = new BlendState();
                blendAlpha.ColorWriteChannels = ColorWriteChannels.Alpha;

                blendAlpha.AlphaDestinationBlend = Blend.Zero;
                blendAlpha.ColorDestinationBlend = Blend.Zero;

                blendAlpha.AlphaSourceBlend = Blend.One;
                blendAlpha.ColorSourceBlend = Blend.One;

                Game1.spriteBatch.Begin(SpriteSortMode.Immediate, blendAlpha);
                Game1.spriteBatch.Draw(t, t.Bounds, Microsoft.Xna.Framework.Color.White);
                Game1.spriteBatch.End();

                //Release the GPU back to drawing to the screen

                Game1.graphics.GraphicsDevice.SetRenderTarget(null);

                resultTexture = new Texture2D(Game1.graphics.GraphicsDevice, result.Width, result.Height);
                Color[] data = new Color[result.Height * result.Width];
                Color[] textureColor = new Color[result.Height * result.Width];

                result.GetData<Color>(textureColor);

                resultTexture.SetData(textureColor);

                textureDictionary.Add(Path.GetFileNameWithoutExtension(S), resultTexture);
            }
           // else
            //  Console.WriteLine("Did not load -- " + Path.GetFileNameWithoutExtension(S) + " -- (add to enum to enable loading)");
        }
    }
1
The code you've posted is fine. Possibly something is wrong with your texture loader - are you doing anything whacky with render targets? It's possible that your textures are not handling "device lost" correctly. You could try minimising your game or locking your session - I'm pretty sure these will also cause device-lost (not 100% sure) - and see if you get the same result.Andrew Russell
Thanks @Andrew, I suspect I may be doing something wacky with whatever render targets are. Also how does one handle device lost? I'll edit my answer to show the texture loading code.Mr Pie

1 Answers

2
votes

As per your updated code, you're copying textures into a render target (RenderTarget2D).

Unlike regular textures, these are not backed by CPU-side memory. Regular textures will automatically re-set their data on the GPU when the graphics device is lost. A render target, however, will raise its ContentLost event and set IsContentLost - you are then expected to reset its content yourself!

There are a few solutions: You could simply respond to ContentLost and refresh the data.

I prefer to use GetData and SetData at load time to copy the contents of the render target into a regular Texture2D, because this "just works". Although for simple cases like switching a texture to use premultiplied alpha, I prefer to just keep everything on the CPU.

I've got a very detailed answer about RenderTarget2D usage for fixed textures over here. It includes code to do premultiplication directly on the CPU.

Although in your case, I'd try and use the content manager. It is possible to have it process all files in a directory and then load them dynamically.