Jump to content

  • Log In with Google      Sign In   
  • Create Account

Interested in a FREE copy of HTML5 game maker Construct 2?

We'll be giving away three Personal Edition licences in next Tuesday's GDNet Direct email newsletter!

Sign up from the right-hand sidebar on our homepage and read Tuesday's newsletter for details!


XNA Generate Mip Maps with Texture.FromStream()


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
10 replies to this topic

#1 codymanix   Members   -  Reputation: 132

Like
0Likes
Like

Posted 06 January 2013 - 04:56 PM

Hi!

 

Can I generate Mip Maps in XNA without using the content pipeline? When I use Texture.FromStream there is no parameter which can be used for that.

 

Is it even recommended using XNA without the content pipeline? I know that it provides faster content loading and its content importers save your quite a bit coding..

 

But the thing with the content pipeline is that everybody who wants/needs to change game content (graphics designer, sound artist or even a hobbyist modder) needs to have an installed copy of visual studio. That is quite bad, so I want to avoid using the content pipeline.

 



Sponsor:

#2 PhillipHamlyn   Members   -  Reputation: 454

Like
2Likes
Like

Posted 07 January 2013 - 02:49 PM

// Input Image is a byte[] from your Png
System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(this.InputImage);
memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
Texture2D intermediateTexture = Texture2D.FromStream(deviceAccessor.GetCurrentGraphicsDevice(), memoryStream);

Texture2D texture = null;
RenderTarget2D renderTarget = new RenderTarget2D(deviceAccessor.GetCurrentGraphicsDevice(), InputImageSize.Width, InputImageSize.Height, mipMap: true, preferredFormat: surfaceFormat, preferredDepthFormat: DepthFormat.None);

BlendState blendState = BlendState.Opaque;

currentGraphicsDevice.SetRenderTarget(renderTarget);
using (SpriteBatch sprite = new SpriteBatch(currentGraphicsDevice))
{
sprite.Begin(SpriteSortMode.Immediate, blendState, samplerState, DepthStencilState.None, RasterizerState.CullNone,
effect: null);
sprite.Draw(this.IntermediateTexture, new Vector2(0, 0), Color.White);
sprite.End();
}

texture = (Texture2D)renderTarget;
currentGraphicsDevice.SetRenderTarget(null);
intermediateTexture.Dispose();
return texture;

Edited by PhillipHamlyn, 07 January 2013 - 02:52 PM.


#3 codymanix   Members   -  Reputation: 132

Like
0Likes
Like

Posted 10 January 2013 - 02:50 PM

Thank you for the reply!

 

I also found out that I could use new Texture2D(device,w,h,true,format) and then call SetData() on it. Would this be possible? Will it automatically create a valid mip map too?

 

I believe that there is a performance penalty on writeable textures (RenderTarget2D). Is this true?



#4 PhillipHamlyn   Members   -  Reputation: 454

Like
0Likes
Like

Posted 10 January 2013 - 03:34 PM

I suspect the data you are setting using SetData must already have the mipmap set into it - but I'm not sure.

 

I dont believe writable textures are bad for performance, so long as you create them and dont then lock and update - in my case, the texture once written stays static.



#5 phil_t   Crossbones+   -  Reputation: 3944

Like
0Likes
Like

Posted 11 January 2013 - 06:41 AM

SetData provides a parameter that indicates with mip level you are supplying the data for. You'll need to calculate the mipmaps yourself if you do it this way.

 

