Revise DrawAtlasBatch to get rid of one copy when generating the batch.

This doesn't appear to have much affect on perf in SampleApp, but
it should avoid some copies and replaces a couple of STArrays
with one that just does a memcpy in the copy constructor.

Review URL: https://codereview.chromium.org/1279343004
diff --git a/src/gpu/GrDrawContext.cpp b/src/gpu/GrDrawContext.cpp
index c5cb855..c471619 100644
--- a/src/gpu/GrDrawContext.cpp
+++ b/src/gpu/GrDrawContext.cpp
@@ -21,9 +21,6 @@
 #include "batches/GrDrawVerticesBatch.h"
 #include "batches/GrRectBatchFactory.h"
 
-#include "SkGr.h"
-#include "SkRSXform.h"
-
 #define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == fContext)
 #define RETURN_IF_ABANDONED        if (!fDrawTarget) { return; }
 #define RETURN_FALSE_IF_ABANDONED  if (!fDrawTarget) { return false; }
@@ -420,63 +417,10 @@
     
     GrPipelineBuilder pipelineBuilder(paint, rt, clip);
     
-    // now build the renderable geometry
-    const int vertCount = spriteCount * 4;
-    SkAutoTMalloc<SkPoint> vertStorage(vertCount * 2);
-    SkPoint* verts = vertStorage.get();
-    SkPoint* texs = verts + vertCount;
-    
-    for (int i = 0; i < spriteCount; ++i) {
-        xform[i].toQuad(texRect[i].width(), texRect[i].height(), verts);
-        texRect[i].toQuad(texs);
-        verts += 4;
-        texs += 4;
-    }
-    
-    // TODO clients should give us bounds
-    SkRect bounds;
-    if (!bounds.setBoundsCheck(vertStorage.get(), vertCount)) {
-        SkDebugf("drawAtlas call empty bounds\n");
-        return;
-    }
-    
-    viewMatrix.mapRect(&bounds);
-    
-    // If we don't have AA then we outset for a half pixel in each direction to account for
-    // snapping
-    if (!paint.isAntiAlias()) {
-        bounds.outset(0.5f, 0.5f);
-    }
-    
-    SkAutoTMalloc<GrColor> colorStorage;
-    GrColor* vertCols = NULL;
-    if (colors) {
-        colorStorage.reset(vertCount);
-        vertCols = colorStorage.get();
-        
-        int paintAlpha = GrColorUnpackA(paint.getColor());
-
-        // need to convert byte order and from non-PM to PM
-        for (int i = 0; i < spriteCount; ++i) {
-            SkColor color = colors[i];
-            if (paintAlpha != 255) {
-                color = SkColorSetA(color, SkMulDiv255Round(SkColorGetA(color), paintAlpha));
-            }
-            GrColor grColor = SkColor2GrColor(color);
-            
-            vertCols[0] = vertCols[1] = vertCols[2] = vertCols[3] = grColor;
-            vertCols += 4;
-        }
-    }
-    
-    verts = vertStorage.get();
-    texs = verts + vertCount;
-    vertCols = colorStorage.get();
-    
     GrDrawAtlasBatch::Geometry geometry;
     geometry.fColor = paint.getColor();
-    SkAutoTUnref<GrBatch> batch(GrDrawAtlasBatch::Create(geometry, viewMatrix, verts, vertCount,
-                                                         vertCols, texs, bounds));
+    SkAutoTUnref<GrBatch> batch(GrDrawAtlasBatch::Create(geometry, viewMatrix, spriteCount,
+                                                         xform, texRect, colors));
     
     fDrawTarget->drawBatch(pipelineBuilder, batch);
 }
diff --git a/src/gpu/batches/GrDrawAtlasBatch.cpp b/src/gpu/batches/GrDrawAtlasBatch.cpp
index c1f2443..9bc7753 100644
--- a/src/gpu/batches/GrDrawAtlasBatch.cpp
+++ b/src/gpu/batches/GrDrawAtlasBatch.cpp
@@ -7,7 +7,9 @@
 
 #include "GrDrawAtlasBatch.h"
 #include "GrBatchTest.h"
