I found the problem: Rendertagets loose their contents on backbuffer change and normally need to be recreated, so needed to copy the data to a new texture.
I managed it to create a normal mip mapped Texture2D from a file with the following code.
It uses 3 Steps:
1. Load file with Texture2D.FromStream
2. Create mipmapped RenderTarget2D and render texture as sprite on it
3. Copy rendertarget texture to new Texture2D using GetData(mipLevel, ..)/SetData(mipLevel, ...)
It is unbelievable unefficient but I fear there is no better way to do it in XNA:
MemoryStream ms = new MemoryStream();
s.CopyTo(ms, 1024);
ms.Seek(0, SeekOrigin.Begin);
// load texture from file
using (Texture2D intermediateTexture = Texture2D.FromStream(Graphics, ms))
{
// create mip mapped texture
using (RenderTarget2D renderTarget = new RenderTarget2D(
Graphics,
intermediateTexture.Width,
intermediateTexture.Height,
mipMap: true,
preferredFormat: SurfaceFormat.Color,
preferredDepthFormat: DepthFormat.None,
preferredMultiSampleCount: 0,
usage: RenderTargetUsage.PreserveContents))
{
SamplerState oldSS = Graphics.SamplerStates[0];
RasterizerState oldrs = Graphics.RasterizerState;
SamplerState newss = SamplerState.LinearClamp; // todo which is best?
Graphics.SetRenderTarget(renderTarget);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, newss, DepthStencilState.None, RasterizerState.CullNone, effect: null);
spriteBatch.Draw(intermediateTexture, new Vector2(0, 0), Color.White);
spriteBatch.End();
Graphics.SetRenderTarget(null);
Graphics.DepthStencilState = DepthStencilState.Default;
Graphics.BlendState = BlendState.Opaque;
Graphics.SamplerStates[0] = oldSS;
Graphics.RasterizerState = oldrs;
// since rendertarget textures are volatile (contents get lost on device) we have to copy data in new texture
Texture2D mergedTexture = new Texture2D(Graphics, intermediateTexture.Width, intermediateTexture.Height, true, SurfaceFormat.Color);
Color[] content = new Color[intermediateTexture.Width * intermediateTexture.Height];
for (int i = 0; i < renderTarget.LevelCount; i++)
{
int n = renderTarget.Width * renderTarget.Height / ((1 << i) * (1 << i));
renderTarget.GetData<Color>(i, null, content, 0, n);
mergedTexture.SetData<Color>(i, null, content, 0, n);
}
t = mergedTexture;
}
}