John a déjà écrit une excellente réponse, alors considérez cette réponse comme une extension de la sienne.
Je travaille actuellement beaucoup avec des calcul shaders pour différents algorithmes. En général, j'ai constaté que les shaders de calcul pouvaient être beaucoup plus rapides que leurs équivalents de pixels équivalents ou transformer les alternatives basées sur le retour.
Une fois que vous avez compris le fonctionnement des calculeurs de shaders, ils ont beaucoup plus de sens dans de nombreux cas. L'utilisation de pixels shaders pour filtrer une image nécessite la configuration d'un framebuffer, l'envoi de sommets, l'utilisation de plusieurs niveaux de shader, etc. Pourquoi devrait-il être nécessaire pour filtrer une image? Avoir l'habitude de restituer des quadruples plein écran pour le traitement d'images est certainement la seule raison "valable" de continuer à les utiliser, à mon avis. Je suis convaincu qu'un nouveau venu dans le domaine des graphiques de calcul trouverait les shaders de calcul un ajustement beaucoup plus naturel pour le traitement d'image que le rendu aux textures.
Votre question concerne le filtrage d'images en particulier, je ne m'étendrai donc pas trop sur d'autres sujets. Dans certains de nos tests, le simple fait de configurer une réaction de transformation ou de changer les objets framebuffer pour les transformer en texture peut engendrer des coûts de performance d’environ 0,2 ms. Gardez à l'esprit que cela exclut tout rendu! Dans un cas, nous avons conservé le même algorithme que celui utilisé pour calculer les shaders et constaté une augmentation notable des performances.
Lorsque vous utilisez des calcul shaders, vous pouvez utiliser davantage de silicium sur le processeur graphique pour effectuer le travail réel. Toutes ces étapes supplémentaires sont requises lors de l’utilisation de la route pixel shader:
- Assemblage de vertex (lecture des attributs de vertex, diviseurs de vertex, conversion de type, élargissement à vec4, etc.)
- Le vertex shader doit être programmé, même s'il est minimal.
- Le rastériseur doit calculer une liste de pixels pour ombrer et interpoler les sorties de sommet (probablement uniquement des coordonnées de texture pour le traitement de l'image).
- Tous les différents états (test de profondeur, test alpha, ciseaux, fusion) doivent être définis et gérés
Vous pourriez faire valoir que tous les avantages de performance mentionnés précédemment pourraient être annulés par un pilote intelligent. Tu aurais raison. Un tel pilote pourrait identifier que vous restituez un quad plein écran sans test de profondeur, etc. et configurer un "tracé rapide" qui ignore tout le travail inutile effectué pour prendre en charge les pixel shaders. Je ne serais pas surpris que certains pilotes accélèrent les passes de post-traitement dans certains jeux AAA pour leurs GPU spécifiques. Vous pouvez bien sûr oublier tout traitement de ce type si vous ne travaillez pas sur un jeu AAA.
Cependant, le conducteur ne peut pas trouver de meilleures opportunités de parallélisme offertes par le pipeline de calcul. Prenons l'exemple classique d'un filtre gaussien. En utilisant des calcul shaders, vous pouvez faire quelque chose comme ceci (séparer le filtre ou non):
- Pour chaque groupe de travail, divisez l'échantillonnage de l'image source en fonction de la taille du groupe de travail et stockez les résultats dans un groupe de mémoire partagée.
- Calculez la sortie du filtre en utilisant les exemples de résultats stockés dans la mémoire partagée.
- Écrire dans la texture de sortie
L'étape 1 est la clé ici. Dans la version pixel shader, l’image source est échantillonnée plusieurs fois par pixel. Dans la version de calcul, chaque texel source est lu une seule fois dans un groupe de travail. Les lectures de texture utilisent généralement un cache basé sur des tuiles, mais ce cache est toujours beaucoup plus lent que la mémoire partagée.
Le filtre gaussien est l’un des exemples les plus simples. D'autres algorithmes de filtrage offrent d'autres possibilités de partager des résultats intermédiaires au sein de groupes de travail utilisant la mémoire partagée.
Il y a cependant un piège. Les shaders de calcul nécessitent des barrières de mémoire explicites pour synchroniser leur sortie. Il existe également moins de garanties pour se protéger contre les accès mémoire errants. Pour les programmeurs ayant de bonnes connaissances en programmation parallèle, les calcul shaders offrent beaucoup plus de flexibilité. Cette flexibilité signifie toutefois qu'il est également plus facile de traiter les shaders de calcul comme du code C ++ ordinaire et d'écrire du code lent ou incorrect.
Les références
- Page wiki OpenGL Compute Shaders
- DirectCompute: Optimisations et meilleures pratiques, Eric Young, NVIDIA Corporation, 2010 [pdf en anglais]
- Gestion efficace de l'informatique, Bill Bilodeau, AMD, 2011? [pps]
- DirectCompute for Gaming - Suralimentez votre moteur avec Compute Shaders, Layla Mah et Stephan Hodes, AMD, 2013, [pps]
- Optimisations Compute Shader pour les GPU AMD: réduction parallèle, Wolfgang Engel, 2014