¿Por qué es tan condicional esto en mi fragment shader?


19

He configurado un código de medición de FPS en WebGL (basado en esta respuesta SO ) y he descubierto algunas rarezas con el rendimiento de mi sombreador de fragmentos. El código solo representa un único quad (o más bien dos triángulos) sobre un lienzo de 1024x1024, por lo que toda la magia ocurre en el sombreador de fragmentos.

Considere este sombreador simple (GLSL; el sombreador de vértices es solo un paso):

// some definitions

void main() {
    float seed = uSeed;
    float x = vPos.x;
    float y = vPos.y;

    float value = 1.0;

    // Nothing to see here...

    gl_FragColor = vec4(value, value, value, 1.0);
}

Entonces, esto solo representa un lienzo blanco. Tiene un promedio de alrededor de 30 fps en mi máquina.

Ahora aumentemos el número y calculemos cada fragmento en función de unas pocas octavas de ruido dependiente de la posición:

void main() {
    float seed = uSeed;
    float x = vPos.x;
    float y = vPos.y;

    float value = 1.0;

      float noise;
      for ( int j=0; j<10; ++j)
      {
        noise = 0.0;
        for ( int i=4; i>0; i-- )
        {
            float oct = pow(2.0,float(i));
            noise += snoise(vec2(mod(seed,13.0)+x*oct,mod(seed*seed,11.0)+y*oct))/oct*4.0;
        }
      }

      value = noise/2.0+0.5;

    gl_FragColor = vec4(value, value, value, 1.0);
}

Si desea ejecutar el código anterior, he estado usando esta implementación desnoise .

Esto reduce los fps a algo así como 7. Eso tiene sentido.

Ahora la parte extraña ... calculemos solo uno de cada 16 fragmentos como ruido y dejemos los otros blancos, envolviendo el cálculo del ruido en el siguiente condicional:

if (int(mod(x*512.0,4.0)) == 0 && int(mod(y*512.0,4.0)) == 0)) {
    // same noise computation
}

Es de esperar que esto sea mucho más rápido, pero sigue siendo solo 7 fps.

Para una prueba más, filtremos los píxeles con el siguiente condicional:

if (x > 0.5 && y > 0.5) {
    // same noise computation
}

Esto proporciona exactamente la misma cantidad de píxeles de ruido que antes, pero ahora hemos vuelto a casi 30 fps.

¿Que esta pasando aqui? ¿No deberían las dos formas de filtrar un décimo sexto de los píxeles dar exactamente el mismo número de ciclos? ¿Y por qué el más lento es tan lento como representar todos los píxeles como ruido?

Pregunta extra: ¿Qué puedo hacer al respecto? ¿Hay alguna forma de trabajo en torno a la actuación horrible si yo en realidad no quiero moteado mi lienzo con sólo unos pocos fragmentos caros?

(Solo para estar seguro, he confirmado que el cálculo del módulo real no afecta la velocidad de fotogramas, al renderizar cada 16 píxeles en negro en lugar de en blanco).

Respuestas:


22

Los píxeles se agrupan en pequeños cuadrados (qué tan grande depende del hardware) y se calculan juntos en una sola tubería SIMD . (estructura del tipo de matriz de SIMD)

Esta canalización (que tiene varios nombres diferentes según el proveedor: urdimbres, frentes de onda) ejecutará operaciones para cada píxel / fragmento de forma sincronizada. Esto significa que si 1 píxel necesita un cálculo realizado, todos los píxeles lo calcularán y los que no necesitan el resultado lo tirarán.

Si todos los fragmentos siguen el mismo camino a través de un sombreador, las otras ramas no se ejecutarán.

Esto significa que su primer método de cálculo cada 16 píxeles será la ramificación en el peor de los casos.

Si aún desea reducir el tamaño de su imagen, simplemente renderice a una textura más pequeña y luego aumente la escala.


55
Renderizar a una textura más pequeña y aumentar el muestreo es una buena manera de hacerlo. Pero si por alguna razón realmente necesita escribir en cada 16 píxeles de la textura grande, usar una sombra de cálculo con una invocación por cada 16 píxeles más la carga / almacenamiento de imágenes para dispersar las escrituras en el destino de representación podría ser una buena opción.
Nathan Reed
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.