Eu sei que o título é um pouco vago, mas é difícil descrever o que realmente estou procurando, mas aqui vai.
No que diz respeito à renderização da CPU, o desempenho é fácil de estimar e direto, mas quando se trata da GPU devido à minha falta de informações técnicas, não tenho noção. Estou usando o XNA, então seria bom se a teoria pudesse estar relacionada a isso.
Então, o que eu realmente quero saber é: o que acontece quando e onde (CPU / GPU) quando você executa ações de desenho específicas? O que é um lote? Que influência os efeitos, projeções etc. têm? Os dados persistem na placa gráfica ou são transferidos a cada etapa? Quando se fala em largura de banda, você está falando sobre a largura de banda interna da placa de vídeo ou o pipeline da CPU para a GPU?
Nota: Na verdade, não estou procurando informações sobre como o processo de desenho acontece, esse é o negócio da GPU, estou interessado em toda a sobrecarga que antecede isso.
Eu gostaria de entender o que está acontecendo quando eu faço a ação X, para adaptar minhas arquiteturas e práticas a isso.
Todos os artigos (possivelmente com exemplos de código), informações, links, tutoriais que fornecem mais informações sobre como escrever jogos melhores são muito apreciados. Obrigado :)
Respostas:
Eu gosto de pensar no desempenho em termos de " limites ". É uma maneira útil de conceituar um sistema interconectado bastante complicado. Quando você tem um problema de desempenho, faz a pergunta: "Que limites estou atingindo?" (Ou: "Estou vinculado à CPU / GPU?")
Você pode dividi-lo em vários níveis. No nível mais alto, você tem a CPU e a GPU. Você pode estar ligado à CPU (GPU ociosa aguardando CPU) ou ligada à GPU (CPU está aguardando na GPU). Aqui está um bom post sobre o assunto.
Você pode dividir ainda mais. No lado da CPU , você pode estar usando todos os seus ciclos nos dados já no cache da CPU. Ou você pode ter memória limitada , deixando a CPU ociosa aguardando a entrada de dados da memória principal ( otimize o layout dos dados ). Você poderia decompô-lo ainda mais.
(Enquanto estou fazendo uma ampla visão geral do desempenho do XNA, vou apontar que uma alocação de um tipo de referência (
class
nãostruct
), embora normalmente barata, pode acionar o coletor de lixo, o que queima muitos ciclos - especialmente no Xbox 360 . Veja aqui para mais detalhes).No lado da GPU , começarei apontando para este excelente post no blog, com muitos detalhes. Se você deseja um nível insano de detalhes no pipeline, leia esta série de postagens no blog . ( Aqui está um mais simples ).
Para simplificar, alguns dos grandes são: " limite de preenchimento " (quantos pixels você pode gravar no backbuffer - geralmente quanto excesso de excesso você pode ter), " limite de sombreador " (quão complicado pode ser seu sombreador e quantos dados você pode enviar através deles), " limite de busca de textura / largura de banda de textura " (quantos dados de textura você pode acessar).
E agora chegamos ao grande problema - que é o que você realmente está perguntando - onde a CPU e a GPU precisam interagir (por meio de várias APIs e drivers). Vagamente, existe o " limite de lote " e a " largura de banda ". (Note-se que a primeira parte da série mencionei anteriormente entra em extensos detalhes.)
Mas, basicamente, um lote ( como você já sabe ) acontece sempre que você chama uma das
GraphicsDevice.Draw*
funções (ou parte do XNA, comoSpriteBatch
faz isso por você). Como você já leu, sem dúvida, você recebe alguns milhares * por quadro. Este é um limite de CPU - portanto, ele compete com seu outro uso de CPU. É basicamente o driver empacotando tudo sobre o que você disse para desenhar e enviando para a GPU.E depois há a largura de banda para a GPU. É a quantidade de dados brutos que você pode transferir para lá. Isso inclui todas as informações de estado que acompanham os lotes - tudo, desde a definição do estado de renderização e constantes / parâmetros do sombreador (que inclui coisas como matrizes de mundo / exibição / projeto) até vértices ao usar as
DrawUser*
funções. Ele também inclui todas as chamadas paraSetData
eGetData
sobre texturas, buffers de vértice, etc.Neste ponto, devo dizer que tudo o que você pode chamar
SetData
(texturas, vértices e buffers de índice, etc), bem comoEffect
s - permanece na memória da GPU. Ele não é constantemente reenviado para a GPU. Um comando de desenho que referencia esses dados é simplesmente enviado com um ponteiro para esses dados.(Além disso: você só pode enviar comandos de desenho do encadeamento principal, mas
SetData
em qualquer encadeamento.)XNA complica as coisas um pouco com suas prestam aulas estatais (
BlendState
,DepthStencilState
, etc). Esses dados de estado são enviados por chamada de empate (em cada lote). Não tenho 100% de certeza, mas tenho a impressão de que ele é enviado preguiçosamente (ele envia apenas o estado que muda). De qualquer forma, as mudanças de estado são baratas ao ponto de graça, em relação ao custo de um lote.Finalmente, a última coisa a mencionar é o pipeline interno da GPU . Você não deseja forçá-lo a liberar escrevendo dados que ainda precisam ler ou lendo dados que ainda precisam gravar. Uma liberação de pipeline significa que aguarda a conclusão das operações, para que tudo fique em um estado consistente quando os dados forem acessados.
Os dois casos específicos a serem observados são: Chamar
GetData
qualquer coisa dinâmica - particularmente aRenderTarget2D
que a GPU possa estar escrevendo. Isso é extremamente ruim para o desempenho - não faça isso.O outro caso está chamando
SetData
buffers de vértice / índice. Se você precisar fazer isso com frequência, use umDynamicVertexBuffer
(tambémDynamicIndexBuffer
). Isso permite que a GPU saiba que mudará com frequência e faça alguma mágica de buffer internamente para evitar a descarga do pipeline.(Observe também que os buffers dinâmicos são mais rápidos que os
DrawUser*
métodos - mas precisam ser pré-alocados no tamanho máximo necessário.)... E isso é tudo o que sei sobre o desempenho do XNA :)
fonte