Tessellate on worker threads

Tessellate and cache (where possible) shadow and round rect
tessellation tasks.

Change-Id: I2cfda8e11d83d51ea74af871235cf26e8f831d40
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 71836dd..31f399a 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -287,6 +287,7 @@
     // of the current frame
     if (getTargetFbo() == 0) {
         mCaches.pathCache.trim();
+        mCaches.tessellationCache.trim();
     }
 
     if (!suppressErrorChecks()) {
@@ -2390,7 +2391,7 @@
     return DrawGlInfo::kStatusDrew;
 }
 
-status_t OpenGLRenderer::drawVertexBuffer(VertexBufferMode mode,
+status_t OpenGLRenderer::drawVertexBuffer(float translateX, float translateY,
         const VertexBuffer& vertexBuffer, const SkPaint* paint, bool useOffset) {
     // not missing call to quickReject/dirtyLayer, always done at a higher level
     if (!vertexBuffer.getVertexCount()) {
@@ -2398,6 +2399,9 @@
         return DrawGlInfo::kStatusDone;
     }
 
+    const Rect& bounds = vertexBuffer.getBounds();
+    dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
+
     int color = paint->getColor();
     bool isAA = paint->isAntiAlias();
 
@@ -2409,7 +2413,7 @@
     setupDrawShader(getShader(paint));
     setupDrawBlending(paint, isAA);
     setupDrawProgram();
-    setupDrawModelView(kModelViewMode_Translate, useOffset, 0, 0, 0, 0);
+    setupDrawModelView(kModelViewMode_Translate, useOffset, translateX, translateY, 0, 0);
     setupDrawColorUniforms(getShader(paint));
     setupDrawColorFilterUniforms(getColorFilter(paint));
     setupDrawShaderUniforms(getShader(paint));
@@ -2429,13 +2433,14 @@
         glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords);
     }
 
-    if (mode == kVertexBufferMode_Standard) {
+    const VertexBuffer::Mode mode = vertexBuffer.getMode();
+    if (mode == VertexBuffer::kStandard) {
         mCaches.unbindIndicesBuffer();
         glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount());
-    } else if (mode == kVertexBufferMode_OnePolyRingShadow) {
+    } else if (mode == VertexBuffer::kOnePolyRingShadow) {
         mCaches.bindShadowIndicesBuffer();
         glDrawElements(GL_TRIANGLE_STRIP, ONE_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0);
-    } else if (mode == kVertexBufferMode_TwoPolyRingShadow) {
+    } else if (mode == VertexBuffer::kTwoPolyRingShadow) {
         mCaches.bindShadowIndicesBuffer();
         glDrawElements(GL_TRIANGLE_STRIP, TWO_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0);
     }
@@ -2460,14 +2465,7 @@
     VertexBuffer vertexBuffer;
     // TODO: try clipping large paths to viewport
     PathTessellator::tessellatePath(path, paint, *currentTransform(), vertexBuffer);
-
-    if (hasLayer()) {
-        SkRect bounds = path.getBounds();
-        PathTessellator::expandBoundsForStroke(bounds, paint);
-        dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *currentTransform());
-    }
-
-    return drawVertexBuffer(kVertexBufferMode_Standard, vertexBuffer, paint);
+    return drawVertexBuffer(vertexBuffer, paint);
 }
 
 /**
@@ -2487,18 +2485,15 @@
     count &= ~0x3; // round down to nearest four
 
     VertexBuffer buffer;
-    SkRect bounds;
-    PathTessellator::tessellateLines(points, count, paint, *currentTransform(), bounds, buffer);
+    PathTessellator::tessellateLines(points, count, paint, *currentTransform(), buffer);
+    const Rect& bounds = buffer.getBounds();
 
-    // can't pass paint, since style would be checked for outset. outset done by tessellation.
-    if (quickRejectSetupScissor(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) {
+    if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) {
         return DrawGlInfo::kStatusDone;
     }
 
-    dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *currentTransform());
-
     bool useOffset = !paint->isAntiAlias();
-    return drawVertexBuffer(kVertexBufferMode_Standard, buffer, paint, useOffset);
+    return drawVertexBuffer(buffer, paint, useOffset);
 }
 
 status_t OpenGLRenderer::drawPoints(const float* points, int count, const SkPaint* paint) {
@@ -2507,18 +2502,15 @@
     count &= ~0x1; // round down to nearest two
 
     VertexBuffer buffer;
-    SkRect bounds;
-    PathTessellator::tessellatePoints(points, count, paint, *currentTransform(), bounds, buffer);
+    PathTessellator::tessellatePoints(points, count, paint, *currentTransform(), buffer);
 
-    // can't pass paint, since style would be checked for outset. outset done by tessellation.
-    if (quickRejectSetupScissor(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) {
+    const Rect& bounds = buffer.getBounds();
+    if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) {
         return DrawGlInfo::kStatusDone;
     }
 
-    dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *currentTransform());
-
     bool useOffset = !paint->isAntiAlias();
-    return drawVertexBuffer(kVertexBufferMode_Standard, buffer, paint, useOffset);
+    return drawVertexBuffer(buffer, paint, useOffset);
 }
 
 status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
@@ -2564,16 +2556,9 @@
         return drawShape(left, top, texture, p);
     }
 
-    SkPath path;
-    SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
-    if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
-        float outset = p->getStrokeWidth() / 2;
-        rect.outset(outset, outset);
-        rx += outset;
-        ry += outset;
-    }
-    path.addRoundRect(rect, rx, ry);
-    return drawConvexPath(path, p);
+    const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect(*currentTransform(),
+            right - left, bottom - top, rx, ry, p);
+    return drawVertexBuffer(left, top, *vertexBuffer, p);
 }
 
 status_t OpenGLRenderer::drawCircle(float x, float y, float radius, const SkPaint* p) {
@@ -3192,8 +3177,8 @@
     transformXY.mapPoint(point.x, point.y);
 }
 
-status_t OpenGLRenderer::drawShadow(const mat4& casterTransformXY, const mat4& casterTransformZ,
-        float casterAlpha, bool casterUnclipped, const SkPath* casterPerimeter) {
+status_t OpenGLRenderer::drawShadow(float casterAlpha,
+        const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
     if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
 
     // TODO: use quickRejectWithScissor. For now, always force enable scissor.
@@ -3202,77 +3187,14 @@
     SkPaint paint;
     paint.setAntiAlias(true); // want to use AlphaVertex
 
-    // tessellate caster outline into a 2d polygon
-    Vector<Vertex> casterVertices2d;
-    const float casterRefinementThresholdSquared = 20.0f; // TODO: experiment with this value
-    PathTessellator::approximatePathOutlineVertices(*casterPerimeter,
-            casterRefinementThresholdSquared, casterVertices2d);
-    if (!ShadowTessellator::isClockwisePath(*casterPerimeter)) {
-        ShadowTessellator::reverseVertexArray(casterVertices2d.editArray(),
-                casterVertices2d.size());
-    }
-
-    if (casterVertices2d.size() == 0) {
-        // empty caster polygon computed from path
-        return DrawGlInfo::kStatusDone;
-    }
-
-    // map 2d caster poly into 3d
-    const int casterVertexCount = casterVertices2d.size();
-    Vector3 casterPolygon[casterVertexCount];
-    float minZ = FLT_MAX;
-    float maxZ = -FLT_MAX;
-    for (int i = 0; i < casterVertexCount; i++) {
-        const Vertex& point2d = casterVertices2d[i];
-        casterPolygon[i] = Vector3(point2d.x, point2d.y, 0);
-        mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ);
-        minZ = fmin(minZ, casterPolygon[i].z);
-        maxZ = fmax(maxZ, casterPolygon[i].z);
-    }
-
-    // map the centroid of the caster into 3d
-    Vector2 centroid =  ShadowTessellator::centroid2d(
-            reinterpret_cast<const Vector2*>(casterVertices2d.array()),
-            casterVertexCount);
-    Vector3 centroid3d(centroid.x, centroid.y, 0);
-    mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ);
-
-    // if the caster intersects the z=0 plane, lift it in Z so it doesn't
-    if (minZ < SHADOW_MIN_CASTER_Z) {
-        float casterLift = SHADOW_MIN_CASTER_Z - minZ;
-        for (int i = 0; i < casterVertexCount; i++) {
-            casterPolygon[i].z += casterLift;
-        }
-        centroid3d.z += casterLift;
-    }
-
-    // Check whether we want to draw the shadow at all by checking the caster's
-    // bounds against clip.
-    // We only have ortho projection, so we can just ignore the Z in caster for
-    // simple rejection calculation.
-    Rect localClip = mSnapshot->getLocalClip();
-    Rect casterBounds(casterPerimeter->getBounds());
-    casterTransformXY.mapRect(casterBounds);
-
-    bool isCasterOpaque = (casterAlpha == 1.0f) && casterUnclipped;
-    // draw caster's shadows
-    if (mCaches.propertyAmbientShadowStrength > 0) {
+    if (ambientShadowVertexBuffer && mCaches.propertyAmbientShadowStrength > 0) {
         paint.setARGB(casterAlpha * mCaches.propertyAmbientShadowStrength, 0, 0, 0);
-        VertexBuffer ambientShadowVertexBuffer;
-        VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateAmbientShadow(
-                isCasterOpaque, casterPolygon, casterVertexCount, centroid3d,
-                casterBounds, localClip, maxZ, ambientShadowVertexBuffer);
-        drawVertexBuffer(vertexBufferMode, ambientShadowVertexBuffer, &paint);
+        drawVertexBuffer(*ambientShadowVertexBuffer, &paint);
     }
 
-    if (mCaches.propertySpotShadowStrength > 0) {
+    if (spotShadowVertexBuffer && mCaches.propertySpotShadowStrength > 0) {
         paint.setARGB(casterAlpha * mCaches.propertySpotShadowStrength, 0, 0, 0);
-        VertexBuffer spotShadowVertexBuffer;
-        VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateSpotShadow(
-                isCasterOpaque, casterPolygon, casterVertexCount,
-                *currentTransform(), mLightCenter, mLightRadius, casterBounds, localClip,
-                spotShadowVertexBuffer);
-        drawVertexBuffer(vertexBufferMode, spotShadowVertexBuffer, &paint);
+        drawVertexBuffer(*spotShadowVertexBuffer, &paint);
     }
 
     return DrawGlInfo::kStatusDrew;