2013-07-25 9 views
5

Apple dice nelle loro migliori pratiche per gli shader a avoid branching se possibile, e in particolare diramazioni sui valori calcolati all'interno dello shader. Quindi ho sostituito alcune istruzioni if con la funzione integrata clamp(). La mia domanda è, sono clamp(), min() e max() probabile che sia più efficiente, o sono semplicemente le funzioni di comodità (ovvero la macro) che si espandono semplicemente ai blocchi if?Best practice ES OpenGL per condizionali

Mi rendo conto che la risposta potrebbe dipendere dall'implementazione. In ogni caso, le funzioni sono ovviamente più pulite e rendono chiaro l'intento, che il compilatore potrebbe fare con.

+2

Penso che la tua ultima frase già lo risponda abbastanza bene. Oltre ad apparire molto più snelli, hanno almeno più probabilità di essere implementati da istruzioni hardware veloci di un semplice "se" è. E a parte questo suggerimento generale (che dovrebbe essere già abbastanza, però), in pratica è molto probabile che usino speciali istruzioni hardware o assegnazioni condizionali e non siano solo funzioni che contengono 'if's. –

risposta

9

Storicamente parlando le GPU hanno supportato istruzioni per frammento come MIN e MAX per molto più tempo di quanto non abbiano supportato la ramificazione condizionale arbitraria. Un esempio di questo in OpenGL desktop è l'estensione GL_ARB_fragment_program (ora sostituita da GLSL) che afferma esplicitamente che non supporta la ramificazione, ma fornisce istruzioni per MIN e MAX nonché alcune altre istruzioni condizionali.

Sarei abbastanza sicuro che tutte le GPU disporranno di hardware dedicato per queste operazioni, visto che i comuni min(), max() e clamp() sono in shader. Questo non è garantito dalle specifiche perché un'implementazione può ottimizzare il codice come meglio crede, ma nel mondo reale dovresti usare le funzioni integrate di GLSL piuttosto che rotolare le tue.

L'unica eccezione sarebbe se si stesse utilizzando il condizionale per evitare una grande quantità di elaborazione aggiuntiva dei frammenti. Ad un certo punto il costo di un ramo sarà inferiore al costo di esecuzione di tutto il codice nel ramo, ma il saldo qui sarà molto dipendente dall'hardware e dovresti fare un benchmark per vedere se effettivamente aiuta nella tua applicazione sul suo hardware di destinazione. Ecco il genere di cosa intendo:

void main() { 
    vec3 N = ...; 
    vec3 L = ...; 
    float NDotL = dot(N, L); 
    if (NDotL > 0.0) 
    { 
     // Lots of very intensive code for an awesome shadowing algorithm that we 
     // want to avoid wasting time on if the fragment is facing away from the light 
    } 
} 

Proprio serraggio NDotL per 0-1 e poi l'elaborazione sempre il codice ombra su ogni frammento solo a moltiplicare attraverso il vostro termine ombra finale NDotL è un sacco di fatica sprecata se NDotL era originariamente < = 0 e possiamo teoricamente evitare questo sovraccarico con un ramo. La ragione per cui questo genere di cose non è sempre una vittoria di prestazioni è che dipende molto da come l'hardware implementa la ramificazione dello shader.

+0

Ottima recensione. Avrei dovuto capire "MIN" e "MAX" predati blocchi generali "if", e sono sicuro che hai ragione riguardo al supporto hardware per questi. Grazie molto! –

+0

Penso che qualcosa che dovrebbe essere menzionato, è che alcune GPU non rispettano gli standard e non implementano nemmeno i rami. Gli unici su cui mi sono imbattuto sono la serie Galaxy Tab di Samsung. Si bloccano silenziosamente senza alcun messaggio o qualsiasi cosa. –