+#include "SkGr.h"
 #include "SkRandom.h"
+#include "SkRSXform.h"
 
 void GrDrawAtlasBatch::initBatchTracker(const GrPipelineOptimizations& opt) {
     // Handle any color overrides
@@ -24,42 +26,27 @@
     fCoverageIgnored = !opt.readsCoverage();
 }
 
-static const GrGeometryProcessor* set_vertex_attributes(bool hasLocalCoords,
-                                                        bool hasColors,
-                                                        int* colorOffset,
-                                                        int* texOffset,
+static const GrGeometryProcessor* set_vertex_attributes(bool hasColors,
                                                         GrColor color,
                                                         const SkMatrix& viewMatrix,
                                                         bool coverageIgnored) {
     using namespace GrDefaultGeoProcFactory;
-    *texOffset = -1;
-    *colorOffset = -1;
     Color gpColor(color);
     if (hasColors) {
         gpColor.fType = Color::kAttribute_Type;
     }
     
     Coverage coverage(coverageIgnored ? Coverage::kNone_Type : Coverage::kSolid_Type);
-    LocalCoords localCoords(hasLocalCoords ? LocalCoords::kHasExplicit_Type :
-                            LocalCoords::kUsePosition_Type);
-    if (hasLocalCoords && hasColors) {
-        *colorOffset = sizeof(SkPoint);
-        *texOffset = sizeof(SkPoint) + sizeof(GrColor);
-    } else if (hasLocalCoords) {
-        *texOffset = sizeof(SkPoint);
-    } else if (hasColors) {
-        *colorOffset = sizeof(SkPoint);
-    }
+    LocalCoords localCoords(LocalCoords::kHasExplicit_Type);
     return GrDefaultGeoProcFactory::Create(gpColor, coverage, localCoords, viewMatrix);
 }
 
 void GrDrawAtlasBatch::generateGeometry(GrBatchTarget* batchTarget) {
-    int colorOffset = -1, texOffset = -1;
     // Setup geometry processor
-    SkAutoTUnref<const GrGeometryProcessor> gp(
-                                set_vertex_attributes(true, this->hasColors(), &colorOffset,
-                                                      &texOffset, this->color(), this->viewMatrix(),
-                                                      this->coverageIgnored()));
+    SkAutoTUnref<const GrGeometryProcessor> gp(set_vertex_attributes(this->hasColors(),
+                                                                     this->color(),
+                                                                     this->viewMatrix(),
+                                                                     this->coverageIgnored()));
     
     batchTarget->initDraw(gp, this->pipeline());
     
@@ -69,53 +56,103 @@
              + (this->hasColors() ? sizeof(GrColor) : 0));
     
     QuadHelper helper;
-    int numQuads = this->vertexCount()/4;
+    int numQuads = this->quadCount();
     void* verts = helper.init(batchTarget, vertexStride, numQuads);
     if (!verts) {
         SkDebugf("Could not allocate vertices\n");
         return;
     }
     
-    int vertexOffset = 0;
+    uint8_t* vertPtr = reinterpret_cast<uint8_t*>(verts);
     for (int i = 0; i < instanceCount; i++) {
         const Geometry& args = fGeoData[i];
         
-        for (int j = 0; j < args.fPositions.count(); ++j) {
-            *((SkPoint*)verts) = args.fPositions[j];
-            if (this->hasColors()) {
-                *(GrColor*)((intptr_t)verts + colorOffset) = args.fColors[j];
-            }
-            *(SkPoint*)((intptr_t)verts + texOffset) = args.fLocalCoords[j];
-            verts = (void*)((intptr_t)verts + vertexStride);
-            vertexOffset++;
-        }
+        size_t allocSize = args.fVerts.count();
+        memcpy(vertPtr, args.fVerts.begin(), allocSize);
+        vertPtr += allocSize;
     }
     helper.issueDraw(batchTarget);
 }
 
 GrDrawAtlasBatch::GrDrawAtlasBatch(const Geometry& geometry, const SkMatrix& viewMatrix,
-                                   const SkPoint* positions, int vertexCount,
-                                   const GrColor* colors, const SkPoint* localCoords,
-                                   const SkRect& bounds) {
+                                   int spriteCount, const SkRSXform* xforms, const SkRect* rects,
+                                   const SkColor* colors) {
     this->initClassID<GrDrawAtlasBatch>();
-    SkASSERT(positions);
-    SkASSERT(localCoords);
+    SkASSERT(xforms);
+    SkASSERT(rects);
     
     fViewMatrix = viewMatrix;
     Geometry& installedGeo = fGeoData.push_back(geometry);
     
-    installedGeo.fPositions.append(vertexCount, positions);
-    
+    // Figure out stride and offsets
+    // Order within the vertex is: position [color] texCoord
+    size_t texOffset = sizeof(SkPoint);
+    size_t vertexStride = 2*sizeof(SkPoint);
+    fHasColors = SkToBool(colors);
     if (colors) {
-        installedGeo.fColors.append(vertexCount, colors);
-        fHasColors = true;
-    } else {
-        fHasColors = false;
+        texOffset += sizeof(GrColor);
+        vertexStride += sizeof(GrColor);
     }
     
-    installedGeo.fLocalCoords.append(vertexCount, localCoords);
-    fVertexCount = vertexCount;
+    // Compute buffer size and alloc buffer
+    fQuadCount = spriteCount;
+    int allocSize = static_cast<int>(4*vertexStride*spriteCount);
+    installedGeo.fVerts.reset(allocSize);
+    uint8_t* currVertex = installedGeo.fVerts.begin();
     
+    SkRect bounds;
+    bounds.setLargestInverted();
+    int paintAlpha = GrColorUnpackA(installedGeo.fColor);
+    for (int spriteIndex = 0; spriteIndex < spriteCount; ++spriteIndex) {
+        // Transform rect
+        SkPoint quad[4];
+        const SkRect& currRect = rects[spriteIndex];
+        xforms[spriteIndex].toQuad(currRect.width(), currRect.height(), quad);
+ 
+        // Copy colors if necessary
+        if (colors) {
+            // convert to GrColor
+            SkColor color = colors[spriteIndex];
+            if (paintAlpha != 255) {
+                color = SkColorSetA(color, SkMulDiv255Round(SkColorGetA(color), paintAlpha));
+            }
+            GrColor grColor = SkColor2GrColor(color);
+            
+            *(reinterpret_cast<GrColor*>(currVertex+sizeof(SkPoint))) = grColor;
+            *(reinterpret_cast<GrColor*>(currVertex+vertexStride+sizeof(SkPoint))) = grColor;
+            *(reinterpret_cast<GrColor*>(currVertex+2*vertexStride+sizeof(SkPoint))) = grColor;
+            *(reinterpret_cast<GrColor*>(currVertex+3*vertexStride+sizeof(SkPoint))) = grColor;
+        }
+        
+        // Copy position and uv to verts
+        *(reinterpret_cast<SkPoint*>(currVertex)) = quad[0];
+        *(reinterpret_cast<SkPoint*>(currVertex+texOffset)) = SkPoint::Make(currRect.fLeft,
+                                                                            currRect.fTop);
+        bounds.growToInclude(quad[0].fX, quad[0].fY);
+        currVertex += vertexStride;
+
+        *(reinterpret_cast<SkPoint*>(currVertex)) = quad[1];
+        *(reinterpret_cast<SkPoint*>(currVertex+texOffset)) = SkPoint::Make(currRect.fRight,
+                                                                            currRect.fTop);
+        bounds.growToInclude(quad[1].fX, quad[1].fY);
+        currVertex += vertexStride;
+        
+        *(reinterpret_cast<SkPoint*>(currVertex)) = quad[2];
+        *(reinterpret_cast<SkPoint*>(currVertex+texOffset)) = SkPoint::Make(currRect.fRight,
+                                                                            currRect.fBottom);
+        bounds.growToInclude(quad[2].fX, quad[2].fY);
+        currVertex += vertexStride;
+        
+        *(reinterpret_cast<SkPoint*>(currVertex)) = quad[3];
+        *(reinterpret_cast<SkPoint*>(currVertex+texOffset)) = SkPoint::Make(currRect.fLeft,
+                                                                            currRect.fBottom);
+        bounds.growToInclude(quad[3].fX, quad[3].fY);
+        currVertex += vertexStride;
+    }
+    
+    viewMatrix.mapRect(&bounds);
+    // Outset for a half pixel in each direction to account for snapping in non-AA case
+    bounds.outset(0.5f, 0.5f);
     this->setBounds(bounds);
 }
  
@@ -143,7 +180,7 @@
         fColor = GrColor_ILLEGAL;
     }
     fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
-    fVertexCount += that->vertexCount();
+    fQuadCount += that->quadCount();
     
     this->joinBounds(that->bounds());
     return true;
@@ -151,62 +188,69 @@
 
 #ifdef GR_TEST_UTILS
 
-static SkPoint random_point(SkRandom* random, SkScalar min, SkScalar max) {
-    SkPoint p;
-    p.fX = random->nextRangeScalar(min, max);
-    p.fY = random->nextRangeScalar(min, max);
-    return p;
+static SkRSXform random_xform(SkRandom* random) {
+    static const SkScalar kMinExtent = -100.f;
+    static const SkScalar kMaxExtent = 100.f;
+    static const SkScalar kMinScale = 0.1f;
+    static const SkScalar kMaxScale = 100.f;
+    static const SkScalar kMinRotate = -SK_ScalarPI;
+    static const SkScalar kMaxRotate = SK_ScalarPI;
+
+    SkRSXform xform = SkRSXform::MakeFromRadians(random->nextRangeScalar(kMinScale, kMaxScale),
+                                                 random->nextRangeScalar(kMinRotate, kMaxRotate),
+                                                 random->nextRangeScalar(kMinExtent, kMaxExtent),
+                                                 random->nextRangeScalar(kMinExtent, kMaxExtent),
+                                                 random->nextRangeScalar(kMinExtent, kMaxExtent),
+                                                 random->nextRangeScalar(kMinExtent, kMaxExtent));
+    return xform;
 }
 
-static void randomize_params(size_t count, SkScalar min, SkScalar max,
-                             SkRandom* random,
-                             SkTArray<SkPoint>* positions,
-                             SkTArray<SkPoint>* texCoords,
+static SkRect random_texRect(SkRandom* random) {
+    static const SkScalar kMinCoord = 0.0f;
+    static const SkScalar kMaxCoord = 1024.f;
+    
+    SkRect texRect = SkRect::MakeLTRB(random->nextRangeScalar(kMinCoord, kMaxCoord),
+                                      random->nextRangeScalar(kMinCoord, kMaxCoord),
+                                      random->nextRangeScalar(kMinCoord, kMaxCoord),
+                                      random->nextRangeScalar(kMinCoord, kMaxCoord));
+    texRect.sort();
+    return texRect;
+}
+
+static void randomize_params(uint32_t count, SkRandom* random,
+                             SkTArray<SkRSXform>* xforms,
+                             SkTArray<SkRect>* texRects,
                              SkTArray<GrColor>* colors, bool hasColors) {
     for (uint32_t v = 0; v < count; v++) {
-        positions->push_back(random_point(random, min, max));
-        texCoords->push_back(random_point(random, min, max));
+        xforms->push_back(random_xform(random));
+        texRects->push_back(random_texRect(random));
         if (hasColors) {
             colors->push_back(GrRandomColor(random));
         }
     }
 }
 
-
 BATCH_TEST_DEFINE(GrDrawAtlasBatch) {
     uint32_t spriteCount = random->nextRangeU(1, 100);
     
-    // TODO make 'sensible' indexbuffers
-    SkTArray<SkPoint> positions;
-    SkTArray<SkPoint> texCoords;
-    SkTArray<GrColor> colors;
+    SkTArray<SkRSXform> xforms(spriteCount);
+    SkTArray<SkRect> texRects(spriteCount);
+    SkTArray<GrColor> colors(spriteCount);
     
     bool hasColors = random->nextBool();
     
-    uint32_t vertexCount = 4*spriteCount;
-    
-    static const SkScalar kMinVertExtent = -100.f;
-    static const SkScalar kMaxVertExtent = 100.f;
-    randomize_params(vertexCount, kMinVertExtent, kMaxVertExtent,
+    randomize_params(spriteCount,
                      random,
-                     &positions,
-                     &texCoords,
+                     &xforms,
+                     &texRects,
                      &colors, hasColors);
     
     SkMatrix viewMatrix = GrTest::TestMatrix(random);
-    SkRect bounds;
-    SkDEBUGCODE(bool result = ) bounds.setBoundsCheck(positions.begin(), vertexCount);
-    SkASSERT(result);
-    
-    viewMatrix.mapRect(&bounds);
     
     GrDrawAtlasBatch::Geometry geometry;
     geometry.fColor = GrRandomColor(random);
-    return GrDrawAtlasBatch::Create(geometry, viewMatrix,
-                                    positions.begin(), vertexCount,
-                                    colors.begin(),
-                                    texCoords.begin(),
-                                    bounds);
+    return GrDrawAtlasBatch::Create(geometry, viewMatrix, spriteCount, xforms.begin(),
+                                    texRects.begin(), colors.begin());
 }
 
 #endif
diff --git a/src/gpu/batches/GrDrawAtlasBatch.h b/src/gpu/batches/GrDrawAtlasBatch.h
index 6db0c94..bc882b2 100644
--- a/src/gpu/batches/GrDrawAtlasBatch.h
+++ b/src/gpu/batches/GrDrawAtlasBatch.h
@@ -15,18 +15,14 @@
 class GrDrawAtlasBatch : public GrBatch {
 public:
     struct Geometry {
-        GrColor fColor;
-        SkTDArray<SkPoint> fPositions;
-        SkTDArray<GrColor> fColors;
-        SkTDArray<SkPoint> fLocalCoords;
+        GrColor                 fColor;
+        SkTArray<uint8_t, true> fVerts;
     };
     
-    static GrBatch* Create(const Geometry& geometry, const SkMatrix& viewMatrix,
-                           const SkPoint* positions, int vertexCount,
-                           const GrColor* colors, const SkPoint* localCoords,
-                           const SkRect& bounds) {
-        return SkNEW_ARGS(GrDrawAtlasBatch, (geometry, viewMatrix, positions,
-                                           vertexCount, colors, localCoords, bounds));
+    static GrBatch* Create(const Geometry& geometry, const SkMatrix& viewMatrix, int spriteCount,
+                           const SkRSXform* xforms, const SkRect* rects, const SkColor* colors) {
+        return SkNEW_ARGS(GrDrawAtlasBatch, (geometry, viewMatrix, spriteCount,
+                                             xforms, rects, colors));
     }
     
     const char* name() const override { return "DrawAtlasBatch"; }
@@ -50,15 +46,14 @@
     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
     
 private:
-    GrDrawAtlasBatch(const Geometry& geometry, const SkMatrix& viewMatrix,
-                     const SkPoint* positions, int vertexCount,
-                     const GrColor* colors, const SkPoint* localCoords, const SkRect& bounds);
+    GrDrawAtlasBatch(const Geometry& geometry, const SkMatrix& viewMatrix, int spriteCount,
+                     const SkRSXform* xforms, const SkRect* rects, const SkColor* colors);
     
     GrColor color() const { return fColor; }
     bool colorIgnored() const { return fColorIgnored; }
     const SkMatrix& viewMatrix() const { return fViewMatrix; }
     bool hasColors() const { return fHasColors; }
-    int vertexCount() const { return fVertexCount; }
+    int quadCount() const { return fQuadCount; }
     bool coverageIgnored() const { return fCoverageIgnored; }
     
     bool onCombineIfPossible(GrBatch* t) override;
@@ -66,7 +61,7 @@
     
     SkMatrix fViewMatrix;
     GrColor  fColor;
-    int      fVertexCount;
+    int      fQuadCount;
     bool     fColorIgnored;
     bool     fCoverageIgnored;
     bool     fHasColors;