Android Opengl: gl_FragColor的替代值

时间:2022-09-10 21:53:45

I'm writing an Android application that utilizes opengl to perform some changes to the camera output. I've written my code in such a way that I finally figured out what is causing the performance issue.

我正在编写一个Android应用程序,它利用opengl对相机输出进行一些修改。我已经以这样的方式编写了代码,最终找出了导致性能问题的原因。

#extension GL_OES_EGL_image_external : require
precision mediump float;
uniform samplerExternalOES sTexture;
uniform vec4 vColor;
const int MAX_COLORS = 6;
uniform float vHues[MAX_COLORS];
uniform float vOffsets[MAX_COLORS];
varying vec2 v_CamTexCoordinate;

float rgb2hue(vec4 c) {
    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
    vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));

    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return abs(q.z + (q.w - q.y) / (6.0 * d + e));
}

bool isInRange(float a1,float a2, float vOffset) {
    if(a2 < 0.0) {
        return false;
    } else if(abs(a1-a2) < vOffset) {
        return true;
    } else if( a1 > a2) {
        return (1.0 - a1 + a2) < vOffset;
    } else {
        return (1.0 + a1 - a2) < vOffset;
    }
}

vec4 getGrey(vec4 c) {
    float grey = (c.r + c.g + c.b) / 3.0;
    return vec4(grey, grey, grey, c.a);
}

void main() {
    vec4 c = texture2D(sTexture, v_CamTexCoordinate);
    bool hasColor = vHues[0] >= 0.0;
    float hue = rgb2hue(c);
    vec4 test = getGrey(c);
    for(int i=0; i < MAX_COLORS; i++) {
       if(isInRange(hue, vHues[i], vOffsets[i])) {
          //If I uncomment this line the performance gets terrible
          //test = c;
       }
    }
    gl_FragColor = test;
}

There is a significant hit to performance (a lot of frames are skipped) when I uncomment the line above. I basically want to use the original color sometimes and a different color depending on some condition. It works but is there a more efficient way to do this? Also, why does this perform so poorly?

当我取消上面一行的注释时,会对性能造成重大影响(很多帧被跳过)。我基本上是想用原来的颜色,根据不同的情况,用不同的颜色。它是有效的,但是有更有效的方法吗?而且,为什么它的表现如此糟糕?

2 个解决方案

#1


1  

If statements and braching in general are a bad idea because your GPU can't optimise properly. See Efficiency of branching in shaders for more details. You should try avoiding the branch, even if it means more calculation.

如果你的GPU不能很好地进行优化,那么总的来说是一个糟糕的想法。有关更多细节,请参见着色器中的分支效率。你应该试着避开这个分支,即使它意味着更多的计算。

The following code should do the trick

下面的代码应该可以做到这一点

//summarise your isInRange function
bool isInRange = a2 >= 0.0 && ((abs(a1-a2) < vOffset) || (1.0 - max(a1, a2) + min(a1, a2)) < vOffset); 

//this will be 0.0 if false and 1.0 if true
float isInRangef = float(isInRange);

//multiply by the float condition to set the value
gl_FragColor = isInRangef * c + (1.0 - isInRangef) * getGrey(c);

As a general formula

作为一个通用的公式

  • convert the bool you're branching on to a float condition
  • 将正在分支的bool转换为浮点条件
  • set your target value as (condition)*valueIfTrue + (1.0 - condition)*valueIfFalse
  • 将目标值设置为(条件)*valueIfTrue +(1.0 -条件)*valueIfFalse

#2


1  

When you comment out that line, you're almost certainly allowing the compiler to remove the entire loop and you're reducing the code snippet to:

当你注释这一行的时候,你几乎可以肯定的是,你允许编译器删除整个循环,你把代码片段减少到:

gl_FragColor = getNewColor(color);

I think we'd need to see the value of MAX_COLORS, vColors and the isValid function before we'd have an idea why performance is so poor.

我认为我们需要看到MAX_COLORS、vColors和isValid函数的值,然后才能理解为什么性能如此糟糕。

Generally, conditionals in fragment shaders on OpenGLES are bad news. If it's possible then replacing your loop with some sort of lookup table (i.e. a texture you can sample) would solve your performance problem.

一般来说,在OpenGLES上的片段着色的条件是坏消息。如果可能的话,那么用某种查找表(例如,可以对纹理进行采样)替换循环将解决性能问题。

Whether it's feasible to do so depends on the problem you're trying to solve and there isn't enough context.

这样做是否可行取决于你要解决的问题,而且没有足够的上下文。

EDIT: OK, now that more information is posted, I can see that your hue comparison function (isInRange) is 1 dimensional, and would be well suited to be changed to a lookup table. You should look toward replacing the entire loop with a texture lookup. So that this:

