GLSL: declarar variables globales fuera del alcance de la función principal

12

¿Ayuda a declarar variables fuera del alcance de su función principal en GLSL? ¿Estas variables realmente se reutilizan y es más eficiente?

Aquí está el código en cuestión:

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 );
}
RodgerDodger
fuente
3
Esta pregunta es confusa. GLSL no tiene un bucle principal. ¿Te refieres a la main()función? ¿Sus valores son realmente variables globales (uniformes o atributos en lenguaje GLSL) o valores constantes?
Sean Middleditch
¿Puedes publicar algún código como ejemplo de lo que estás hablando?
Nathan Reed
Actualicé la pregunta con código.
RodgerDodger
@ user1286792: Eso no cambia el hecho de que GLSL no tiene un bucle principal . No está claro de qué estás hablando. ¿Qué estás pensando exactamente que se salvará al hacer esto?
Nicol Bolas
@NicolBolas He actualizado la pregunta para que sea más clara. Espero que sea útil ahora para alguien en el futuro.
RodgerDodger

Respuestas:

33

Creo que entiendo lo que intentas preguntar. Supongo que su principal preocupación son las variables no uniformes definidas fuera de main():

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

Echemos un vistazo a cómo funcionan la GPU y GLSL. La GPU no tiene una pila ni registros de activación de llamadas. No hay una manera de simular el alcance o las variables locales en GLSL como un compilador de C puede hacer en la mayoría de las CPU. Todo lo que existe son los registros, que son registros uniformes, entradas de etapa de sombreador, salidas y el archivo de registro local exclusivo de esa invocación de sombreador.

En otras palabras, como no existe una función o la pila o un montón, todas las variables declaradas en cualquier lugar viven en un registro. Si son locales para algún ámbito en GLSL o globales para todo el archivo, no hay diferencia. Son solo registros.

Sin embargo, el asignador de registros no forma parte del estándar GLSL. Las diferentes implementaciones de OpenGL pueden tener diferentes niveles de calidad cuando se trata de convertir el código GLSL de alto nivel en el código de máquina de bajo nivel que entiende la GPU. Una de las partes más complicadas de un compilador (GLSL o no) es la asignación de registros . Esta es la parte del compilador que determina qué registros ocupa una variable determinada. C lo tiene un poco más difícil ya que generalmente tiene que lidiar con archivos de registro muy pequeños (especialmente en x86) y tiene que lidiar con el derrame de registros (mover variables a la pila) y alias (guardar variables de nuevo en la RAM antes de llamar a las funciones) y instrucciones impares que exigen que la salida esté en un registro particular (x86idivpor ejemplo). Las GPU tienen un archivo de registro de gran tamaño debido a que no tienen pila ni montón, por lo que el asignador puede ser más simple.

Sin embargo, el archivo de registro no es infinito. Si tiene más variables que registros compatibles con su hardware, el compilador tendrá que intentar ajustar todas sus variables en los registros. Esto generalmente requiere alguna forma de verificación del rango de vida . Es decir, si usa una variable xnpara un cálculo y nunca la usa nuevamente, el compilador puede determinar esto y luego saber que el registro ocupado por xnpodría ser usado por otra variable más adelante, permitiendo así más variables de las que hay registros (siempre ya que no hay demasiadas variables vivas a la vez).

Sin embargo, el compilador podría no hacer esto. No tiene O podría hacerlo solo en algunos casos. Los ámbitos dados a los compiladores más simples son un problema mucho más fácil de resolver. Todos los registros asignados a las variables de función local pueden reutilizarse después de que esa función salga porque sabe que las variables están muertas. Las variables globales no tienen una garantía tan fácil. Por lo tanto, algunos compiladores menos capaces también pueden no optimizar sus vidas, y las variables globales siempre consumirán un registro. Esto no hará que nada sea más lento, pero en algunos controladores puede limitar el tamaño del sombreador que puede escribir.

En general, recomendaría mantener localizadas todas las variables. Mantenga la definición tan cerca del uso de la variable como tenga sentido. Esto se aplica a todos los lenguajes de programación, no solo a GLSL. También recomendaría hacer cada constante "variable" en todos los casos posibles. Una vez más, puede ser una pista para ciertos compiladores menos capaces de que ciertas optimizaciones son posibles y, lo que es más importante, hace que su código sea más autodocumentado y fácil de mantener.

Y, por supuesto, aquí tienes tu obligatorio "solo perfil para probar y averiguar con seguridad". Escriba su sombreador con y sin sus globales y perfílelo. Se debe desconfiar de todos y cada uno de los consejos de rendimiento en línea y se debe suponer que están impregnados o no actualizados.

Sean Middleditch
fuente
Eso es exactamente lo que quise decir. Respondiste a mi pregunta perfectamente y también explicaste mejor lo que estaba preguntando.
RodgerDodger
1
En realidad, a veces declarar las matrices como const en lugar de no const hace que todo el sombreador sea más lento (MUCHO más lento). Noté ese problema en mi GTX 460.
Tara
Acabo de deshacerme de un error extraño y sospecho fuertemente que fue una GPU Adreno (OpenGL ES 3.1) que no pudo compilar un sombreador porque las variables se declararon fuera de main.
comodoro
Una de las respuestas SO más completas que he visto, ¡bien hecho!
duhaime
Hoy he aprendido algo nuevo. Hoy, aprendo, realmente no conozco ni entiendo glsl. El hecho de que pueda usarlo para hacer gif de geometría transformada en espacio cilíndrico no significa que entiendo cómo funciona.
cmarangu