Estou fazendo esta pergunta porque não encontrei uma resposta definitiva para ela.
Antes de mais, deixe-me dizer algumas coisas sobre o jogo e o que eu já fiz. O jogo será um RTS ambientado em um mundo processualmente gerado usando ruído Simplex. O mundo consiste em pedaços de 16 x 16 de sprites de 64 x 64. Consegui carregar e descarregar pedaços dinamicamente, o que funciona muito bem. O mundo parecerá um pouco com o Rimworld, de cima para baixo com diferentes camadas de sprites (primeiro terreno, sprites de transição, árvores, decalques etc.). Os mundos recém-gerados podem conter entidades que podem afetar o meio ambiente (por exemplo, uma vila que se tornou uma cidade) e, portanto, o pedaço. Tenho certeza de que isso pode ser calculado usando algum tipo de função, mas é algo a ser observado.
O principal problema que tenho é quando diminui o zoom, mais e mais peças são desenhadas, o que afeta seriamente o desempenho. Em cerca de 30000 sprites, a seção de desenho leva 8 ms, que é metade do necessário para executar a 60 FPS. E esse é apenas o terreno. Estou usando atlas de textura para limitar as contagens de empate (30000 sprites desenhados em 6 contagens).
O objetivo é diminuir o zoom do nível da cidade / vila / cidade até ver um país inteiro. Isso deve ser feito dinamicamente (por exemplo, não clicando em um ícone de minimapa, mas basta rolar para trás como no Supreme Commander).
Eu li muitos artigos sobre esse problema, mas não encontrei ou vi um exemplo claro de onde ele funcionaria. Aqui está uma lista de técnicas que eu achei que deveriam funcionar:
- Retângulos sujos, como descrito aqui , onde você só desenha coisas novas e mantém o resto no backbuffer. Isso faz muito sentido, mas eu não tenho idéia de como implementar isso no Monogame.
- Minha escolha preferida: faça uso extensivo de RenderTargets, conforme descrito aqui , onde você atrai um RenderTarget e o salva como textura. No meu caso, um pedaço de 16 x 16 composto por 64 x 64 criaria uma textura de 1024 x 1024. Eu realmente duvido que isso funcione em termos de desempenho, mas o resultado consistiria em texturas altamente detalhadas e também é ótimo de usar, considerando o fato de que a maioria é estática (terreno / árvores etc.) e não muda tanto. Mas isso também significa que toda vez que uma alteração é feita em um pedaço, o Texture2D precisa ser alterado usando o SetData, que pelo que experimentei consome bastante CPU. No entanto, se as texturas fossem 16 x 16, elas podem realmente funcionar, além de reduzir o uso de memória e disco.
- Até texturas, especificando o SourceRectangle no SpriteBatch. Para prados / oceanos enormes, isso é uma vantagem, pois apenas um sprite é desenhado. No entanto, para terrenos detalhados com cores diferentes e sprites diferentes misturados (biomas e transições de biomas), receio que não faça uma diferença enorme.
- Minha própria solução, que compromete os detalhes, era usar um ladrilho branco de 64 x 64 padrão, dar a cor dos 4 ladrilhos circundantes e escalá-lo para cobrir os 4 ladrilhos anteriores. A diferença aqui (além do azulejo ter uma cor lisa) é que a paisagem mudou visivelmente. Também devo mencionar que as florestas ainda precisam ser desenhadas individualmente, pois não são perfeitamente quadradas.
Se alguém tiver alguma idéia de como resolver esse problema, mesmo que isso comprometa certas coisas (como detalhes ou uso de sprites 16 x 16), eu adoraria ouvi-lo.
Respostas:
As placas gráficas são otimizadas para desenhar algumas coisas várias vezes, e não o contrário.
No seu caso, você tem muitas coisas (texturas diferentes) que deseja desenhar muitas vezes (número de peças).
Na IMO, as otimizações mais simples que você pode fazer para obter ganhos significativos de desempenho são:
Eu recomendaria fazer as duas coisas. Boa sorte.
fonte
Eu descobri que o SpriteBatch não é adequado para desenhar ladrilhos. Uma razão é que os sprites são destinados a objetos dinâmicos e são usados para vários propósitos. Outro motivo é que é incrivelmente ligado à CPU , por exemplo. como explicado na descrição, desenhar 30.000 objetos custa 8 ms (enquanto meu chipset atingiu o limite de 30%). Além disso, até 3000 sprites podem ser sorteados antes que uma nova chamada de sorteio seja feita para a GPU (em lote e tudo).
A solução foi desenhar meus próprios ladrilhos usando quads texturizados . Isso, em combinação com um VertexBuffer, permitiu-me desenhar até 500.000 peças texturizadas em uma chamada de desenho, onde o método de desenho consumiu aproximadamente 1/20 de um milissegundo. É claro que provavelmente não precisarei desenhar tantos, mas de qualquer maneira finalmente consegui que minha GPU tivesse uma carga de trabalho de 75% a 60 fps constantes.
fonte