编辑:好的,现在发布了更多的信息,我可以看到您的颜色比较函数(isInRange)是1维的,非常适合将其更改为查找表。您应该考虑用纹理查找替换整个循环。所以这个:

vec4 test = getGrey(c);
for(int i=0; i < MAX_COLORS; i++) {
   if(isInRange(hue, vHues[i], vOffsets[i])) {
      //If I uncomment this line the performance gets terrible
      //test = c;
   }
}
gl_FragColor = test;

Would become something like this:

会变成这样:

gl_FragColor = mix(getGrey(c), c, texture1d(sLookupTableTex, hue).r);

Constructing such a lookup table will no doubt be fiddly, and if vHues and vOffsets is changing all the time, then the lookup table will need to change all the time also which will have it's own performance implications, but I think the gains from replacing the loop and all the conditionals with a texture lookup will be enormous.

建造这样一个查找表无疑会繁琐,如果vHues vOffsets正在改变,然后查找表需要改变所有的时间也会有它自己的性能影响,但我认为收益取代循环和条件与纹理查找将是巨大的。

#1


1  

If statements and braching in general are a bad idea because your GPU can't optimise properly. See Efficiency of branching in shaders for more details. You should try avoiding the branch, even if it means more calculation.

如果你的GPU不能很好地进行优化,那么总的来说是一个糟糕的想法。有关更多细节,请参见着色器中的分支效率。你应该试着避开这个分支,即使它意味着更多的计算。

The following code should do the trick

下面的代码应该可以做到这一点

//summarise your isInRange function
bool isInRange = a2 >= 0.0 && ((abs(a1-a2) < vOffset) || (1.0 - max(a1, a2) + min(a1, a2)) < vOffset); 

//this will be 0.0 if false and 1.0 if true
float isInRangef = float(isInRange);

//multiply by the float condition to set the value
gl_FragColor = isInRangef * c + (1.0 - isInRangef) * getGrey(c);

As a general formula

作为一个通用的公式

  • convert the bool you're branching on to a float condition
  • 将正在分支的bool转换为浮点条件
  • set your target value as (condition)*valueIfTrue + (1.0 - condition)*valueIfFalse
  • 将目标值设置为(条件)*valueIfTrue +(1.0 -条件)*valueIfFalse

#2


1  

When you comment out that line, you're almost certainly allowing the compiler to remove the entire loop and you're reducing the code snippet to:

当你注释这一行的时候,你几乎可以肯定的是,你允许编译器删除整个循环,你把代码片段减少到:

gl_FragColor = getNewColor(color);

I think we'd need to see the value of MAX_COLORS, vColors and the isValid function before we'd have an idea why performance is so poor.

我认为我们需要看到MAX_COLORS、vColors和isValid函数的值,然后才能理解为什么性能如此糟糕。

Generally, conditionals in fragment shaders on OpenGLES are bad news. If it's possible then replacing your loop with some sort of lookup table (i.e. a texture you can sample) would solve your performance problem.

一般来说,在OpenGLES上的片段着色的条件是坏消息。如果可能的话,那么用某种查找表(例如,可以对纹理进行采样)替换循环将解决性能问题。

Whether it's feasible to do so depends on the problem you're trying to solve and there isn't enough context.

这样做是否可行取决于你要解决的问题,而且没有足够的上下文。

EDIT: OK, now that more information is posted, I can see that your hue comparison function (isInRange) is 1 dimensional, and would be well suited to be changed to a lookup table. You should look toward replacing the entire loop with a texture lookup. So that this:

编辑:好的,现在发布了更多的信息,我可以看到您的颜色比较函数(isInRange)是1维的,非常适合将其更改为查找表。您应该考虑用纹理查找替换整个循环。所以这个:

vec4 test = getGrey(c);
for(int i=0; i < MAX_COLORS; i++) {
   if(isInRange(hue, vHues[i], vOffsets[i])) {
      //If I uncomment this line the performance gets terrible
      //test = c;
   }
}
gl_FragColor = test;

Would become something like this:

会变成这样:

gl_FragColor = mix(getGrey(c), c, texture1d(sLookupTableTex, hue).r);

Constructing such a lookup table will no doubt be fiddly, and if vHues and vOffsets is changing all the time, then the lookup table will need to change all the time also which will have it's own performance implications, but I think the gains from replacing the loop and all the conditionals with a texture lookup will be enormous.

建造这样一个查找表无疑会繁琐,如果vHues vOffsets正在改变,然后查找表需要改变所有的时间也会有它自己的性能影响,但我认为收益取代循环和条件与纹理查找将是巨大的。