Sign in to follow this  
  • entries
    162
  • comments
    262
  • views
    166906

Spot the difference

Sign in to follow this  
OrangyTang

68 views

Original version:

public void addToBuffer(Buffers dest)
{
float innerRadius = radius - edgeWidth;
innerRadius = Math.max(innerRadius, 0);

final float outerRadius = radius;

final float vTexel = (1f / edgeImage.getTexture().getHeight()) / 2;


for (int i=0; i {
float angle0 = (((float)Math.PI*2f)/(NUM_SUBDIVISIONS)) * i;
float angle1 = (((float)Math.PI*2f)/(NUM_SUBDIVISIONS)) * (i+1);

angle0 += angleOffset;
angle1 += angleOffset;


final float alpha0 = i / (float)(NUM_SUBDIVISIONS);
final float u0 = getU(alpha0, edgeImage);

final float alpha1 = (i+1) / (float)(NUM_SUBDIVISIONS);
final float u1 = getU(alpha1, edgeImage);

// --- First edge ---
dest.vertex( coord(innerRadius*(float)Math.cos(angle0) * xScale + x,
innerRadius*(float)Math.sin(angle0) * yScale + y),
textureCoord(u0, edgeImage.getTy1()-vTexel),
colour(red, green, blue, alpha) );


dest.vertex( coord(outerRadius*(float)Math.cos(angle0) * xScale + x,
outerRadius*(float)Math.sin(angle0) * yScale + y),
textureCoord(u0, edgeImage.getTy0()+vTexel),
colour(red, green, blue, alpha) );


// --- Second edge ---
dest.vertex( coord(outerRadius*(float)Math.cos(angle1) * xScale + x,
outerRadius*(float)Math.sin(angle1) * yScale + y),
textureCoord(u1, edgeImage.getTy0()+vTexel),
colour(red, green, blue, alpha) );


dest.vertex( coord(innerRadius*(float)Math.cos(angle1) * xScale + x,
innerRadius*(float)Math.sin(angle1) * yScale + y),
textureCoord(u1, edgeImage.getTy1()-vTexel),
colour(red, green, blue, alpha) );
}

}

private static float getU(float alpha, SpriteImage image)
{
// Offset the u coord by half a texel so that the left and right texels of the
// sprite map directly next to each other.
// Otherwise we get a seam due to texture filtering

final float halfUTexel = (1f / image.getTexture().getWidth()) / 2f;

final float invAlpha = 1f - alpha;

return ((image.getTx0()+(halfUTexel)) * invAlpha) + ((image.getTx1()-halfUTexel) * alpha);
}



Optimised version:
public void addToBuffer(Buffers dest)
{
// Loop constants
float innerRadius = radius - edgeWidth;
innerRadius = Math.max(innerRadius, 0);

final float outerRadius = radius;

final float vTexel = (1f / edgeImage.getTexture().getHeight()) / 2;
final float halfUTexel = (1f / edgeImage.getTexture().getWidth()) / 2f;

final float ty0 = edgeImage.getTy0() + vTexel;
final float ty1 = edgeImage.getTy1() - vTexel;

// Start at angleOffset and add angleInc each iteration
final float angleInc = ((float)Math.PI*2f)/NUM_SUBDIVISIONS;
float angle0 = angleOffset;
float angle1 = angleOffset + angleInc;

// Calculate the first pair of cos/sin, then reuse the previous iteration's values after that
float cosAngle1 = (float)Math.cos(angle0);
float sinAngle1 = (float)Math.sin(angle0);
float cosAngle0, sinAngle0;

// Start with the initial alpha0, then reuse the previous iteration's values after that
float alpha0;
float alpha1 = 0;

for (int i=0; i {
// Reuse the texture alpha from the last iteration
alpha0 = alpha1;
alpha1 = (i+1) / (float)NUM_SUBDIVISIONS;

// Calculate texture interpolation texture coords
final float invAlpha0 = 1f - alpha0;
final float u0 = ((edgeImage.getTx0()+(halfUTexel)) * invAlpha0) + ((edgeImage.getTx1()-halfUTexel) * alpha0);

final float invAlpha1 = 1f - alpha1;
final float u1 = ((edgeImage.getTx0()+(halfUTexel)) * invAlpha1) + ((edgeImage.getTx1()-halfUTexel) * alpha1);

// Reuse the cos/sin from last iteration
cosAngle0 = cosAngle1;
sinAngle0 = sinAngle1;
cosAngle1 = (float)Math.cos(angle1);
sinAngle1 = (float)Math.sin(angle1);

// Geometry coord generation
final float cosAngle0Scaled = cosAngle0 * xScale;
final float sinAngle0Scaled = sinAngle0 * yScale;

final float cosAngle1Scaled = cosAngle1 * xScale;
final float sinAngle1Scaled = sinAngle1 * yScale;

// --- First edge ---
dest.vertex( innerRadius * cosAngle0Scaled + x,
innerRadius * sinAngle0Scaled + y,
u0, ty1,
red, green, blue, alpha);

dest.vertex( outerRadius * cosAngle0Scaled + x,
outerRadius * sinAngle0Scaled + y,
u0, ty0,
red, green, blue, alpha);

// --- Second edge ---
dest.vertex( outerRadius * cosAngle1Scaled + x,
outerRadius * sinAngle1Scaled + y,
u1, ty0,
red, green, blue, alpha);

dest.vertex( innerRadius * cosAngle1Scaled + x,
innerRadius * sinAngle1Scaled + y,
u1, ty1,
red, green, blue, alpha);

angle0 = angle1;
angle1 += angleInc;
}

}



I wanted to add some water ripples to Rescue Squad, but adding too many ring geometries really hit the framerate. Cracking out the profiler shows that the geometry generation (above) was taking most of the time, so I started tinkering.

The two biggest gains were inlining the getU call, and reusing the sin and cos calls from the previous iteration instead of recalculating them. Then a whole bunch of lesser optimisations like extracting out duplicate calculations and moving as much work out of the loop as possible gave small but solid improvements.

Overall on my test scene of 500 rings I've managed to go from 6fps to a nice 32fps. [grin] Thats still a little slow for my liking, but thats about all the optimisations I can do here I think. The last one is making the subdivision amount dynamic (either automatic based on size, or manually set at creation time). However I'll leave this for later as I can't figure out which way would be best right now.

Suggestions for improvements are welcome, but I think I've done all that is possible without actually changing the external behaviour of the code.
Sign in to follow this  


0 Comments


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now