8
\$\begingroup\$

Summary: I get FPS slowdown as soon as I try to tint the sprites (i.e: multiply texture with color in the fragment shader)

Details:

Hardware: iPod touch 4

I am drawing 700 sprites on the screen using glDrawArrays. And yes I am batching all of these in a single draw call. Following shows Vertex data structure:

struct Vertex {
    float Position[2];
    float Color[4];
    float Texture[2];
};

Yes I am sending colour with each vertex because I selectively need to tint some sprites but not others. Following is the fragment shader I am using:

varying lowp vec2 TexCoord;
uniform sampler2D TextureSampler;

void main(void)
{
    gl_FragColor = texture2D( TextureSampler, TexCoord );
}

Till now it is working GREAT, giving me full 60 FPS !!!

BUT

As soon as I change the fragment shader to the following (to allowing tinting):

varying lowp vec4 DestinationColor;
varying lowp vec2 TexCoord;
uniform sampler2D TextureSampler;

void main(void)
{
    gl_FragColor = texture2D( TextureSampler, TexCoord ) * DestinationColor;
}

Using following 64x64 png texture containing alpha channel, rendering with glEnable(GL_BLEND):

enter image description here

The performance drops to 47 FPS only due to this single change {just by multiplication with ONE vector} (FPS measured using xcode instruments and OpenGL detective). Any ideas what is going on ?

Thanks.

Edit:

I've also tried stripping off per vertex color attribute:

struct Vertex {
    float Position[2];
    float Texture[2];
};

And modifying the fragment shader as follows:

precision lowp float;
varying lowp vec2 TexCoord;
uniform sampler2D TextureSampler;

void main(void)
{
    gl_FragColor = texture2D( TextureSampler, TexCoord ) * vec4(1.0,0.0,0.0,1.0);
}

It is running at 52 FPS for 700 sprites (a gain of only 5 FPS). So this is not interpolation, seems like the multiplication is extremely expensive. Just this ONE multiplication?

\$\endgroup\$
5
  • \$\begingroup\$ Do you have vsync enabled? The numbers could mean that after the change you start missing every other vsync, which leads to 45 FPS on average. \$\endgroup\$
    – msell
    Commented May 23, 2013 at 19:26
  • \$\begingroup\$ I am testing on iPhone 4, I suppose vsync is already enabled by default. It is showing 47 FPS by the way in xcode Instruments so i think vsync is not really an issue at all. But my real question is: Why the performance slow down and how to improve it? \$\endgroup\$
    – fakhir
    Commented May 23, 2013 at 21:42
  • 1
    \$\begingroup\$ Does your texture have an alpha channel? If the texture does not have an alpha channel, and the rgb is multiplied with a vec3, does it draw at 60 fps again? \$\endgroup\$
    – Will
    Commented May 23, 2013 at 23:44
  • \$\begingroup\$ Yes, the texture has alpha channel. Please see the texture attached above. \$\endgroup\$
    – fakhir
    Commented May 24, 2013 at 1:43
  • 2
    \$\begingroup\$ Single core SGX 535, high-DPI display with a GPU never meant to handle it. Performance of graphics at native res on those devices was always awful. You should either downscale resolution or target 30 fps or target newer hardware. You're expecting miracles out of that GPU. It doesn't take much to tank it. \$\endgroup\$ Commented May 24, 2013 at 22:44

1 Answer 1

2
\$\begingroup\$

I don't think the performance problem is happening on the multiplication, but on the interpolation of your DestinationColor across the triangles, between vertex and fragment shaders. You have four floats to interpolate between tree vertices, for each fragment for each sprite.

For 700 sprites 64x64 pixels each, this is 11468800 additional operations per frame that you're asking the GPU to perform. It's quite possible that you will be missing some vsyncs, and therefore dropping to 40-ish FPS.

If you want each vertex to have a different color, so you can have gradients for each sprite, you're out of luck. There's also some other tricks you may want to try, but I think this is not the case.

Since what you seem to be doing is tinting each sprite, you could demote your DestinationColor to a uniform, use it directly in the fragment shader, and change it for each call. That way no interpolations will take place. You will lose the entire batching, but you may be able to batch a bit if you sort them by color.

\$\endgroup\$
3
  • \$\begingroup\$ I've edited the question above and added some details. Basically I tried stripping off per vertex color and simply multiply the texture with a CONSTANT vector i.e.: gl_FragColor = texture2D( TextureSampler, TexCoord ) * vec4(1.0,0.0,0.0,1.0);. Got 52 FPS, a gain of almost 5 FPS. But still way too slow as compared to no tint. Slow down of 8 FPS only due to a single vector multiplication ? \$\endgroup\$
    – fakhir
    Commented May 24, 2013 at 16:26
  • 3
    \$\begingroup\$ But it's not a single multiplication - it's ~11 million per frame. \$\endgroup\$ Commented May 24, 2013 at 16:57
  • 1
    \$\begingroup\$ @fakhir The iPod Touch 4 display's resolution is 960x640 pixels. That's 614400 pixels in total. You want to render 700 sprites 64x64 pixels each. That's 2867200 pixels, or almost 5 times the entire screen. You probably got your original 60 fps because the optimizer figured out what you were doing and possibly sampled the image just once, but don't expect that to happen for all cases. Mobile graphics programming is much more limited than desktop programming, so act accordingly. \$\endgroup\$ Commented May 26, 2013 at 14:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .