Reputation: 327
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)");
}
}
Upvotes: 2
Views: 879
Reputation: 27245
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.
Upvotes: 2