lo he hecho exactamente lo mismo, y veo varias cosas que podrían ser optimizados aquí.
En primer lugar, eliminaría el condicional enableTexture
y en su lugar dividiría su sombreador en dos programas, uno para el estado verdadero de esto y otro para falso. Los condicionales son muy caros en los sombreadores de fragmentos de iOS, especialmente los que tienen lecturas de texturas dentro de ellos.
En segundo lugar, tiene nueve lecturas de textura dependientes aquí. Estas son lecturas de textura donde las coordenadas de textura se calculan dentro del sombreador de fragmentos. Las lecturas de textura dependientes son muy costosas en las GPU PowerVR dentro de los dispositivos iOS, ya que impiden que el hardware optimice las lecturas de textura usando el almacenamiento en caché, etc. Dado que está tomando muestras de un desplazamiento fijo para los 8 píxeles circundantes y uno central, estos cálculos deben movido hacia arriba en el sombreador de vértices.Esto también significa que estos cálculos no tendrán que realizarse para cada píxel, solo una vez para cada vértice y luego la interpolación de hardware manejará el resto.
En tercer lugar, los bucles for() no han sido manejados tan bien por el compilador shader iOS hasta la fecha, por lo que tiendo a evitar aquellos donde puedo.
Como he mencionado, he hecho sombreadores de convolución como este en mi marco de código abierto iOS GPUImage. Para un filtro de convolución genérico, utilizo el siguiente shader vértice:
attribute vec4 position;
attribute vec4 inputTextureCoordinate;
uniform highp float texelWidth;
uniform highp float texelHeight;
varying vec2 textureCoordinate;
varying vec2 leftTextureCoordinate;
varying vec2 rightTextureCoordinate;
varying vec2 topTextureCoordinate;
varying vec2 topLeftTextureCoordinate;
varying vec2 topRightTextureCoordinate;
varying vec2 bottomTextureCoordinate;
varying vec2 bottomLeftTextureCoordinate;
varying vec2 bottomRightTextureCoordinate;
void main()
{
gl_Position = position;
vec2 widthStep = vec2(texelWidth, 0.0);
vec2 heightStep = vec2(0.0, texelHeight);
vec2 widthHeightStep = vec2(texelWidth, texelHeight);
vec2 widthNegativeHeightStep = vec2(texelWidth, -texelHeight);
textureCoordinate = inputTextureCoordinate.xy;
leftTextureCoordinate = inputTextureCoordinate.xy - widthStep;
rightTextureCoordinate = inputTextureCoordinate.xy + widthStep;
topTextureCoordinate = inputTextureCoordinate.xy - heightStep;
topLeftTextureCoordinate = inputTextureCoordinate.xy - widthHeightStep;
topRightTextureCoordinate = inputTextureCoordinate.xy + widthNegativeHeightStep;
bottomTextureCoordinate = inputTextureCoordinate.xy + heightStep;
bottomLeftTextureCoordinate = inputTextureCoordinate.xy - widthNegativeHeightStep;
bottomRightTextureCoordinate = inputTextureCoordinate.xy + widthHeightStep;
}
y la siguiente shader fragmento:
precision highp float;
uniform sampler2D inputImageTexture;
uniform mediump mat3 convolutionMatrix;
varying vec2 textureCoordinate;
varying vec2 leftTextureCoordinate;
varying vec2 rightTextureCoordinate;
varying vec2 topTextureCoordinate;
varying vec2 topLeftTextureCoordinate;
varying vec2 topRightTextureCoordinate;
varying vec2 bottomTextureCoordinate;
varying vec2 bottomLeftTextureCoordinate;
varying vec2 bottomRightTextureCoordinate;
void main()
{
mediump vec4 bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate);
mediump vec4 bottomLeftColor = texture2D(inputImageTexture, bottomLeftTextureCoordinate);
mediump vec4 bottomRightColor = texture2D(inputImageTexture, bottomRightTextureCoordinate);
mediump vec4 centerColor = texture2D(inputImageTexture, textureCoordinate);
mediump vec4 leftColor = texture2D(inputImageTexture, leftTextureCoordinate);
mediump vec4 rightColor = texture2D(inputImageTexture, rightTextureCoordinate);
mediump vec4 topColor = texture2D(inputImageTexture, topTextureCoordinate);
mediump vec4 topRightColor = texture2D(inputImageTexture, topRightTextureCoordinate);
mediump vec4 topLeftColor = texture2D(inputImageTexture, topLeftTextureCoordinate);
mediump vec4 resultColor = topLeftColor * convolutionMatrix[0][0] + topColor * convolutionMatrix[0][1] + topRightColor * convolutionMatrix[0][2];
resultColor += leftColor * convolutionMatrix[1][0] + centerColor * convolutionMatrix[1][1] + rightColor * convolutionMatrix[1][2];
resultColor += bottomLeftColor * convolutionMatrix[2][0] + bottomColor * convolutionMatrix[2][1] + bottomRightColor * convolutionMatrix[2][2];
gl_FragColor = resultColor;
}
El texelWidth
y texelHeight
uniformes son la inversa de la anchura y altura de la imagen de entrada y el uniforme convolutionMatrix
especifica los pesos para las diversas muestras en su convolución.
En un iPhone 4, esto se ejecuta en 4-8 ms para un video de cámara de 640x480, que es lo suficientemente bueno para 60 FPS en ese tamaño de imagen. Si solo necesita hacer algo como detección de bordes, puede simplificar lo anterior, convertir la imagen a luminancia en un paso previo, y luego solo tomar muestras de un canal de color. Eso es aún más rápido, aproximadamente 2 ms por cuadro en el mismo dispositivo.
"* He intentado reemplazar la llamada texture2D() en el bucle con solo un vec4 sólido y no se ejecuta ningún problema *" ¿Qué significa eso? ¿Se hizo más rápido? ¿No cambió el rendimiento? ¿Que pasó? –
"* No puedo ver por qué esto está causando ningún problema. *" ¿Estás haciendo * diez accesos de textura * por invocación de sombreado, y no ves lo que podría estar causando un problema? Además, accedes al centro texel dos veces. –
Obtengo un sólido 60 fps sin las búsquedas de texturas (excluyendo el final). Como dije, no está optimizado, pero no hay manera de evitar esas llamadas de textura. El filtro no podría funcionar de otra manera. Pero he visto muchos juegos, móviles y no, que usan efectos basados en filtros de convolución, y no parecen tener ningún problema. ¿A menos que haya algún truco para evitarlos? – user1137704