John ha già scritto un'ottima risposta, quindi considera questa risposta un'estensione della sua.
Attualmente sto lavorando molto con shader di calcolo per diversi algoritmi. In generale, ho scoperto che gli shader di calcolo possono essere molto più veloci del loro equivalente pixel shader o trasformare alternative basate sul feedback.
Una volta che avvolgi la testa su come funzionano gli shader di calcolo, hanno anche molto più senso in molti casi. L'utilizzo di pixel shader per filtrare un'immagine richiede l'impostazione di un framebuffer, l'invio di vertici, l'utilizzo di più livelli di shader, ecc. Perché questo dovrebbe essere richiesto per filtrare un'immagine? Essere utilizzato per il rendering di quad a schermo intero per l'elaborazione delle immagini è certamente l'unico motivo "valido" per continuare a usarli secondo me. Sono convinto che un nuovo arrivato nel campo della grafica di calcolo troverebbe gli shader di calcolo molto più naturali per l'elaborazione delle immagini rispetto al rendering in trame.
La tua domanda si riferisce in particolare al filtraggio delle immagini, quindi non tratterò troppo su altri argomenti. In alcuni dei nostri test, la semplice impostazione di un feedback di trasformazione o il passaggio di oggetti framebuffer per il rendering in una trama potrebbe comportare costi di prestazioni di circa 0,2 ms. Tieni presente che questo esclude qualsiasi rendering! In un caso, abbiamo mantenuto lo stesso algoritmo portato per calcolare gli shader e abbiamo visto un notevole aumento delle prestazioni.
Quando si utilizzano shader di calcolo, è possibile utilizzare una parte maggiore del silicio sulla GPU per eseguire il lavoro effettivo. Tutti questi passaggi aggiuntivi sono necessari quando si utilizza il percorso del pixel shader:
- Assemblaggio di vertici (lettura degli attributi dei vertici, divisori dei vertici, conversione dei tipi, espansione in vec4, ecc.)
- Lo shader di vertice deve essere programmato, non importa quanto sia minimo
- Il rasterizzatore deve calcolare un elenco di pixel per ombreggiare e interpolare gli output dei vertici (probabilmente solo trame di trama per l'elaborazione delle immagini)
- Tutti i diversi stati (test di profondità, test alfa, forbice, miscelazione) devono essere impostati e gestiti
Si potrebbe sostenere che tutti i vantaggi prestazionali precedentemente menzionati potrebbero essere annullati da un driver intelligente. Avresti ragione. Un tale driver potrebbe identificare che stai eseguendo il rendering di un quad a schermo intero senza test di profondità, ecc. E configurare un "percorso rapido" che salta tutto il lavoro inutile fatto per supportare i pixel shader. Non sarei sorpreso se alcuni driver lo facessero per accelerare i passaggi di post-elaborazione in alcuni giochi AAA per le loro GPU specifiche. Ovviamente puoi dimenticare qualsiasi trattamento del genere se non stai lavorando a un gioco AAA.
Ciò che il guidatore non può fare è tuttavia trovare migliori opportunità di parallelismo offerte dalla pipeline di shader di calcolo. Prendi il classico esempio di filtro gaussiano. Usando gli shader di calcolo, puoi fare qualcosa del genere (separando o meno il filtro):
- Per ciascun gruppo di lavoro, dividere il campionamento dell'immagine di origine tra le dimensioni del gruppo di lavoro e archiviare i risultati per raggruppare la memoria condivisa.
- Calcola l'output del filtro utilizzando i risultati del campione archiviati nella memoria condivisa.
- Scrivi sulla trama di output
Il passaggio 1 è la chiave qui. Nella versione pixel shader, l'immagine sorgente viene campionata più volte per pixel. Nella versione di shader di calcolo, ogni texel di origine viene letto una sola volta all'interno di un gruppo di lavoro. Le letture delle trame solitamente usano una cache basata su tile, ma questa cache è ancora molto più lenta della memoria condivisa.
Il filtro gaussiano è uno degli esempi più semplici. Altri algoritmi di filtro offrono altre opportunità per condividere i risultati intermedi all'interno dei gruppi di lavoro utilizzando la memoria condivisa.
C'è comunque un problema. Gli shader di elaborazione richiedono barriere di memoria esplicite per sincronizzare il loro output. Ci sono anche meno garanzie per proteggere da accessi di memoria errati. Per i programmatori con una buona conoscenza della programmazione parallela, gli shader di calcolo offrono molta più flessibilità. Questa flessibilità significa tuttavia che è anche più semplice trattare shader di calcolo come il normale codice C ++ e scrivere codice lento o errato.
Riferimenti
- Pagina wiki di OpenGL Compute Shaders
- DirectCompute: ottimizzazioni e migliori pratiche, Eric Young, NVIDIA Corporation, 2010 [pdf]
- Proramming efficiente dello shader di calcolo, Bill Bilodeau, AMD, 2011? [pps]
- DirectCompute for Gaming: potenzia il tuo motore con shader di calcolo, Layla Mah e Stephan Hodes, AMD, 2013, [pps]
- Ottimizzazione dello shader di calcolo per le GPU AMD: Parallel Reduction, Wolfgang Engel, 2014