2012-09-18 20 views
15

iOS için OpenGL ES2.0'da bir 2B anahat gölgelendiricisi uygulamaya çalışıyorum. Bu delicesine yavaş. 5 fps'de olduğu gibi yavaş. Bunu texture2D() çağrılarına kadar takip ettim. Bununla birlikte, herhangi bir konvolüsyon shader olmadan geri alınamaz. Ben medip yerine lowp kullanmayı denedim, ama her şey sadece siyah, başka bir 5fps vermesine rağmen, ama yine de kullanılamaz.Basit GLSL kıvrımlı gölgelendirici yavaş yavaş

İşte benim parça gölgelendiricim.

varying mediump vec4 colorVarying; 
    varying mediump vec2 texCoord; 

    uniform bool enableTexture; 
    uniform sampler2D texture; 

    uniform mediump float k; 

    void main() { 

     const mediump float step_w = 3.0/128.0; 
     const mediump float step_h = 3.0/128.0; 
     const mediump vec4 b = vec4(0.0, 0.0, 0.0, 1.0); 
     const mediump vec4 one = vec4(1.0, 1.0, 1.0, 1.0); 

     mediump vec2 offset[9]; 
     mediump float kernel[9]; 
     offset[0] = vec2(-step_w, step_h); 
     offset[1] = vec2(-step_w, 0.0); 
     offset[2] = vec2(-step_w, -step_h); 
     offset[3] = vec2(0.0, step_h); 
     offset[4] = vec2(0.0, 0.0); 
     offset[5] = vec2(0.0, -step_h); 
     offset[6] = vec2(step_w, step_h); 
     offset[7] = vec2(step_w, 0.0); 
     offset[8] = vec2(step_w, -step_h); 

     kernel[0] = kernel[2] = kernel[6] = kernel[8] = 1.0/k; 
     kernel[1] = kernel[3] = kernel[5] = kernel[7] = 2.0/k; 
     kernel[4] = -16.0/k; 

     if (enableTexture) { 
       mediump vec4 sum = vec4(0.0); 
      for (int i=0;i<9;i++) { 
       mediump vec4 tmp = texture2D(texture, texCoord + offset[i]); 
       sum += tmp * kernel[i]; 
      } 

      gl_FragColor = (sum * b) + ((one-sum) * texture2D(texture, texCoord)); 
     } else { 
      gl_FragColor = colorVarying; 
     } 
    } 

Bu, düzenlenmemiş ve kesinleştirilmemiş, ancak devam etmeden önce performans getirmem gerekiyor. Döngünün içindeki texture2D() çağrısını sadece bir vec4 ile değiştirmeyi denedim ve diğer her şeye rağmen sorun yok.

Bunu nasıl optimize edebilirim? Bunun mümkün olduğunu biliyorum çünkü 3D'nin problemsizce daha fazla etki yarattığını gördüm. Bunun neden sorun çıkardığını göremiyorum.

+0

"* Texture2D() çağrısını döngüde yalnızca katı bir vec4 ile değiştirmeyi denedim ve sorun değil *" Bu ne anlama geliyor? Daha hızlı mı oldu? Performansı değişmedi mi? Ne oldu? –

+0

"* Bunun neden herhangi bir soruna neden olduğunu göremiyorum. *" Shader çağrısında * dokuya * erişiyorsunuz ve bir soruna neden olabileceğini görmüyor musunuz? Ayrıca, orta dokusuna iki kez erişiyorsunuz. –

+0

Doku aramaları olmadan (sonuncu hariç) katı bir 60 fps alıyorum. Dediğim gibi, bu optimize edilmiyor, ancak bu doku çağrılarından kaçınmanın bir yolu yok. Filtre başka türlü çalışamadı. Ama çok sayıda oyun gördüm, mobil ve değil, evrişim filtrelerine dayanan efektler kullanıyorlar ve herhangi bir sorun yaşamayacak gibi görünmüyorlar. Onlardan sakınmak için bir hile yok mu? – user1137704

cevap

6

Bu gölgelendiricide alınan süreyi azaltmanın tek yolu, doku getirme sayısını azaltmaktır. Gölgelendiriciniz, merkez pikselleri hakkında eşit aralıklı noktalardan dokuları örneklediğinden ve bunları doğrusal olarak birleştirdiğinden, doku örneklemesi için GL_LINEAR modu kullanılabilirliğini kullanarak getirme sayısını azaltabilirsiniz.

Temel olarak, her bir dokuda örnekleme yapmak yerine, doğrudan bir doğrusal ağırlıklı toplam elde etmek için bir çift tekstüre arasında örnekleme yapın.

