Ho fatto questo cosa esatta me stesso, e vedo diverse cose che potrebbero essere ottimizzati qui.
Prima di tutto, rimuoverei il condizionale enableTexture
e dividere lo shader in due programmi, uno per lo stato vero di questo e uno per il falso. I condizionali sono molto costosi in shader di frammenti iOS, in particolare quelli che hanno letture di texture al loro interno.
In secondo luogo, si hanno nove letture della trama dipendenti qui. Si tratta di letture della trama in cui vengono calcolate le coordinate della trama all'interno dello shader del frammento. Le letture delle texture dipendenti sono molto costose sulle GPU PowerVR all'interno dei dispositivi iOS, perché impediscono che l'hardware ottimizzi le letture delle texture usando la cache, ecc. Poiché si esegue il campionamento da un offset fisso per gli 8 pixel circostanti e uno centrale, questi calcoli dovrebbero essere spostato verso il vertex shader.Questo significa anche che questi calcoli non dovranno essere eseguiti per ogni pixel, solo una volta per ogni vertice e quindi l'interpolazione hardware gestirà il resto.
In terzo luogo, per() i loop non sono stati gestiti molto bene dal compilatore di shader iOS fino ad oggi, quindi tendo ad evitare quelli dove posso.
Come ho già detto, ho realizzato gli ombreggiatori di convoluzione come questo nel mio framework open source iOS GPUImage. Per un filtro convoluzione generico, uso il seguente vertex shader:
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;
}
e la seguente Shader frammento:
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;
}
Il texelWidth
e texelHeight
uniformi sono l'inverso della larghezza e l'altezza dell'immagine in ingresso e l'uniforme convolutionMatrix
specifica i pesi per i vari campioni nella convoluzione.
Su un iPhone 4, questo viene eseguito in 4-8 ms per un fotogramma video 640x480, che è abbastanza buono per il rendering di 60 FPS a quella dimensione dell'immagine. Se hai solo bisogno di fare qualcosa come il rilevamento dei bordi, puoi semplificare quanto sopra, convertire l'immagine in luminanza in un pre-passaggio, quindi campionare solo da un canale di colore. Questo è ancora più veloce, a circa 2 ms per frame sullo stesso dispositivo.
"* Ho provato a sostituire la chiamata a texture2D() nel ciclo solo con un solido vec4 e non ha problemi *" Che cosa significa? È diventato più veloce? Non ha cambiato le prestazioni? Quello che è successo? –
"* Non riesco a capire perché questo causa problemi. *" Stai eseguendo * dieci accessi texture * per invocazione shader e non vedi cosa potrebbe causare un problema? Inoltre, accedi al centro texel due volte. –
Ottengo un solido 60fps senza le ricerche di texture (escluso quello finale). Come ho detto, non è ottimizzato, ma non c'è modo di evitare quelle chiamate di texture. Il filtro non potrebbe funzionare diversamente. Ma ho visto molti giochi, mobili e non, che usano effetti basati sui filtri di convoluzione, e non sembrano avere alcun problema. A meno che non ci sia un trucco per evitarli? – user1137704