Como melhorar o desempenho do lote

9

Estou desenvolvendo um jogo 2D baseado em sprite para plataforma (s) móvel (s) e estou usando o OpenGL (bem, na verdade, o Irrlicht) para renderizar gráficos. Primeiro, implementei a renderização de sprite de uma maneira simples: todo objeto de jogo é renderizado como um quad com sua própria chamada de empate na GPU, o que significa que, se eu tivesse 200 objetos de jogo, fiz 200 chamadas de empate por quadro. É claro que essa foi uma péssima escolha e meu jogo foi completamente vinculado à CPU porque há uma pequena sobrecarga de CPU associada a cada chamada de empate da GPU. A GPU permaneceu ociosa a maior parte do tempo.

Agora, pensei em melhorar o desempenho coletando objetos em lotes grandes e processando esses lotes com apenas algumas chamadas de empate. Eu implementei lotes (para que todos os objetos do jogo que compartilham a mesma textura sejam renderizados no mesmo lote) e pensei que meus problemas haviam desaparecido ... apenas para descobrir que minha taxa de quadros estava ainda mais baixa do que antes.

Por quê? Bem, eu tenho 200 (ou mais) objetos de jogo e eles são atualizados 60 vezes por segundo. Cada quadro que eu tenho que recalcular nova posição (translação e rotação) para vértices na CPU (GPU em plataformas móveis não suporta instanciação, então não posso fazê-lo lá), e fazendo esse cálculo 48000 por segundo (200 * 60 * 4 desde todo sprite tem 4 vértices) simplesmente parece muito lento.

O que eu poderia fazer para melhorar o desempenho? Todos os objetos do jogo estão se movendo / girando (quase) todos os quadros, então eu realmente tenho que recalcular as posições dos vértices. A única otimização que eu conseguia pensar é uma tabela de consulta para rotações, para que eu não tivesse que calculá-las. Os sprites de ponto ajudariam? Algum hacks desagradável? Algo mais?

Obrigado.

user4241
fonte

Respostas:

5

Você usou o meu porto de irrlicht para android? Para sprites 2D no Android e iphone, eu uso os mesmos truques que você: em lotes. Eu tento muitas soluções no OpenGL ES 1.xe 2.x:

  • classifique por z (paralaxe) e por textura, faça as transformações na CPU e chame glDrawArrays ou glDrawElements (maneira mais rápida). Use uma textura grande, se puder.
  • mesmo truque com VBO, não mais rápido, porque para cada quadro você atualiza todas as informações. Pode ser útil para sprites estáticos.
  • use o OpenGL ES 2.xe o vertex shader para calcular posições (mais devagar)
  • use PointSprites (nenhuma solução se não for um quadrado e houver muitos pixels transparentes eliminam a taxa de preenchimento)
  • use a extensão gldrawtexoes ...
  • use uma chamada de chamada para cada sprite (método mais lento)

Assim como você, todas as transformações são feitas pela CPU para o OGLES 1.x ou OGLES 2.x. Se você tiver instruções de néon, poderá usá-las para acelerar seus cálculos.

Ps: em dispositivos iphone ou android, não tenho CPU limitada, mas taxa de preenchimento limitada. Portanto, é muito importante limitar o excesso.

Ellis
fonte
Excelente, isso é algo que eu estava procurando. Eu não sabia da sua porta do Irrlicht, mas já tenho minha versão do Irrlicht em execução no iOS. Você diz que não tem CPU limitada - quantos sprites você está desenhando? E quais são as taxas de quadros, digamos, de 100 sprites no iPhone? Se eu tiver 200 objetos, acabarei fazendo 48000 cálculos por segundo. Sua opinião sobre a taxa de preenchimento é boa.
user4241
Sprites estáticos (plano de fundo) estão no VBO. Eu uso um VBO por paralaxe. Caso contrário, eu tenho 100 a 200 sprites no Moblox. Em todos os iphones, incluindo o 3G, tenho mais de 30fps (pelo que me lembro). Mas grandes sprites são muito dispendiosos (problema taxa de preenchimento) ....
Ellis
Estou trabalhando em um mecanismo de partículas, no qual posso usar até 20.000 partículas com todas as posições de computação feitas na CPU e tenho 10fps com configurações extremas (no 3GS e iPhone4). Portanto, 1000 sprites devem ser possíveis no 3GS ou iPhone4 com boa taxa de quadros.
Ellis
Obrigado, muito útil! Como você está implementando seu mecanismo de partículas? Suponho que você esteja brincando com shaders?
user4241
Uso shaders porque preciso do gl_PointSize para configurar cada tamanho de partícula. Não trabalho mais com o OGLES 1.x porque os telefones antigos não são meu alvo. Primeiro, todo o meu código era OGLES 1.x, depois OGLES 1.xe OGLES 2.x (sem melhoria de desempenho) e agora OGLES 2.x (melhoria de renderização).
Ellis
1

