Le cause della ramificazione in GLSL dipendono dal modello GPU e dalla versione del driver OpenGL.
La maggior parte delle GPU sembra avere una forma di operazione "seleziona uno dei due valori" che non ha costi di ramificazione:
n = (a==b) ? x : y;
e a volte anche cose come:
if(a==b) {
n = x;
m = y;
} else {
n = y;
m = x;
}
sarà ridotto ad alcune operazioni a valore selezionato senza penalità di ramificazione.
Alcuni GPU / Driver hanno (avuto?) Un po 'di penalità sull'operatore di confronto tra due valori ma un'operazione più veloce sul confronto contro zero.
Dove potrebbe essere più veloce fare:
gl_FragColor.xyz = ((tmp1 - tmp2) != vec3(0.0)) ? E : tmp1;
piuttosto che confrontare (tmp1 != tmp2)
direttamente ma questo dipende molto dalla GPU e dal driver, quindi a meno che tu non stia prendendo di mira una GPU molto specifica e nessun altro ti consiglio di utilizzare l'operazione di confronto e lasciare quel lavoro di ottimizzazione al driver OpenGL poiché un altro driver potrebbe avere un problema con la forma più lunga ed essere più veloce con il modo più semplice e più leggibile.
Anche i "rami" non sono sempre una brutta cosa. Ad esempio sulla GPU SGX530 utilizzata in OpenPandora, questo shader scale2x (30ms):
lowp vec3 E = texture2D(s_texture0, v_texCoord[0]).xyz;
lowp vec3 D = texture2D(s_texture0, v_texCoord[1]).xyz;
lowp vec3 F = texture2D(s_texture0, v_texCoord[2]).xyz;
lowp vec3 H = texture2D(s_texture0, v_texCoord[3]).xyz;
lowp vec3 B = texture2D(s_texture0, v_texCoord[4]).xyz;
if ((D - F) * (H - B) == vec3(0.0)) {
gl_FragColor.xyz = E;
} else {
lowp vec2 p = fract(pos);
lowp vec3 tmp1 = p.x < 0.5 ? D : F;
lowp vec3 tmp2 = p.y < 0.5 ? H : B;
gl_FragColor.xyz = ((tmp1 - tmp2) != vec3(0.0)) ? E : tmp1;
}
Finì drammaticamente più veloce di questo shader equivalente (80ms):
lowp vec3 E = texture2D(s_texture0, v_texCoord[0]).xyz;
lowp vec3 D = texture2D(s_texture0, v_texCoord[1]).xyz;
lowp vec3 F = texture2D(s_texture0, v_texCoord[2]).xyz;
lowp vec3 H = texture2D(s_texture0, v_texCoord[3]).xyz;
lowp vec3 B = texture2D(s_texture0, v_texCoord[4]).xyz;
lowp vec2 p = fract(pos);
lowp vec3 tmp1 = p.x < 0.5 ? D : F;
lowp vec3 tmp2 = p.y < 0.5 ? H : B;
lowp vec3 tmp3 = D == F || H == B ? E : tmp1;
gl_FragColor.xyz = tmp1 == tmp2 ? tmp3 : E;
Non si sa mai in anticipo come si comporteranno un compilatore GLSL specifico o una GPU specifica fino a quando non lo si confronta.
Per aggiungere il punto to (anche se non ho numeri di temporizzazione effettivi e codice shader per presentarti per questa parte) attualmente uso come normale hardware di test:
- Intel HD Graphics 3000
- Grafica Intel HD 405
- nVidia GTX 560M
- nVidia GTX 960
- AMD Radeon R7 260X
- nVidia GTX 1050
Come una vasta gamma di modelli GPU diversi e comuni con cui testare.
Test di ciascuno con driver OpenGL e OpenCL open source per Windows, proprietari di Linux e Linux.
E ogni volta che provo a micro-ottimizzare lo shader GLSL (come nell'esempio di SGX530 sopra) o le operazioni OpenCL per una particolare combinazione di GPU / Driver finisco per danneggiare ugualmente le prestazioni su più di una delle altre GPU / Driver.
Quindi, oltre a ridurre chiaramente la complessità matematica di alto livello (ad esempio: convertire 5 divisioni identiche in un unico reciproco e 5 moltiplicazioni) e ridurre le ricerche di trama / larghezza di banda, molto probabilmente sarà una perdita di tempo.
Ogni GPU è troppo diversa dalle altre.
Se lavorassi specificamente su (a) console di gioco con una GPU specifica, questa sarebbe una storia diversa.
L'altro aspetto (meno significativo per gli sviluppatori di giochi di piccole dimensioni ma comunque notevole) è che i driver GPU per computer potrebbero un giorno sostituire silenziosamente i tuoi shader ( se il tuo gioco diventa abbastanza popolare ) con quelli riscritti personalizzati ottimizzati per quella particolare GPU. Fare tutto questo funziona per te.
Lo faranno per giochi popolari che vengono spesso usati come benchmark.
Oppure, se dai ai tuoi giocatori l'accesso agli shader in modo che possano facilmente modificarli, alcuni di loro potrebbero spremere qualche FPS in più a proprio vantaggio.
Ad esempio, ci sono shader e texture pack creati dai fan per Oblivion per aumentare notevolmente la frequenza dei fotogrammi su hardware altrimenti a malapena riproducibile.
E infine, una volta che lo shader diventa abbastanza complesso, il gioco è quasi completato e inizi a testare su hardware diverso, sarai abbastanza occupato solo a sistemare gli shader in modo che funzionino su una varietà di GPU poiché è dovuto a vari bug che non vuoi avere tempo per ottimizzarli a quel livello.