[libgdx] Many micro optimizations, shaved off 40% of software clipping runtime.

This commit is contained in:
badlogic 2017-03-27 20:39:20 +02:00
parent 019a8e6587
commit 8866925570
4 changed files with 135 additions and 100 deletions

View File

@ -116,7 +116,8 @@ public class ClippingTest extends ApplicationAdapter {
}
public void render () {
state.update(Gdx.graphics.getDeltaTime() * 0.3f);
// state.update(Gdx.graphics.getDeltaTime() * 0.3f);
state.update(0);
Gdx.gl.glClearColor(0.3f, 0.3f, 0.3f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

View File

@ -174,12 +174,6 @@ public class SoftwareClippingTest extends ApplicationAdapter {
}
private void clip () {
boolean clipped = clipper.clip(triangle, 0, triangle.length, 5, clippingPolygon, clippedPolygon);
System.out.println("Clipped: " + clipped);
if (clipped) {
clippedPolygonVertices.clear();
clippedPolygonIndices.clear();
float x1 = triangle[0];
float y1 = triangle[1];
float x2 = triangle[5];
@ -187,6 +181,17 @@ public class SoftwareClippingTest extends ApplicationAdapter {
float x3 = triangle[10];
float y3 = triangle[11];
// must duplicate first vertex at end of polygon
// so we can avoid module/branch in clipping code
clippingPolygon.add(clippingPolygon.get(0));
clippingPolygon.add(clippingPolygon.get(1));
boolean clipped = clipper.clip(x1, y1, x2, y2, x3, y3, clippingPolygon, clippedPolygon);
System.out.println("Clipped: " + clipped);
if (clipped) {
clippedPolygonVertices.clear();
clippedPolygonIndices.clear();
float d0 = y2 - y3;
float d1 = x3 - x2;
float d2 = x1 - x3;
@ -222,6 +227,8 @@ public class SoftwareClippingTest extends ApplicationAdapter {
} else {
clippedPolygon.clear();
}
clippingPolygon.setSize(clippingPolygon.size - 2);
}
public static void main (String[] args) {

View File

@ -62,7 +62,7 @@ public class SkeletonRenderer {
private ClippingAttachment clipAttachment;
private Slot clipEnd;
private FloatArray clippingArea = new FloatArray();
private float[] clipInput = new float[6];
private boolean clippingAreaClockwise;
private FloatArray clipOutput = new FloatArray(400);
private FloatArray clippedVertices = new FloatArray(400);
private ShortArray clippedTriangles = new ShortArray(400);
@ -100,9 +100,9 @@ public class SkeletonRenderer {
} else if (attachment instanceof ClippingAttachment) {
ClippingAttachment clip = (ClippingAttachment) attachment;
batch.end();
if (!softwareClipping) batch.end();
clipStart(batch.getProjectionMatrix(), batch.getTransformMatrix(), slot, clip);
batch.begin();
if (!softwareClipping) batch.begin();
continue;
} else if (attachment instanceof MeshAttachment) {
@ -179,9 +179,9 @@ public class SkeletonRenderer {
} else if (attachment instanceof ClippingAttachment) {
ClippingAttachment clip = (ClippingAttachment) attachment;
batch.end();
if (!softwareClipping) batch.end();
clipStart(batch.getProjectionMatrix(), batch.getTransformMatrix(), slot, clip);
batch.begin();
if (!softwareClipping) batch.begin();
continue;
} else if (attachment instanceof SkeletonAttachment) {
@ -284,9 +284,9 @@ public class SkeletonRenderer {
} else if (attachment instanceof ClippingAttachment) {
ClippingAttachment clip = (ClippingAttachment) attachment;
batch.end();
if (!softwareClipping) batch.end();
clipStart(batch.getProjectionMatrix(), batch.getTransformMatrix(), slot, clip);
batch.begin();
if (!softwareClipping) batch.begin();
continue;
} else if (attachment instanceof SkeletonAttachment) {
@ -393,6 +393,9 @@ public class SkeletonRenderer {
int n = clip.getWorldVerticesLength();
float[] vertices = this.clippingArea.setSize(n);
clip.computeWorldVertices(slot, 0, n, vertices, 0, 2);
clippingAreaClockwise = SutherlandHodgmanClipper.clockwise(this.clippingArea);
clippingArea.add(clippingArea.items[0]);
clippingArea.add(clippingArea.items[1]);
}
}
@ -402,7 +405,7 @@ public class SkeletonRenderer {
if (!softwareClipping) Gdx.gl.glDisable(GL20.GL_STENCIL_TEST);
}
private void clipSoftware(float[] vertices, int offset, int verticesLength, short[] triangles, int triangleOffset, int trianglesLength, FloatArray clippedVertices, ShortArray clippedTriangles, float dark, float light, boolean twoColor) {
private void clipSoftware(final float[] vertices, final int offset, final int verticesLength, final short[] triangles, final int triangleOffset, final int trianglesLength, final FloatArray clippedVertices, final ShortArray clippedTriangles, final float dark, final float light, final boolean twoColor) {
int idx = 0;
clippedVertices.clear();
clippedTriangles.clear();
@ -410,32 +413,25 @@ public class SkeletonRenderer {
final int uvOffset = twoColor ? 4 : 3;
for (int i = 0; i < trianglesLength; i += 3) {
int triOffset = triangles[i] * vertexSize;
clipInput[0] = vertices[triOffset];
clipInput[1] = vertices[triOffset + 1];
float x1 = vertices[triOffset];
float y1= vertices[triOffset + 1];
float u1 = vertices[triOffset + uvOffset];
float v1 = vertices[triOffset + uvOffset + 1];
triOffset = triangles[i + 1] * vertexSize;
clipInput[2] = vertices[triOffset];
clipInput[3] = vertices[triOffset + 1];
float x2 = vertices[triOffset];
float y2 = vertices[triOffset + 1];
float u2 = vertices[triOffset + uvOffset];
float v2 = vertices[triOffset + uvOffset + 1];
triOffset = triangles[i + 2] * vertexSize;
clipInput[4] = vertices[triOffset];
clipInput[5] = vertices[triOffset + 1];
float x3 = vertices[triOffset];
float y3 = vertices[triOffset + 1];
float u3 = vertices[triOffset + uvOffset];
float v3 = vertices[triOffset + uvOffset + 1];
boolean clipped = clipper.clip(clipInput, 0, 6, 2, clippingArea, clipOutput);
boolean clipped = clipper.clip(x1, y1, x2, y2, x3, y3, clippingArea, clipOutput, clippingAreaClockwise);
if (clipped) {
float x1 = clipInput[0];
float y1 = clipInput[1];
float x2 = clipInput[2];
float y2 = clipInput[3];
float x3 = clipInput[4];
float y3 = clipInput[5];
float d0 = y2 - y3;
float d1 = x3 - x2;
float d2 = x1 - x3;
@ -450,8 +446,10 @@ public class SkeletonRenderer {
float x = clipVertices[j];
float y = clipVertices[j + 1];
float a = (d0 * (x - x3) + d1 * (y - y3)) * denom;
float b = (d4 * (x - x3) + d2 * (y - y3)) * denom;
float c0 = x - x3;
float c1 = y - y3;
float a = (d0 * c0 + d1 * c1) * denom;
float b = (d4 * c0 + d2 * c1) * denom;
float c = 1.0f - a - b;
float u = u1 * a + u2 * b + u3 * c;
@ -472,22 +470,22 @@ public class SkeletonRenderer {
idx += clipOutput.size >> 1;
} else {
clippedVertices.add(clipInput[0]);
clippedVertices.add(clipInput[1]);
clippedVertices.add(x1);
clippedVertices.add(y1);
clippedVertices.add(light);
if (twoColor) clippedVertices.add(dark);
clippedVertices.add(u1);
clippedVertices.add(v1);
clippedVertices.add(clipInput[2]);
clippedVertices.add(clipInput[3]);
clippedVertices.add(x2);
clippedVertices.add(y2);
clippedVertices.add(light);
if (twoColor) clippedVertices.add(dark);
clippedVertices.add(u2);
clippedVertices.add(v2);
clippedVertices.add(clipInput[4]);
clippedVertices.add(clipInput[5]);
clippedVertices.add(x3);
clippedVertices.add(y3);
clippedVertices.add(light);
if (twoColor) clippedVertices.add(dark);
clippedVertices.add(u3);

View File

@ -1,7 +1,6 @@
package com.esotericsoftware.spine.utils;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.FloatArray;
@ -9,29 +8,50 @@ public class SutherlandHodgmanClipper {
Vector2 tmp = new Vector2();
final FloatArray scratch = new FloatArray();
/**
* Clips the input triangle against the convex clipping area. If the triangle lies entirely
* within the clipping area, false is returned.
*/
public boolean clip (float[] triangle, int offset, int length, int stride, FloatArray clippingArea, FloatArray output) {
public boolean clip(float x1, float y1, float x2, float y2, float x3, float y3, FloatArray clippingArea,
FloatArray output) {
boolean isClockwise = clockwise(clippingArea);
return clip(x1, y1, x2, y2, x3, y3, clippingArea, output, isClockwise);
}
FloatArray originalOutput = output;
/**
* Clips the input triangle against the convex clipping area. If the
* triangle lies entirely within the clipping area, false is returned. The
* clipping area must duplicate the first vertex at the end of the vertices
* list!
*/
public boolean clip(float x1, float y1, float x2, float y2, float x3, float y3, FloatArray clippingArea,
FloatArray output, boolean isClockwise) {
final FloatArray originalOutput = output;
boolean clipped = false;
FloatArray input = scratch;
input.clear();
for (int i = offset; i < offset + length; i+= stride) {
input.add(triangle[i]);
input.add(triangle[i + 1]);
FloatArray input = null;
// avoid copy at the end
if ((clippingArea.size / 2) % 2 != 0) {
input = output;
output = scratch;
} else {
input = scratch;
}
input.clear();
input.add(x1);
input.add(y1);
input.add(x2);
input.add(y2);
input.add(x3);
input.add(y3);
input.add(x1);
input.add(y1);
output.clear();
for (int i = 0; i < clippingArea.size; i += 2) {
float edgeX = clippingArea.items[i];
float edgeY = clippingArea.items[i + 1];
float edgeX2 = clippingArea.items[(i + 2) % clippingArea.size];
float edgeY2 = clippingArea.items[(i + 3) % clippingArea.size];
final float[] clippingVertices = clippingArea.items;
final int clippingVerticesLength = clippingArea.size - 2;
for (int i = 0; i < clippingVerticesLength; i += 2) {
float edgeX = clippingVertices[i];
float edgeY = clippingVertices[i + 1];
float edgeX2 = clippingVertices[i + 2];
float edgeY2 = clippingVertices[i + 3];
if (!isClockwise) {
float tmp = edgeX;
@ -43,14 +63,20 @@ public class SutherlandHodgmanClipper {
edgeY2 = tmp;
}
for (int j = 0; j < input.size; j += 2) {
float inputX = input.items[j % input.size];
float inputY = input.items[(j + 1) % input.size];
float inputX2 = input.items[(j + 2) % input.size];
float inputY2 = input.items[(j + 3) % input.size];
final float deltaX = edgeX - edgeX2;
final float deltaY = edgeY - edgeY2;
int side = pointLineSide(edgeX2, edgeY2, edgeX, edgeY, inputX, inputY);
int side2 = pointLineSide(edgeX2, edgeY2, edgeX, edgeY, inputX2, inputY2);
final float[] inputVertices = input.items;
final int inputVerticesLength = input.size - 2;
for (int j = 0; j < inputVerticesLength; j += 2) {
final float inputX = inputVertices[j];
final float inputY = inputVertices[j + 1];
final float inputX2 = inputVertices[j + 2];
final float inputY2 = inputVertices[j + 3];
final int side = pointLineSide(deltaX, deltaY, edgeX2, edgeY2, inputX, inputY);
final int side2 = pointLineSide(deltaX, deltaY, edgeX2, edgeY2, inputX2, inputY2);
// v1 inside, v2 inside
if (side >= 0 && side2 >= 0) {
@ -59,9 +85,7 @@ public class SutherlandHodgmanClipper {
}
// v1 inside, v2 outside
else if (side >= 0 && side2 < 0) {
if (!Intersector.intersectLines(edgeX, edgeY, edgeX2, edgeY2, inputX, inputY, inputX2, inputY2, tmp)) {
throw new RuntimeException("Lines should intersect, but didn't");
}
intersectLines(edgeX, edgeY, edgeX2, edgeY2, inputX, inputY, inputX2, inputY2, tmp);
output.add(tmp.x);
output.add(tmp.y);
clipped = true;
@ -73,9 +97,7 @@ public class SutherlandHodgmanClipper {
}
// v1 outside, v2 inside
else if (side < 0 && side2 >= 0) {
if (!Intersector.intersectLines(edgeX, edgeY, edgeX2, edgeY2, inputX, inputY, inputX2, inputY2, tmp)) {
throw new RuntimeException("Lines should intersect, but didn't");
}
intersectLines(edgeX, edgeY, edgeX2, edgeY2, inputX, inputY, inputX2, inputY2, tmp);
output.add(tmp.x);
output.add(tmp.y);
output.add(inputX2);
@ -84,7 +106,10 @@ public class SutherlandHodgmanClipper {
}
}
if (i < clippingArea.size - 2) {
output.add(output.items[0]);
output.add(output.items[1]);
if (i < clippingVerticesLength - 2) {
FloatArray tmp = output;
output = input;
output.clear();
@ -97,37 +122,41 @@ public class SutherlandHodgmanClipper {
originalOutput.addAll(output.items, 0, output.size);
}
originalOutput.setSize(originalOutput.size - 2);
return clipped;
}
private int pointLineSide(float lineX, float lineY, float lineX2, float lineY2, float pointX, float pointY) {
return (int)Math.signum((lineX2 - lineX) * (pointY - lineY) - (lineY2 - lineY) * (pointX - lineX));
private int pointLineSide(float deltaX, float deltaY, float lineX, float lineY, float pointX, float pointY) {
return (int) Math.signum(deltaX * (pointY - lineY) - deltaY * (pointX - lineX));
}
public static boolean intersectLines (float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4,
public static void intersectLines(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4,
Vector2 intersection) {
float d = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
if (d == 0) return false;
float c0 = y4 - y3;
float c1 = x2 - x1;
float c2 = x4 - x3;
float c3 = y2 - y1;
float d = c0 * c1 - c2 * c3;
if (intersection != null) {
float ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / d;
float ua = (c2 * (y1 - y3) - c0 * (x1 - x3)) / d;
intersection.set(x1 + (x2 - x1) * ua, y1 + (y2 - y1) * ua);
}
return true;
}
public static boolean clockwise (FloatArray poly) {
public static boolean clockwise(FloatArray poly) {
return area(poly) < 0;
}
public static float area (FloatArray poly) {
public static float area(FloatArray poly) {
float area = 0;
for (int i = 0; i < poly.size; i += 2) {
float x = poly.items[i];
float y = poly.items[i + 1];
float x2 = poly.items[(i + 2) % poly.size];
float y2 = poly.items[(i + 3) % poly.size];
final float[] polyVertices = poly.items;
final int polySize = poly.size;
for (int i = 0; i < polySize; i += 2) {
float x = polyVertices[i];
float y = polyVertices[i + 1];
float x2 = polyVertices[(i + 2) % poly.size];
float y2 = polyVertices[(i + 3) % poly.size];
area += x * y2 - y * x2;
}