GLSL - Deklarieren globaler Variablen außerhalb des Hauptfunktionsbereichs


12

Hilft es, Variablen außerhalb Ihres Hauptfunktionsbereichs in GLSL zu deklarieren? Werden diese Variablen tatsächlich wiederverwendet und sind sie effizienter?

Hier ist der fragliche Code:

varying vec2 vposition;
uniform float seed;
uniform float top;
uniform float bottom;
uniform float phi;
uniform float theta;
uniform float scaledPI;
uniform float yn;
uniform float ym;
uniform float rx;
uniform float ry;
uniform float radius;

const float PI = 3.141592653589793238462643383;

float left;
float right;
float mscaled;
float xn;
float xm;
void main() {
    float t = vposition.y * yn + ym;

    if(t <= 0.0 || t >= PI){
        left = phi - PI;
        right = phi + PI;
    }else{
        mscaled = scaledPI / (1 - abs(Math.cos(theta)));
        mscaled = mscaled < PI ? mscaled : PI;
        left = phi - mscaled;
        right = phi + mscaled;
    }
    xn = (left - right) / ((-rx / 2.0) - (rx / 2.0));
    xm = left - ((-rx/2.0) * xn);
    float p = vposition.x * xn + xm;

    vec3 coords = vec3( sin(p) * sin(t), cos(t), cos(p) * sin(t) );
    float nv = surface( vec4( coords, seed ) );
    gl_FragColor = vec4( vec3( nv, nv, nv ), 1.0 );
}

3
Diese Frage ist verwirrend. GLSL hat keine Hauptschleife. Meinst du die main()Funktion? Sind Ihre Werte tatsächlich globale Variablen (Uniformen oder Attribute im GLSL-Sprachgebrauch) oder konstante Werte?
Sean Middleditch

Können Sie einen Code als Beispiel für das veröffentlichen, worüber Sie sprechen?
Nathan Reed

Ich habe die Frage mit Code aktualisiert.
RodgerDodger

@ user1286792: Das ändert nichts an der Tatsache, dass GLSL keine Hauptschleife hat . Es ist nicht klar, wovon du sprichst. Was genau wird Ihrer Meinung nach dadurch gerettet?
Nicol Bolas

@NicolBolas Ich habe die Frage aktualisiert, um klarer zu sein. Hoffentlich ist es jetzt für jemanden in der Zukunft nützlich.
RodgerDodger

Antworten:


33

Ich glaube, ich verstehe, was Sie fragen wollen. Ich gehe davon aus, dass Ihr Hauptanliegen die uneinheitlichen Variablen sind, die außerhalb von definiert sind main():

float left;
float right;
float mscaled;
float xn;
float xm;

Werfen wir einen Blick darauf, wie GPU und GLSL funktionieren. Die GPU verfügt nicht über einen Stapel oder einen Anrufaktivierungsdatensatz. Es gibt keine Möglichkeit, den Bereich oder lokale Variablen in GLSL zu simulieren, wie dies ein C-Compiler auf den meisten CPUs tun kann. Alles, was existiert, sind die Register, die entweder einheitliche Register, Shader-Stage-Eingänge, Ausgänge und die lokale Registerdatei sind, die für diesen Shader-Aufruf eindeutig ist.

Mit anderen Worten, da es keine Funktion, keinen Stapel oder keinen Heap gibt, leben alle irgendwo deklarierten Variablen in einem Register. Es spielt keine Rolle, ob sie in einem bestimmten Bereich in GLSL lokal oder in der gesamten Datei global sind. Sie sind nur Register.

Der Registerzuweiser ist jedoch nicht Teil des GLSL-Standards. Verschiedene OpenGL-Implementierungen können unterschiedliche Qualitätsstufen aufweisen, wenn es darum geht, GLSL-Code auf hoher Ebene in Maschinencode auf niedriger Ebene zu konvertieren, den die GPU versteht. Einer der komplizierteren Teile eines Compilers (GLSL oder anders) ist Registerzuordnung . Dies ist der Teil des Compilers, der bestimmt, welche Register eine bestimmte Variable belegt. C hat es etwas schwieriger, da es normalerweise mit sehr kleinen Registerdateien (insbesondere unter x86) und mit Registerverschüttungen (Verschieben von Variablen in den Stapel) und Aliasing (Speichern von Variablen zurück in den RAM vor dem Aufrufen von Funktionen) und zu tun hat ungerade Befehle, die verlangen, dass sich die Ausgabe in einem bestimmten Register befindet (x86)idivzum Beispiel). GPUs haben eine große Registerdatei, da sie keinen Stapel oder Heap haben, so dass der Allokator einfacher sein kann.

Die Registerdatei ist jedoch nicht unendlich. Wenn Sie mehr Variablen als Register haben, die von Ihrer Hardware unterstützt werden, muss der Compiler versuchen, alle Ihre Variablen in die Register einzupassen. Dies erfordert normalerweise eine Form der Überprüfung des Lebendigkeitsbereichs . Das heißt, wenn Sie eine Variable xnfür eine Berechnung verwenden und sie dann nie wieder verwenden, kann der Compiler dies feststellen und dann wissen, dass das von belegte belegte Register xnspäter von einer anderen Variablen verwendet werden kann, wodurch mehr Variablen zugelassen werden, als es Register gibt (so lange) da nicht zu viele Live-Variablen gleichzeitig vorhanden sind).

Der Compiler tut dies jedoch möglicherweise nicht. Es hat nicht. Oder es könnte es nur in einigen Fällen tun. Bereiche mit einfacheren Compilern sind ein viel einfacher zu lösendes Problem. Alle den lokalen Funktionsvariablen zugewiesenen Register können nach dem Beenden dieser Funktion wiederverwendet werden, da sie wissen, dass die Variablen tot sind. Globale Variablen haben keine so einfache Garantie. Daher optimieren einige weniger fähige Compiler möglicherweise auch ihre Lebensdauer nicht, und die globalen Variablen verschlingen immer ein Register. Dies macht nichts langsamer , kann jedoch bei einigen Treibern die Größe des Shaders einschränken, den Sie schreiben können.

Im Allgemeinen würde ich dringend empfehlen, alle Variablen lokal zu halten. Halten Sie die Definition so nah wie möglich an der Verwendung der Variablen. Dies gilt für alle Programmiersprachen, nicht nur für GLSL. Ich würde auch empfehlen, jede "Variable" const in jedem Fall zu machen, den Sie können. Dies kann wiederum ein Hinweis für bestimmte weniger leistungsfähige Compiler sein, dass bestimmte Optimierungen möglich sind, und vor allem macht es Ihren Code selbstdokumentierender und einfacher zu warten.

Und natürlich ist hier Ihr obligatorischer Ratschlag "Nur Profil, um zu testen und sicher herauszufinden". Schreiben Sie Ihren Shader mit und ohne Globals und profilieren Sie ihn. Alle Online-Leistungsratschläge sollten misstrauisch sein und entweder als mutmaßlich oder veraltet angesehen werden.


Genau das habe ich gemeint. Sie haben meine Frage perfekt beantwortet und auch besser erklärt, was ich gefragt habe.
RodgerDodger

1
Tatsächlich macht das Deklarieren von Arrays manchmal als const anstelle von non-const den gesamten Shader langsamer (VIEL langsamer). Ich habe dieses Problem auf meiner GTX 460 bemerkt.
Tara

Ich habe gerade einen seltsamen Fehler beseitigt und bin der festen Überzeugung, dass eine Adreno-GPU (OpenGL ES 3.1) einen Shader nicht kompilieren konnte, weil Variablen außerhalb von main deklariert wurden.
Comodoro

Eine der gründlichsten SO-Antworten, die ich je gesehen habe - gut gemacht!
Duhaime

Heute habe ich etwas Neues gelernt. Heute, so lerne ich, kenne oder verstehe ich glsl nicht wirklich. Nur weil ich es verwenden kann, um zylindrische raumtransformierte Geometrie zu erstellen, heißt das nicht, dass ich verstehe, wie es funktioniert.
Cmarangu
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.