Eu recomendaria ter um VBO, com cada vértice que contenha a posição / rotação de cada objeto renderizado e os lotes com base na textura como você está fazendo. Eu não estou muito familiarizado com o ogl ES, por isso não tenho certeza de qual versão do glsl ele suporta, mas você pode até fazer um lote com base em um conjunto de texturas e armazenar quais das quatro texturas que você está passando em que você usaria dentro do vértice. Os sprites pontuais definitivamente melhorariam seu desempenho, pois reduziriam drasticamente a quantidade de dados que você está enviando, e os lotes nunca diminuirão o desempenho se você estiver fazendo isso corretamente. Além disso, você pode melhorar um pouco o desempenho calculando a rotação no sombreador e passando apenas um valor int / float para os parâmetros ou dentro do próprio vértice. (os parâmetros seriam mais rápidos,

sringer
fonte
Obrigado pela sua resposta. Sua sugestão sobre o cálculo da rotação no shader é excellet, mas infelizmente estou usando o OpenGL ES 1, que não suporta shaders, por isso estou preso a um pipeline fixo. Vou tentar sprites de ponto, mas não posso usá-los em todos os casos porque há um limite superior para o tamanho deles. Ainda estou um pouco pessimista em relação à VBO, se estou recalculando a posição de cada vértice a cada quadro, como a VBO ajuda?
user4241
permite que seus dados de vértice permaneçam na gpu, o que diminui a quantidade de dados que você precisa enviar para a gpu em cada quadro. você não precisa de sombreadores para tirar proveito disso, não precisa alterar os dados do vértice; se tiver uma posição base (como a origem) para cada sprite, basta alterar a matriz mundial é transformar antes de chamar draw. no entanto, isso pode ser difícil durante o lote. usando a função fixa, provavelmente seria mais benéfico mudar para VBOs e abandonar o lote por enquanto, pelo menos, isso definitivamente lhe dará um impulso.
sringer 28/02
Eu entendo o seu ponto. Afinal, você não está falando de lotes, mas simplesmente usando uma chamada de empate para desenhar um objeto de jogo. Definitivamente vou testar como o VBO sem lote afeta o FPS no meu jogo, mas ainda assim 200 chamadas por quadro parecem muito grandes ... mas acho que tenho que conviver com isso. Aceitarei sua resposta se nenhuma outra resposta aparecer.
user4241
1

Você menciona plataformas móveis que não possuem instanciamento. Mas você ainda tem shaders de vértice, não é?

Nesse caso, você ainda pode executar pseudo-instanciamento, o que também é muito rápido. Faça um VBO (GL_STATIC_DRAW) com os pontos de canto (relativos ao ponto central do sprite, por exemplo, -1 / -1, 1 / -1, 1/1, -1/1) e todas as coordenadas de textura necessárias, nele .
Em seguida, defina um dos atributos genéricos do vértice para cada chamada de desenho no ponto central do sprite e desenhe os dois triângulos com o buffer vinculado. Dentro do sombreador de vértice, leia o atributo de vértice genérico e adicione as coordenadas do vértice.

Isso economizará o bloqueio de uma transferência de dados para cada sprite e deve ser muito mais rápido. O número real de chamadas de empate não é tão importante, o bloqueio / bloqueio no meio é.

dm.skt
fonte
Isso parece uma boa solução para o OpenGL ES 2.0. Infelizmente, estou usando o ES 1, que não possui shaders.
user4241
0

O problema reside na quantidade de dados que você está enviando para a GPU em cada quadro. Apenas crie um VBO para cada lote e preencha-o uma vez e aplique as matrizes de transformação correspondentes (via glMultMatrix ou um sombreador se você estiver usando o ES 2.0) ao desenhar os lotes.

r2d2rigo
fonte
Não entendo como isso ajuda quando tenho 200 objetos de jogo separados com transformações exclusivas. Usar o glMultMatrix aplicaria a mesma transformação a todos os objetos, o que não é o que eu quero. Além disso, o envio de dados para a GPU não é um gargalo; se eu remover as transformações do lado da CPU, o desempenho é muito bom.
user4241
Sim, mas uma VBO ainda pode melhorar o desempenho se aplicada corretamente. Como você está atualmente renderizando seus 200 objetos? Você está usando glBegin / glEnd?
TheBuzzSaw
11
Estou usando o mecanismo 3D Irrlicht com nó de cena personalizado, portanto não estou usando o OpenGL diretamente (mas suponho que ele esteja usando glBegin / glEnd simples neste caso). O VBO realmente ajudaria, já que eu teria que modificar todo o buffer a cada quadro? Além disso, isso não resolve o problema fundamental de ser vinculado à CPU devido aos cálculos de transformação de vértice. Mas obrigado por suas respostas de qualquer maneira!
user4241