O que causa a ramificação no GLSL depende do modelo da GPU e da versão do driver OpenGL.
A maioria das GPUs parece ter uma forma de operação "selecione um dos dois valores" que não tem custo de ramificação:
n = (a==b) ? x : y;
e às vezes até coisas como:
if(a==b) {
n = x;
m = y;
} else {
n = y;
m = x;
}
will be reduced to a few select-value operation with no branching penalty.
Alguns GPU / Drivers têm (tiveram?) Uma penalidade no operador de comparação entre dois valores, mas uma operação mais rápida na comparação contra zero.
Onde pode ser mais rápido:
gl_FragColor.xyz = ((tmp1 - tmp2) != vec3(0.0)) ? E : tmp1;
em vez de comparar (tmp1 != tmp2)
diretamente, mas isso depende muito da GPU e do driver; portanto, a menos que você esteja direcionando uma GPU muito específica e nenhuma outra, recomendo usar a operação de comparação e deixar esse trabalho de otimização para o driver OpenGL, pois outro driver pode ter um problema com o formato mais longo e seja mais rápido com a maneira mais simples e legível.
"Ramos" nem sempre é uma coisa ruim. Por exemplo, na GPU SGX530 usada no OpenPandora, este 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;
}
Acabou drasticamente mais rápido que esse 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;
Você nunca sabe de antemão como um compilador GLSL específico ou uma GPU específica serão executados até que você faça o benchmark.
Para adicionar o ponto final (mesmo que eu não tenha números de tempo reais e código de sombreamento para apresentá-lo para esta parte), atualmente uso como meu hardware de teste regular:
- Intel HD Graphics 3000
- Gráficos Intel HD 405
- nVidia GTX 560M
- nVidia GTX 960
- AMD Radeon R7 260X
- nVidia GTX 1050
Como uma ampla variedade de modelos diferentes e comuns de GPU para testar.
Testando cada um com drivers OpenGL e OpenCL de código aberto do Windows, proprietários de Linux e Linux.
E toda vez que tento otimizar o sombreador GLSL (como no exemplo SGX530 acima) ou as operações OpenCL para uma combinação de GPU / Driver específica, acabo afetando igualmente o desempenho em mais de uma das outras GPUs / Drivers.
Portanto, além de reduzir claramente a complexidade matemática de alto nível (por exemplo, converter 5 divisões idênticas em uma única recíproca e 5 multiplicações) e reduzir as pesquisas de textura / largura de banda, provavelmente será uma perda de tempo.
Cada GPU é muito diferente dos outros.
Se você estivesse trabalhando especificamente em (a) console (s) de jogos com uma GPU específica, isso seria uma história diferente.
O outro aspecto (menos significativo para desenvolvedores de jogos pequenos, mas ainda notável) é que os drivers de GPU de computador podem um dia substituir silenciosamente seus shaders ( se o jogo se tornar popular o suficiente ) por reescritos personalizados otimizados para essa GPU em particular. Fazendo tudo isso funcionar para você.
Eles farão isso para jogos populares que são freqüentemente usados como benchmarks.
Ou, se você der aos seus jogadores acesso aos shaders para que eles possam editá-los facilmente, alguns deles podem extrair alguns FPS extras para seu próprio benefício.
Por exemplo, existem pacotes de shader e textura feitos pelo ventilador para o Oblivion para aumentar drasticamente a taxa de quadros em um hardware que dificilmente seria reproduzido.
E, finalmente, quando seu shader ficar complexo o suficiente, seu jogo quase terminar, e você começar a testar em diferentes hardwares, estará ocupado o suficiente apenas consertando seus shaders para funcionar em uma variedade de GPUs, devido a vários erros que você não conseguirá. ter tempo para otimizá-los nesse nível.