Örneklemeyi sırasıyla x0 ve x1 olarak ofset (-stepw, -steph) ve (-stepw, 0) olarak çağıralım. Sonra toplamının, x0 dan k0/(k0+k1) ve x1 bu nedenle k1/(k0+k1) uzaklıkta, bu iki texel arasında örnek yerine Şimdi eğer

sum = x0*k0 + x1*k1

, sonra GPU sırasında lineer ağırlık yapacaktır alıp , vermek

y = x1*k1/(k0+k1) + x0*k0/(k1+k0)

Böylece toplam

gibi hesaplanabilir

Sadece bir getiriden 10!

Diğer bitişik pikseller için bunu tekrarlarsanız, bitişik ofsetlerin her biri için 4 doku alma işlemi gerçekleştirirsiniz ve ortadaki piksel için fazladan bir doku getirisi elde edersiniz.

link bu çok daha iyi

38

bu kesin şeyi kendim yaptığım açıklar ve burada optimize edilebilecek bazı şeyleri görüyoruz.

İlk olarak, enableTexture koşullu koşulunu kaldırırdım ve bunun yerine gölgelendiricinizi iki programa ayırırdım, biri bunun gerçek durumu için, diğeri ise false için. IOS fragman shader'larında, özellikle de içinde doku bulunan okuyucularda şartlı koşullar çok pahalıdır.

İkincisi, burada dokuz bağımlı doku okuması var. Bunlar, doku koordinatlarının parça gölgelendirici içinde hesaplandığı doku okumalarıdır. IOS cihazlarındaki PowerVR GPU'larda bağlı doku okumaları çok pahalıdır, çünkü donanımın önbellekleme kullanarak doku okumalarını optimize etmesini engeller, çünkü 8 çevreleyen piksel ve bir merkezi nokta için sabit bir uzaklıktan örnekleme yapıyorsanız, bu hesaplamalar yapılmalıdır. köşe gölgelendirici içine taşındı.Bu aynı zamanda, her bir piksel için, her bir piksel için bir kez gerçekleştirilmesinin gerekmediği anlamına gelir ve daha sonra donanım enterpolasyonu geri kalanı işleyecektir.

Üçüncüsü, şimdilik() döngüler iOS shader derleyicisi tarafından bugüne kadar hiç kullanılmamıştı, bu yüzden elimden gelenin en iyisini yapmaya eğilimliyim.

Daha önce de belirttiğim gibi, açık kaynak kodlu iOS GPUImage çerçevesindeki bu tür evrimsel gölgelendiriciler yaptım.

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; 
} 

ve aşağıdaki fragman gölgelendirici:

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; 
} 

texelWidth ve texelHeight üniforma giriş görüntüsünün genişliğinin ters ve yüksekliği genel evrişim filtre için, aşağıdaki tepe gölgelendirici kullanmak ve convolutionMatrix forması, evrişiminizdeki çeşitli örneklerin ağırlıklarını belirtir.

Bir iPhone 4'te, bu 640x480 kamera video karesi için 4-8 ms'de çalışır; bu, bu görüntü boyutunda 60 FPS işleme için yeterince iyidir. Kenar algılama gibi bir şey yapmanız gerekiyorsa, yukarıdakileri basitleştirebilir, ön geçişte görüntüyü parlaklığa dönüştürebilir ve ardından yalnızca bir renk kanalından örnekleyebilirsiniz. Aynı cihazdaki kare başına yaklaşık 2 ms daha hızlı.

+0

Özel teşekkürler. Beni kurtardı! – hiepnd

+0

Harika bir örnek. tl; dr: ** bağımlı doku okumalarından kaçının **. Çatışmaların sayısını azaltmak için iki geçişte ayrıştırarak ayrılabilir kıvrımları test etmek için de çaba sarfetin (9 gibi bir örnek için bunun yarısından daha azına inmeyecek olmasına rağmen, bu durumda iki geçişli bir yaklaşım olabilir. kötü bir fikir olabilir) –

+1

@StevenLu - 9 doku okumasının ötesine geçtiğinizde veya bu GPU'ların çoğunda tek geçişte performansta şaşırtıcı derecede keskin bir düşüş var. Bunu iki geçişe bölmek, tek geçişteki örnek sayısına kıyasla, performans üzerinde doğrusal olmayan bir etkiye sahip olabilir. Test ettim ve bunu tek geçişte çalıştırmak, bu küçük bir dizi örnek için bile çekirdeği ayırmaktan çok daha yavaştır. –