Desenho enorme quantidade de peças em Monogame (XNA) de forma eficiente

8

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.

Guguhl Pluhs
fonte
Não permita que o usuário diminua o zoom tanto #
Bálint
Também tive problemas de desempenho com meu projeto MonoGame gerado processualmente. Minha solução foi limitar o que foi renderizado (limitar o zoom out e desenhar apenas o que está na tela). Parece que o MonoGame foi projetado com um mantra "desenhe tudo a cada quadro", mas espero que alguém mais experiente com o MonoGame me corrija.
Falácia lógica
Como você renderiza as peças? Mais especificamente - você usa o SpriteBatch e como o usa?
loodakrawa

Respostas:

3

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:

  1. Combine sua textura em atlas. Isso deve acelerar significativamente as coisas, já que agora sua placa gráfica está desenhando apenas uma (ou duas) texturas, em vez de muitas pequenas.
  2. Lote seus blocos com o SpriteBatch no modo SpriteSortMode.Texture. Isso classificará seus sprites por textura e minimizará o número de opções de textura para o número real de diferentes texturas. A desvantagem dessa abordagem é que seus sprites não serão classificados por profundidade. O que é presumivelmente bom, já que seus ladrilhos estão todos na mesma profundidade. Suponho que você tenha outras coisas também; portanto, faz sentido separar um SpriteBatch para blocos e outro para todo o resto (definido como SpriteSortMode.BackToFront).

Eu recomendaria fazer as duas coisas. Boa sorte.

loodakrawa
fonte
2

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.

Guguhl Pluhs
fonte
Parece muito bom. Gostaria de saber mais sobre isso - você seguiu algum tutorial ou possui links com uma descrição mais detalhada desse processo?
precisa saber é o seguinte
2
Eu aprendi a maioria sobre todos os lugares que mencionavam XNA / Monogame e quads texturizados. No entanto, o riemers.net me ajudou muito, pois é direcionado a pessoas que só querem configurar algo rapidamente que funcione. Você vai querer seguir especificamente o tutorial do terreno em 3D, uma vez que também abrange VertexBuffers e IndexBuffers. Transformar em um tipo de terreno de ladrilhos não deve ser um problema.
Guguhl Pluhs
Se é isso que responder a mais a sua pergunta, você é mais que bem-vindo para marcá-lo como aceite :)
Vaillancourt
@GuguhlPluhs Eu sei que é tarde, mas você ainda tem mais algumas informações sobre esse assunto? Estou enfrentando o mesmo problema.
21919 Jelle