Also note that using Dxt-compressed textures (which can provide space savings, and significant performance improvements if you're bandwidth-limited) becomes much harder if you're not using the content pipeline. It's not possible if you're using a RenderTarget2D as shown above. And if you're using Texture2D.SetData, you'll need to supply your own code to compress the texture data.



#6 PhillipHamlyn   Members   -  Reputation: 454

Like
0Likes
Like

Posted 12 January 2013 - 05:33 AM

SetData provides a parameter that indicates with mip level you are supplying the data for. You'll need to calculate the mipmaps yourself if you do it this way.

 

Also note that using Dxt-compressed textures (which can provide space savings, and significant performance improvements if you're bandwidth-limited) becomes much harder if you're not using the content pipeline. It's not possible if you're using a RenderTarget2D as shown above. And if you're using Texture2D.SetData, you'll need to supply your own code to compress the texture data.

 

Phil_t - the constructor for the RenderTarget2D allows you to specify your SurfaceFormat, which I set to DXT1 - so the original code post does support compression, however I would agree that the codemanix should avoid SetData if they are wanting mipmap support.



#7 phil_t   Crossbones+   -  Reputation: 3944

Like
0Likes
Like

Posted 12 January 2013 - 07:42 AM

Phil_t - the constructor for the RenderTarget2D allows you to specify your SurfaceFormat, which I set to DXT1 - so the original code post does support compression, however I would agree that the codemanix should avoid SetData if they are wanting mipmap support.

 

It allows you to specify your SurfaceFormat, but the compressed surface formats aren't supported as rendertargets.



#8 PhillipHamlyn   Members   -  Reputation: 454

Like
0Likes
Like

Posted 13 January 2013 - 11:24 AM

Phil_t - the constructor for the RenderTarget2D allows you to specify your SurfaceFormat, which I set to DXT1 - so the original code post does support compression, however I would agree that the codemanix should avoid SetData if they are wanting mipmap support.

 

 

It allows you to specify your SurfaceFormat, but the compressed surface formats aren't supported as rendertargets.

 

Ok, I see that now - its interesting that XNA doesn't complain when I request DXT1 but just gives me back a Color surface. I hadn't noticed it ignored my preferred format before now. Thanks for the tip.



#9 codymanix   Members   -  Reputation: 132

Like
0Likes
Like

Posted 03 February 2013 - 08:54 AM

// Input Image is a byte[] from your Png
System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(this.InputImage);
memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
Texture2D intermediateTexture = Texture2D.FromStream(deviceAccessor.GetCurrentGraphicsDevice(), memoryStream);

Texture2D texture = null;
RenderTarget2D renderTarget = new RenderTarget2D(deviceAccessor.GetCurrentGraphicsDevice(), InputImageSize.Width, InputImageSize.Height, mipMap: true, preferredFormat: surfaceFormat, preferredDepthFormat: DepthFormat.None);

BlendState blendState = BlendState.Opaque;

currentGraphicsDevice.SetRenderTarget(renderTarget);
using (SpriteBatch sprite = new SpriteBatch(currentGraphicsDevice))
{
sprite.Begin(SpriteSortMode.Immediate, blendState, samplerState, DepthStencilState.None, RasterizerState.CullNone,
effect: null);
sprite.Draw(this.IntermediateTexture, new Vector2(0, 0), Color.White);
sprite.End();
}

texture = (Texture2D)renderTarget;
currentGraphicsDevice.SetRenderTarget(null);
intermediateTexture.Dispose();
return texture;

 

Thank you for the Code. It first seemed to work, it creates textures which also look mip mapped.

But now if I switch during the running game to fullscreen or activate multisampling (Anything that needs

 

GraphicsDeviceManager.ApplyChanges()
 

 

to be called), then things go mad.

My textures seem to be exchanged and sometimes I see nothing at all. When I use the original texture loading mechanism through content pipeline, it works again.

 

My first thought was passing

 

RenderTargetUsage.PreserveContents
 

 

when creating the rendertarget, but this didn't help.

 

Do you have an idea what the problem could be?



#10 codymanix   Members   -  Reputation: 132

Like
0Likes
Like

Posted 03 February 2013 - 12:49 PM

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;
                    }                   
                }
 


#11 phil_t   Crossbones+   -  Reputation: 3944

Like
0Likes
Like

Posted 04 February 2013 - 09:44 AM

Render targets (and dynamic vertex buffers, and a few other things) lose their data when the graphics device is reset. So another option is to listen for the GraphicsDevice.DeviceReset event and just regenerate your render target then.

(Note: you don't need RenderTargetUsage.PreserveContents in the code you have above).




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS