João já escreveu uma ótima resposta, então considere essa resposta uma extensão dele.
Atualmente, estou trabalhando muito com shaders de computação para diferentes algoritmos. Em geral, descobri que os sombreadores de computação podem ser muito mais rápidos que seus sombreadores de pixel equivalentes ou transformar alternativas baseadas em feedback.
Depois de entender como os shaders de computação funcionam, eles também fazem muito mais sentido em muitos casos. O uso de pixels shaders para filtrar uma imagem requer a configuração de um buffer de quadro, o envio de vértices, o uso de vários estágios de shader etc. Por que isso é necessário para filtrar uma imagem? Estar acostumado a renderizar quads em tela cheia para processamento de imagem é certamente o único motivo "válido" para continuar usando-os na minha opinião. Estou convencido de que um iniciante no campo de computação gráfica consideraria os sombreadores de computação um ajuste muito mais natural para o processamento de imagens do que renderizar para texturas.
Sua pergunta refere-se à filtragem de imagens em particular, para que não seja elaborado muito sobre outros tópicos. Em alguns de nossos testes, apenas a configuração de um feedback de transformação ou a troca de objetos de buffer de estrutura para renderizar em uma textura pode resultar em custos de desempenho em torno de 0,2ms. Lembre-se de que isso exclui qualquer renderização! Em um caso, mantivemos exatamente o mesmo algoritmo portado para calcular shaders e observamos um aumento notável no desempenho.
Ao usar sombreadores de computação, mais silício na GPU pode ser usado para fazer o trabalho real. Todas essas etapas adicionais são necessárias ao usar a rota do sombreador de pixels:
- Montagem de vértice (lendo os atributos de vértice, divisores de vértice, conversão de tipo, expandindo-os para vec4 etc.)
- O sombreador de vértice precisa ser agendado, não importa quão mínimo seja
- O rasterizador precisa calcular uma lista de pixels para sombrear e interpolar as saídas de vértice (provavelmente apenas cordas de textura para processamento de imagem)
- Todos os diferentes estados (teste de profundidade, teste alfa, tesoura, mistura) devem ser definidos e gerenciados
Você pode argumentar que todas as vantagens de desempenho mencionadas anteriormente podem ser negadas por um driver inteligente. Você estaria certo. Esse driver pode identificar que você está renderizando um quad em tela cheia sem teste de profundidade etc. e configurar um "caminho rápido" que ignora todo o trabalho inútil feito para oferecer suporte a pixel shaders. Não ficaria surpreso se alguns drivers fizessem isso para acelerar os passes de pós-processamento em alguns jogos AAA para suas GPUs específicas. É claro que você pode esquecer qualquer tratamento se não estiver trabalhando em um jogo AAA.
No entanto, o que o motorista não pode fazer é encontrar melhores oportunidades de paralelismo oferecidas pelo pipeline do shader de computação. Veja o exemplo clássico de um filtro gaussiano. Usando sombreadores de computação, você pode fazer algo assim (separando o filtro ou não):
- Para cada grupo de trabalho, divida a amostragem da imagem de origem pelo tamanho do grupo de trabalho e armazene os resultados para agrupar a memória compartilhada.
- Calcule a saída do filtro usando os resultados da amostra armazenados na memória compartilhada.
- Gravar na textura de saída
Etapa 1 é a chave aqui. Na versão do pixel shader, a imagem de origem é amostrada várias vezes por pixel. Na versão do shader de computação, cada texel de origem é lido apenas uma vez dentro de um grupo de trabalho. As leituras de textura geralmente usam um cache baseado em bloco, mas esse cache ainda é muito mais lento que a memória compartilhada.
O filtro gaussiano é um dos exemplos mais simples. Outros algoritmos de filtragem oferecem outras oportunidades para compartilhar resultados intermediários dentro de grupos de trabalho usando memória compartilhada.
No entanto, há um problema. Os sombreadores de computação exigem barreiras explícitas à memória para sincronizar sua saída. Também há menos salvaguardas para proteger contra acessos de memória incorretos. Para programadores com bom conhecimento de programação paralela, os sombreadores de computação oferecem muito mais flexibilidade. Essa flexibilidade, no entanto, significa que também é mais fácil tratar shaders de computação como código C ++ comum e escrever código lento ou incorreto.
Referências
- Página da wiki OpenGL Compute Shaders
- DirectCompute: Otimizações e práticas recomendadas, Eric Young, NVIDIA Corporation, 2010 [pdf]
- Proramming eficiente de sombreadores de computação, Bill Bilodeau, AMD, 2011? [pps]
- DirectCompute for Gaming - sobrecarregue seu mecanismo com sombreadores de computação, Layla Mah e Stephan Hodes, AMD, 2013, [pps]
- Otimizações de computação de sombreador para GPUs AMD: redução paralela, Wolfgang Engel, 2014