Introdução O componente de FPS utilizado para esta demonstração foi publicado na comunidade SharpGames, neste link. Obs.: A única mudança feita, no componente de FPS, foi retirar a frase de exibição “Pressione (Escape) para trocar os efeitos”. Espero que o autor não fique bravo!!!!! Ganhando Desempenho em assuntos relacionados com SpriteBatch - Desenhe muitos sprites dentro de um par Begin/End
- Se possível, use SpriteSortMode.Immediate; SpriteSortMode.Immediate é mais rápido que SpriteSortMode.Deferred
- Desenhe as texturas em ordem de exibição, evitando o uso de SpriteSortMode.FrontToBack ou SpriteSortMode.BackToFront
- Se não for possível usar SpriteSortMode.Immediate, então, utilize SpriteSortMode.Texture ao invés de SpriteSortMode.Deferred
Observação Se você utiliza o bloco de código abaixo: spriteBatch.Begin(); . . . spriteBatch.End(); Por padrão, o SpriteSortMode utilizado será o SpriteSortMode.Deferred. Então, para utilizar o SpriteSortMode.Immediate, utilize o bloco de código a seguir:
spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None); . . .
Demonstração Para essa demonstração serão desenhados na tela 1.000 sprites. Também Serão demonstradas 2 formas de se fazer “a mesma coisa”, entretanto, ambas as formas obtendo diferentes resultados de performance. 1º Forma: Nesta forma, será atribuido a cada sprite 1 spriteBatch e também 1 textura. Ou seja, o “jogo” vai conter 1.000 texturas e 1.000 spriteBatchs. public class Sprite { SpriteBatch spriteBatch; public Texture2D texture; public Vector2 Position; public Sprite(Game game) { spriteBatch = new SpriteBatch(game.GraphicsDevice); } public void Draw() { spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None); spriteBatch.Draw(texture, Position, Color.White); spriteBatch.End(); } } public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; Sprite[] xna = new Sprite[1000]; currentFPSCounter fps; . . . protected override void Initialize() { // TODO: Add your initialization logic here . . . /* * na tela, será visto apenas 1 imagem pelo fato dos 1.000 sprites estarem * sendo desenhado na mesma posicao como visto no bloco de codigo abaixo * */ for (int i = 0; i < xna.Length; i++) { xna[i] = new Sprite(this); xna[i].Position.X = 400; xna[i].Position.Y = 20; } base.Initialize(); } . . . protected override void Draw(GameTime gameTime) { graphics.GraphicsDevice.Clear(Color.CornflowerBlue); for (int i = 0; i < xna.Length; i++) { xna[i].Draw(); } base.Draw(gameTime); } Resultado obtido depois de 1 minuto: 
Projeto apresentado acima para download: testspritebatch-1.zip 139988 bytes 2º Forma: Nesta forma, será utilizado apenas 1 spriteBatch e 1 textura para desenhar os 1.000 sprites. public class Sprite { public Vector2 Position; } public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Texture2D texture; Sprite[] xna = new Sprite[1000]; currentFPSCounter fps; . . . protected override void Draw(GameTime gameTime) { graphics.GraphicsDevice.Clear(Color.CornflowerBlue); // TODO: Add your drawing code here spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None); for (int i = 0; i < xna.Length; i++) { spriteBatch.Draw(texture, xna[i].Position, Color.White); } spriteBatch.End(); base.Draw(gameTime); } Resultado obtido depois de 1 minuto: 
- Utilize 1 spriteBatch para desenhar várias texturas na tela.
- Desenhe muitos sprites dentro de um par Begin/End.
- Se possivel, em jogos que você tenha que ter por exemplo: uma chuva de meteoros na tela, ou vários objetos de mesmo aspecto/textura – Utilize uma única textura para essas tarefas.
Ganhando Desempenho em assuntos relacionados a Matemática Passe objetos como Vector e/ou Matrix por referência. Tenha em mente que essa prática pode afetar na legibilidade do codigo.
Demonstração Para a demonstração a seguir utilizaremos a classe abaixo. Aonde serão verificados os resultados de performance dos metodos Update1, Update2 e Update3. Os 3 metodos, fazem a mesma coisa, entretanto fazem de maneira que diferem um do outro. class Particle { public Vector3 Position; public Vector3 Velocity; const float Friction = 0.9f; public void Update1() { Position += Velocity; Velocity *= Friction; } /// /// Passando Estruturas por Referencia /// public void Update2() { Vector3.Add(ref Position, ref Velocity, out Position); Vector3.Multiply(ref Velocity, Friction, out Velocity); } public void Update3() { Position.X += Velocity.X; Position.Y += Velocity.Y; Position.Z += Velocity.Z; Velocity.X *= Friction; Velocity.Y *= Friction; Velocity.Z *= Friction; } } Os fragmentos de codigo utilizados nos metodos Update1, Update2 e Update3 foram retirados da apresentação do Shawn Hargreaves com o tema Understanding XNA Framework Performance no evento GameFest 2007 em Washington. Resultados apresentados: Obs.: Quanto maior o número de Updates por segundo, melhor. Com o Fragmento de codigo encontrado no método: - Update1
3380000 Updates por Segundo.
- Update2
5540000 Updates por Segundo. Ou seja, (x 1.6) maior que no Update1.
- Update3
12840000 Updates por Segundo. Ou seja, (x 3.8) maior que no Update2.
Realizando mais alguns testes Serão utilizados para o teste dos métodos Updates: 400.000 partículas. public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Particle[] particle = new Particle[400000]; currentFPSCounter fps; . . . protected override void Update(GameTime gameTime) { for (int i = 0; i < particle.Length; i++) { particle[i].Update1(); //particle[i].Update2(); //particle[i].Update3(); } base.Update(gameTime); } Observe as fotos com os resultados, depois de 1 minuto decorrido: Resultado utilizando o método Update1: 
Resultado utilizando o método Update2:Obs.: Comente a linha de código - particle[i].Update1(); - e retire o comentario da linha - particle[i].Update2(); O seu metodo Update ficará: protected override void Update(GameTime gameTime) { for (int i = 0; i < particle.Length; i++) { //particle[i].Update1(); particle[i].Update2(); //particle[i].Update3(); } base.Update(gameTime); } 
Resultado utilizando o método Update3:Faça o procedimento semelhante ao feito com o teste do método Update2; você irá comentar a linha do metodo Update2 e retirar o comentario do Update3 
Projeto apresentado acima para download: testandoperformance.zip 60507 bytes Mais questões de Desempenho no Tópico Matemática:Evite o uso da linha de codigo abaixo: Position = new Vector3(1, 2, 3); Ao invés disso use: Position = new Vector3(); Position.X = 1; Position.Y = 2; Position.Z = 3; A mesma prática pode ser estendida para estruturas criadas por você. Não é por acaso que todas as Estruturas para checagem de colisão providas pelo XNA FrameWork(e também, a maioria das estruturas providas pelo XNA FrameWork), contêm um construtor vazio para, posteriormente, atribuir valores manualmente. Outros Tópicos Fontes consultadas: |