Jump to content
  • Advertisement
Sign in to follow this  
EarlgrayCZ

How to increase 2D sprite rendering performance

This topic is 2080 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello,

 

I plan to program the isometric strategy game. Right now I'm doing performance tests of XNA drawing and I'm really disappointed. When I draw4000 tilesFPS drops to 30 frames per second. That's a pretty poor performance, so I wonder if I do not do anything wrong.

 

My code is very simple:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace XnaDrawingPerformanceTest
{
    public class Game1 : Game
    {
        GraphicsDeviceManager _graphics;
        SpriteBatch _spriteBatch;

        SpriteFont font;
        Texture2D TilePng;
        Vector2 tilePosition, textPosition;

        public Game1()
        {
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            base.IsMouseVisible = true;

            base.Initialize();
        }

        protected override void LoadContent()
        {
            _spriteBatch = new SpriteBatch(GraphicsDevice);

            font = Content.Load<SpriteFont>("Debug");
            TilePng = Content.Load<Texture2D>("TilePng");

            tilePosition = new Vector2(32, 32);
            textPosition = new Vector2(8, 8);
        }

        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        protected override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            _spriteBatch.Begin();

            _spriteBatch.DrawString(font, "FPS: " + (1000 / gameTime.ElapsedGameTime.Milliseconds), textPosition, Color.White);

            for (int i = 0; i < 4000; i++)
            {
                _spriteBatch.Draw(TilePng, tilePosition, Color.White);
            }

            _spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

When I delete row _spriteBatch.Draw(TilePng, tilePosition, Color.White); then FPS is again full 60 frames per second. Please advice me how can I improve this code for better performance.

Share this post


Link to post
Share on other sites
Advertisement

You can try rendering to a rendertarget and then presenting to the screen, instead of rendering directly to the screen.  

Share this post


Link to post
Share on other sites

Hi, why do you need to re-draw 4000 tiles every frame ? Surely if you know that some tiles will allways be static you could draw those to one render target, render the dynamic ones to another, then blit them both together for the final frame.

 

Also you could do some spacial partitioning to determine which tiles are actually in view and only redraw those.

Thanks -NGangst

Share this post


Link to post
Share on other sites

SpriteBatch sends all the vertices to the GPU every frame. 4000 sprites at (4 * 24 bytes) apiece is 348KB of data being sent.

 

You could do the render target thing like the others mentioned, or you could forgo SpriteBatch and make a vertex buffer and draw the quads directly from that (this is assuming that the data (positions/texcoords) of the tiles don't change from frame to frame, so they don't need to be updated).

Share this post


Link to post
Share on other sites

Hi, why do you need to re-draw 4000 tiles every frame ? Surely if you know that some tiles will allways be static you could draw those to one render target, render the dynamic ones to another, then blit them both together for the final frame.

 

Also you could do some spacial partitioning to determine which tiles are actually in view and only redraw those.

Thanks -NGangst

 
It sounds very interesting but I can not imagine too. How exactly do I do it, even surface (tile) There must be redrawn on every frame because each frame of the screen is repainted any color GraphicsDevice.Clear (Color.CornflowerBlue). Or, after the tile just go through a game character. Can you please write a sample directly into my own code. Thanks a lot.

Share this post


Link to post
Share on other sites

You can try rendering to a rendertarget and then presenting to the screen, instead of rendering directly to the screen.  

 

I tried it but framerat is still 30fps

 

this is my code with render target:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace XnaDrawingPerformanceTest
{
    public class Game1 : Game
    {
        GraphicsDeviceManager _graphics;
        SpriteBatch _spriteBatch;

        SpriteFont font;
        Texture2D TilePng;
        Vector2 tilePosition, textPosition;

        RenderTarget2D renderTarget;

        public Game1()
        {
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            base.IsMouseVisible = true;

            base.Initialize();
        }

        protected override void LoadContent()
        {
            _spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here

            font = Content.Load<SpriteFont>("Debug");
            TilePng = Content.Load<Texture2D>("TilePng");

            tilePosition = new Vector2(32, 32);
            textPosition = new Vector2(8, 8);

            renderTarget = new RenderTarget2D(GraphicsDevice, 1366, 768);
        }

        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        protected override void Update(GameTime gameTime)
        {
            // TODO: Add your update logic here

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.SetRenderTarget(renderTarget);
            
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            _spriteBatch.Begin();

            _spriteBatch.DrawString(font, "FPS: " + (1000 / gameTime.ElapsedGameTime.Milliseconds), textPosition, Color.White);

            for (int i = 0; i < 4000; i++)
            {
                _spriteBatch.Draw(TilePng, tilePosition, Color.White);
            }

            _spriteBatch.End();

            GraphicsDevice.SetRenderTarget(null);

            GraphicsDevice.Clear(Color.CornflowerBlue);

            _spriteBatch.Begin();

            _spriteBatch.Draw(renderTarget, new Vector2(0, 0), Color.White);

            _spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

Share this post


Link to post
Share on other sites


I tried it but framerat is still 30fps

 

Your bottleneck is your 4000 SpriteBatch.Draw calls, and you're still doing that. The point was, if you render them to a render target, you only need to do that once. Then, in subsequent frames, you can skip that part and just draw the render target.

Share this post


Link to post
Share on other sites

 


I tried it but framerat is still 30fps

 

Your bottleneck is your 4000 SpriteBatch.Draw calls, and you're still doing that. The point was, if you render them to a render target, you only need to do that once. Then, in subsequent frames, you can skip that part and just draw the render target.

 

 

Thanks, now I understand. You helped me so much ;)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!