Home | History | Annotate | Download | only in gpu
      1 
      2 /*
      3  * Copyright 2011 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 #include "GrAAHairLinePathRenderer.h"
     10 
     11 #include "GrContext.h"
     12 #include "GrDrawState.h"
     13 #include "GrGpu.h"
     14 #include "GrIndexBuffer.h"
     15 #include "GrPathUtils.h"
     16 #include "SkGeometry.h"
     17 #include "SkStroke.h"
     18 #include "SkTemplates.h"
     19 
     20 namespace {
     21 // quadratics are rendered as 5-sided polys in order to bound the
     22 // AA stroke around the center-curve. See comments in push_quad_index_buffer and
     23 // bloat_quad.
     24 static const int kVertsPerQuad = 5;
     25 static const int kIdxsPerQuad = 9;
     26 
     27 static const int kVertsPerLineSeg = 4;
     28 static const int kIdxsPerLineSeg = 6;
     29 
     30 static const int kNumQuadsInIdxBuffer = 256;
     31 static const size_t kQuadIdxSBufize = kIdxsPerQuad *
     32                                       sizeof(uint16_t) *
     33                                       kNumQuadsInIdxBuffer;
     34 
     35 bool push_quad_index_data(GrIndexBuffer* qIdxBuffer) {
     36     uint16_t* data = (uint16_t*) qIdxBuffer->lock();
     37     bool tempData = NULL == data;
     38     if (tempData) {
     39         data = SkNEW_ARRAY(uint16_t, kNumQuadsInIdxBuffer * kIdxsPerQuad);
     40     }
     41     for (int i = 0; i < kNumQuadsInIdxBuffer; ++i) {
     42 
     43         // Each quadratic is rendered as a five sided polygon. This poly bounds
     44         // the quadratic's bounding triangle but has been expanded so that the
     45         // 1-pixel wide area around the curve is inside the poly.
     46         // If a,b,c are the original control points then the poly a0,b0,c0,c1,a1
     47         // that is rendered would look like this:
     48         //              b0
     49         //              b
     50         //
     51         //     a0              c0
     52         //      a            c
     53         //       a1       c1
     54         // Each is drawn as three triangles specified by these 9 indices:
     55         int baseIdx = i * kIdxsPerQuad;
     56         uint16_t baseVert = (uint16_t)(i * kVertsPerQuad);
     57         data[0 + baseIdx] = baseVert + 0; // a0
     58         data[1 + baseIdx] = baseVert + 1; // a1
     59         data[2 + baseIdx] = baseVert + 2; // b0
     60         data[3 + baseIdx] = baseVert + 2; // b0
     61         data[4 + baseIdx] = baseVert + 4; // c1
     62         data[5 + baseIdx] = baseVert + 3; // c0
     63         data[6 + baseIdx] = baseVert + 1; // a1
     64         data[7 + baseIdx] = baseVert + 4; // c1
     65         data[8 + baseIdx] = baseVert + 2; // b0
     66     }
     67     if (tempData) {
     68         bool ret = qIdxBuffer->updateData(data, kQuadIdxSBufize);
     69         delete[] data;
     70         return ret;
     71     } else {
     72         qIdxBuffer->unlock();
     73         return true;
     74     }
     75 }
     76 }
     77 
     78 GrPathRenderer* GrAAHairLinePathRenderer::Create(GrContext* context) {
     79     const GrIndexBuffer* lIdxBuffer = context->getQuadIndexBuffer();
     80     if (NULL == lIdxBuffer) {
     81         return NULL;
     82     }
     83     GrGpu* gpu = context->getGpu();
     84     GrIndexBuffer* qIdxBuf = gpu->createIndexBuffer(kQuadIdxSBufize, false);
     85     SkAutoTUnref<GrIndexBuffer> qIdxBuffer(qIdxBuf);
     86     if (NULL == qIdxBuf ||
     87         !push_quad_index_data(qIdxBuf)) {
     88         return NULL;
     89     }
     90     return SkNEW_ARGS(GrAAHairLinePathRenderer,
     91                       (context, lIdxBuffer, qIdxBuf));
     92 }
     93 
     94 GrAAHairLinePathRenderer::GrAAHairLinePathRenderer(
     95                                         const GrContext* context,
     96                                         const GrIndexBuffer* linesIndexBuffer,
     97                                         const GrIndexBuffer* quadsIndexBuffer) {
     98     fLinesIndexBuffer = linesIndexBuffer;
     99     linesIndexBuffer->ref();
    100     fQuadsIndexBuffer = quadsIndexBuffer;
    101     quadsIndexBuffer->ref();
    102 }
    103 
    104 GrAAHairLinePathRenderer::~GrAAHairLinePathRenderer() {
    105     fLinesIndexBuffer->unref();
    106     fQuadsIndexBuffer->unref();
    107 }
    108 
    109 namespace {
    110 
    111 typedef SkTArray<SkPoint, true> PtArray;
    112 #define PREALLOC_PTARRAY(N) SkSTArray<(N),SkPoint, true>
    113 typedef SkTArray<int, true> IntArray;
    114 
    115 // Takes 178th time of logf on Z600 / VC2010
    116 int get_float_exp(float x) {
    117     GR_STATIC_ASSERT(sizeof(int) == sizeof(float));
    118 #if GR_DEBUG
    119     static bool tested;
    120     if (!tested) {
    121         tested = true;
    122         GrAssert(get_float_exp(0.25f) == -2);
    123         GrAssert(get_float_exp(0.3f) == -2);
    124         GrAssert(get_float_exp(0.5f) == -1);
    125         GrAssert(get_float_exp(1.f) == 0);
    126         GrAssert(get_float_exp(2.f) == 1);
    127         GrAssert(get_float_exp(2.5f) == 1);
    128         GrAssert(get_float_exp(8.f) == 3);
    129         GrAssert(get_float_exp(100.f) == 6);
    130         GrAssert(get_float_exp(1000.f) == 9);
    131         GrAssert(get_float_exp(1024.f) == 10);
    132         GrAssert(get_float_exp(3000000.f) == 21);
    133     }
    134 #endif
    135     const int* iptr = (const int*)&x;
    136     return (((*iptr) & 0x7f800000) >> 23) - 127;
    137 }
    138 
    139 // we subdivide the quads to avoid huge overfill
    140 // if it returns -1 then should be drawn as lines
    141 int num_quad_subdivs(const SkPoint p[3]) {
    142     static const SkScalar gDegenerateToLineTol = SK_Scalar1;
    143     static const SkScalar gDegenerateToLineTolSqd =
    144         SkScalarMul(gDegenerateToLineTol, gDegenerateToLineTol);
    145 
    146     if (p[0].distanceToSqd(p[1]) < gDegenerateToLineTolSqd ||
    147         p[1].distanceToSqd(p[2]) < gDegenerateToLineTolSqd) {
    148         return -1;
    149     }
    150 
    151     SkScalar dsqd = p[1].distanceToLineBetweenSqd(p[0], p[2]);
    152     if (dsqd < gDegenerateToLineTolSqd) {
    153         return -1;
    154     }
    155 
    156     if (p[2].distanceToLineBetweenSqd(p[1], p[0]) < gDegenerateToLineTolSqd) {
    157         return -1;
    158     }
    159 
    160     static const int kMaxSub = 4;
    161     // tolerance of triangle height in pixels
    162     // tuned on windows  Quadro FX 380 / Z600
    163     // trade off of fill vs cpu time on verts
    164     // maybe different when do this using gpu (geo or tess shaders)
    165     static const SkScalar gSubdivTol = 175 * SK_Scalar1;
    166 
    167     if (dsqd <= SkScalarMul(gSubdivTol, gSubdivTol)) {
    168         return 0;
    169     } else {
    170         // subdividing the quad reduces d by 4. so we want x = log4(d/tol)
    171         // = log4(d*d/tol*tol)/2
    172         // = log2(d*d/tol*tol)
    173 
    174 #ifdef SK_SCALAR_IS_FLOAT
    175         // +1 since we're ignoring the mantissa contribution.
    176         int log = get_float_exp(dsqd/(gSubdivTol*gSubdivTol)) + 1;
    177         log = GrMin(GrMax(0, log), kMaxSub);
    178         return log;
    179 #else
    180         SkScalar log = SkScalarLog(
    181                           SkScalarDiv(dsqd,
    182                                       SkScalarMul(gSubdivTol, gSubdivTol)));
    183         static const SkScalar conv = SkScalarInvert(SkScalarLog(2));
    184         log = SkScalarMul(log, conv);
    185         return  GrMin(GrMax(0, SkScalarCeilToInt(log)),kMaxSub);
    186 #endif
    187     }
    188 }
    189 
    190 /**
    191  * Generates the lines and quads to be rendered. Lines are always recorded in
    192  * device space. We will do a device space bloat to account for the 1pixel
    193  * thickness.
    194  * Quads are recorded in device space unless m contains
    195  * perspective, then in they are in src space. We do this because we will
    196  * subdivide large quads to reduce over-fill. This subdivision has to be
    197  * performed before applying the perspective matrix.
    198  */
    199 int generate_lines_and_quads(const SkPath& path,
    200                              const SkMatrix& m,
    201                              const GrIRect& devClipBounds,
    202                              PtArray* lines,
    203                              PtArray* quads,
    204                              IntArray* quadSubdivCnts) {
    205     SkPath::Iter iter(path, false);
    206 
    207     int totalQuadCount = 0;
    208     GrRect bounds;
    209     GrIRect ibounds;
    210 
    211     bool persp = m.hasPerspective();
    212 
    213     for (;;) {
    214         GrPoint pts[4];
    215         GrPoint devPts[4];
    216         GrPathCmd cmd = (GrPathCmd)iter.next(pts);
    217         switch (cmd) {
    218             case kMove_PathCmd:
    219                 break;
    220             case kLine_PathCmd:
    221                 m.mapPoints(devPts, pts, 2);
    222                 bounds.setBounds(devPts, 2);
    223                 bounds.outset(SK_Scalar1, SK_Scalar1);
    224                 bounds.roundOut(&ibounds);
    225                 if (SkIRect::Intersects(devClipBounds, ibounds)) {
    226                     SkPoint* pts = lines->push_back_n(2);
    227                     pts[0] = devPts[0];
    228                     pts[1] = devPts[1];
    229                 }
    230                 break;
    231             case kQuadratic_PathCmd:
    232                 m.mapPoints(devPts, pts, 3);
    233                 bounds.setBounds(devPts, 3);
    234                 bounds.outset(SK_Scalar1, SK_Scalar1);
    235                 bounds.roundOut(&ibounds);
    236                 if (SkIRect::Intersects(devClipBounds, ibounds)) {
    237                     int subdiv = num_quad_subdivs(devPts);
    238                     GrAssert(subdiv >= -1);
    239                     if (-1 == subdiv) {
    240                         SkPoint* pts = lines->push_back_n(4);
    241                         pts[0] = devPts[0];
    242                         pts[1] = devPts[1];
    243                         pts[2] = devPts[1];
    244                         pts[3] = devPts[2];
    245                     } else {
    246                         // when in perspective keep quads in src space
    247                         SkPoint* qPts = persp ? pts : devPts;
    248                         SkPoint* pts = quads->push_back_n(3);
    249                         pts[0] = qPts[0];
    250                         pts[1] = qPts[1];
    251                         pts[2] = qPts[2];
    252                         quadSubdivCnts->push_back() = subdiv;
    253                         totalQuadCount += 1 << subdiv;
    254                     }
    255                 }
    256                 break;
    257             case kCubic_PathCmd:
    258                 m.mapPoints(devPts, pts, 4);
    259                 bounds.setBounds(devPts, 4);
    260                 bounds.outset(SK_Scalar1, SK_Scalar1);
    261                 bounds.roundOut(&ibounds);
    262                 if (SkIRect::Intersects(devClipBounds, ibounds)) {
    263                     PREALLOC_PTARRAY(32) q;
    264                     // we don't need a direction if we aren't constraining the subdivision
    265                     static const SkPath::Direction kDummyDir = SkPath::kCCW_Direction;
    266                     // We convert cubics to quadratics (for now).
    267                     // In perspective have to do conversion in src space.
    268                     if (persp) {
    269                         SkScalar tolScale =
    270                             GrPathUtils::scaleToleranceToSrc(SK_Scalar1, m,
    271                                                              path.getBounds());
    272                         GrPathUtils::convertCubicToQuads(pts, tolScale, false, kDummyDir, &q);
    273                     } else {
    274                         GrPathUtils::convertCubicToQuads(devPts, SK_Scalar1, false, kDummyDir, &q);
    275                     }
    276                     for (int i = 0; i < q.count(); i += 3) {
    277                         SkPoint* qInDevSpace;
    278                         // bounds has to be calculated in device space, but q is
    279                         // in src space when there is perspective.
    280                         if (persp) {
    281                             m.mapPoints(devPts, &q[i], 3);
    282                             bounds.setBounds(devPts, 3);
    283                             qInDevSpace = devPts;
    284                         } else {
    285                             bounds.setBounds(&q[i], 3);
    286                             qInDevSpace = &q[i];
    287                         }
    288                         bounds.outset(SK_Scalar1, SK_Scalar1);
    289                         bounds.roundOut(&ibounds);
    290                         if (SkIRect::Intersects(devClipBounds, ibounds)) {
    291                             int subdiv = num_quad_subdivs(qInDevSpace);
    292                             GrAssert(subdiv >= -1);
    293                             if (-1 == subdiv) {
    294                                 SkPoint* pts = lines->push_back_n(4);
    295                                 // lines should always be in device coords
    296                                 pts[0] = qInDevSpace[0];
    297                                 pts[1] = qInDevSpace[1];
    298                                 pts[2] = qInDevSpace[1];
    299                                 pts[3] = qInDevSpace[2];
    300                             } else {
    301                                 SkPoint* pts = quads->push_back_n(3);
    302                                 // q is already in src space when there is no
    303                                 // perspective and dev coords otherwise.
    304                                 pts[0] = q[0 + i];
    305                                 pts[1] = q[1 + i];
    306                                 pts[2] = q[2 + i];
    307                                 quadSubdivCnts->push_back() = subdiv;
    308                                 totalQuadCount += 1 << subdiv;
    309                             }
    310                         }
    311                     }
    312                 }
    313                 break;
    314             case kClose_PathCmd:
    315                 break;
    316             case kEnd_PathCmd:
    317                 return totalQuadCount;
    318         }
    319     }
    320 }
    321 
    322 struct Vertex {
    323     GrPoint fPos;
    324     union {
    325         struct {
    326             SkScalar fA;
    327             SkScalar fB;
    328             SkScalar fC;
    329         } fLine;
    330         GrVec   fQuadCoord;
    331         struct {
    332             SkScalar fBogus[4];
    333         };
    334     };
    335 };
    336 GR_STATIC_ASSERT(sizeof(Vertex) == 3 * sizeof(GrPoint));
    337 
    338 void intersect_lines(const SkPoint& ptA, const SkVector& normA,
    339                      const SkPoint& ptB, const SkVector& normB,
    340                      SkPoint* result) {
    341 
    342     SkScalar lineAW = -normA.dot(ptA);
    343     SkScalar lineBW = -normB.dot(ptB);
    344 
    345     SkScalar wInv = SkScalarMul(normA.fX, normB.fY) -
    346                     SkScalarMul(normA.fY, normB.fX);
    347     wInv = SkScalarInvert(wInv);
    348 
    349     result->fX = SkScalarMul(normA.fY, lineBW) - SkScalarMul(lineAW, normB.fY);
    350     result->fX = SkScalarMul(result->fX, wInv);
    351 
    352     result->fY = SkScalarMul(lineAW, normB.fX) - SkScalarMul(normA.fX, lineBW);
    353     result->fY = SkScalarMul(result->fY, wInv);
    354 }
    355 
    356 void bloat_quad(const SkPoint qpts[3], const SkMatrix* toDevice,
    357                 const SkMatrix* toSrc, Vertex verts[kVertsPerQuad]) {
    358     GrAssert(!toDevice == !toSrc);
    359     // original quad is specified by tri a,b,c
    360     SkPoint a = qpts[0];
    361     SkPoint b = qpts[1];
    362     SkPoint c = qpts[2];
    363 
    364     // this should be in the src space, not dev coords, when we have perspective
    365     GrPathUtils::QuadUVMatrix DevToUV(qpts);
    366 
    367     if (toDevice) {
    368         toDevice->mapPoints(&a, 1);
    369         toDevice->mapPoints(&b, 1);
    370         toDevice->mapPoints(&c, 1);
    371     }
    372     // make a new poly where we replace a and c by a 1-pixel wide edges orthog
    373     // to edges ab and bc:
    374     //
    375     //   before       |        after
    376     //                |              b0
    377     //         b      |
    378     //                |
    379     //                |     a0            c0
    380     // a         c    |        a1       c1
    381     //
    382     // edges a0->b0 and b0->c0 are parallel to original edges a->b and b->c,
    383     // respectively.
    384     Vertex& a0 = verts[0];
    385     Vertex& a1 = verts[1];
    386     Vertex& b0 = verts[2];
    387     Vertex& c0 = verts[3];
    388     Vertex& c1 = verts[4];
    389 
    390     SkVector ab = b;
    391     ab -= a;
    392     SkVector ac = c;
    393     ac -= a;
    394     SkVector cb = b;
    395     cb -= c;
    396 
    397     // We should have already handled degenerates
    398     GrAssert(ab.length() > 0 && cb.length() > 0);
    399 
    400     ab.normalize();
    401     SkVector abN;
    402     abN.setOrthog(ab, SkVector::kLeft_Side);
    403     if (abN.dot(ac) > 0) {
    404         abN.negate();
    405     }
    406 
    407     cb.normalize();
    408     SkVector cbN;
    409     cbN.setOrthog(cb, SkVector::kLeft_Side);
    410     if (cbN.dot(ac) < 0) {
    411         cbN.negate();
    412     }
    413 
    414     a0.fPos = a;
    415     a0.fPos += abN;
    416     a1.fPos = a;
    417     a1.fPos -= abN;
    418 
    419     c0.fPos = c;
    420     c0.fPos += cbN;
    421     c1.fPos = c;
    422     c1.fPos -= cbN;
    423 
    424     intersect_lines(a0.fPos, abN, c0.fPos, cbN, &b0.fPos);
    425 
    426     if (toSrc) {
    427         toSrc->mapPointsWithStride(&verts[0].fPos, sizeof(Vertex), kVertsPerQuad);
    428     }
    429     DevToUV.apply<kVertsPerQuad, sizeof(Vertex), sizeof(GrPoint)>(verts);
    430 }
    431 
    432 void add_quads(const SkPoint p[3],
    433                int subdiv,
    434                const SkMatrix* toDevice,
    435                const SkMatrix* toSrc,
    436                Vertex** vert) {
    437     GrAssert(subdiv >= 0);
    438     if (subdiv) {
    439         SkPoint newP[5];
    440         SkChopQuadAtHalf(p, newP);
    441         add_quads(newP + 0, subdiv-1, toDevice, toSrc, vert);
    442         add_quads(newP + 2, subdiv-1, toDevice, toSrc, vert);
    443     } else {
    444         bloat_quad(p, toDevice, toSrc, *vert);
    445         *vert += kVertsPerQuad;
    446     }
    447 }
    448 
    449 void add_line(const SkPoint p[2],
    450               int rtHeight,
    451               const SkMatrix* toSrc,
    452               Vertex** vert) {
    453     const SkPoint& a = p[0];
    454     const SkPoint& b = p[1];
    455 
    456     SkVector orthVec = b;
    457     orthVec -= a;
    458 
    459     if (orthVec.setLength(SK_Scalar1)) {
    460         orthVec.setOrthog(orthVec);
    461 
    462         SkScalar lineC = -(a.dot(orthVec));
    463         for (int i = 0; i < kVertsPerLineSeg; ++i) {
    464             (*vert)[i].fPos = (i < 2) ? a : b;
    465             if (0 == i || 3 == i) {
    466                 (*vert)[i].fPos -= orthVec;
    467             } else {
    468                 (*vert)[i].fPos += orthVec;
    469             }
    470             (*vert)[i].fLine.fA = orthVec.fX;
    471             (*vert)[i].fLine.fB = orthVec.fY;
    472             (*vert)[i].fLine.fC = lineC;
    473         }
    474         if (NULL != toSrc) {
    475             toSrc->mapPointsWithStride(&(*vert)->fPos,
    476                                        sizeof(Vertex),
    477                                        kVertsPerLineSeg);
    478         }
    479     } else {
    480         // just make it degenerate and likely offscreen
    481         (*vert)[0].fPos.set(SK_ScalarMax, SK_ScalarMax);
    482         (*vert)[1].fPos.set(SK_ScalarMax, SK_ScalarMax);
    483         (*vert)[2].fPos.set(SK_ScalarMax, SK_ScalarMax);
    484         (*vert)[3].fPos.set(SK_ScalarMax, SK_ScalarMax);
    485     }
    486 
    487     *vert += kVertsPerLineSeg;
    488 }
    489 
    490 }
    491 
    492 bool GrAAHairLinePathRenderer::createGeom(
    493             const SkPath& path,
    494             GrDrawTarget* target,
    495             int* lineCnt,
    496             int* quadCnt,
    497             GrDrawTarget::AutoReleaseGeometry* arg) {
    498     const GrDrawState& drawState = target->getDrawState();
    499     int rtHeight = drawState.getRenderTarget()->height();
    500 
    501     GrIRect devClipBounds;
    502     target->getClip()->getConservativeBounds(drawState.getRenderTarget(),
    503                                              &devClipBounds);
    504 
    505     GrVertexLayout layout = GrDrawState::kEdge_VertexLayoutBit;
    506     SkMatrix viewM = drawState.getViewMatrix();
    507 
    508     PREALLOC_PTARRAY(128) lines;
    509     PREALLOC_PTARRAY(128) quads;
    510     IntArray qSubdivs;
    511     *quadCnt = generate_lines_and_quads(path, viewM, devClipBounds,
    512                                         &lines, &quads, &qSubdivs);
    513 
    514     *lineCnt = lines.count() / 2;
    515     int vertCnt = kVertsPerLineSeg * *lineCnt + kVertsPerQuad * *quadCnt;
    516 
    517     GrAssert(sizeof(Vertex) == GrDrawState::VertexSize(layout));
    518 
    519     if (!arg->set(target, layout, vertCnt, 0)) {
    520         return false;
    521     }
    522 
    523     Vertex* verts = reinterpret_cast<Vertex*>(arg->vertices());
    524 
    525     const SkMatrix* toDevice = NULL;
    526     const SkMatrix* toSrc = NULL;
    527     SkMatrix ivm;
    528 
    529     if (viewM.hasPerspective()) {
    530         if (viewM.invert(&ivm)) {
    531             toDevice = &viewM;
    532             toSrc = &ivm;
    533         }
    534     }
    535 
    536     for (int i = 0; i < *lineCnt; ++i) {
    537         add_line(&lines[2*i], rtHeight, toSrc, &verts);
    538     }
    539 
    540     int unsubdivQuadCnt = quads.count() / 3;
    541     for (int i = 0; i < unsubdivQuadCnt; ++i) {
    542         GrAssert(qSubdivs[i] >= 0);
    543         add_quads(&quads[3*i], qSubdivs[i], toDevice, toSrc, &verts);
    544     }
    545 
    546     return true;
    547 }
    548 
    549 bool GrAAHairLinePathRenderer::canDrawPath(const SkPath& path,
    550                                            const SkStrokeRec& stroke,
    551                                            const GrDrawTarget* target,
    552                                            bool antiAlias) const {
    553     if (!stroke.isHairlineStyle() || !antiAlias) {
    554         return false;
    555     }
    556 
    557     static const uint32_t gReqDerivMask = SkPath::kCubic_SegmentMask |
    558                                           SkPath::kQuad_SegmentMask;
    559     if (!target->getCaps().shaderDerivativeSupport() &&
    560         (gReqDerivMask & path.getSegmentMasks())) {
    561         return false;
    562     }
    563     return true;
    564 }
    565 
    566 bool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path,
    567                                           const SkStrokeRec&,
    568                                           GrDrawTarget* target,
    569                                           bool antiAlias) {
    570 
    571     int lineCnt;
    572     int quadCnt;
    573     GrDrawTarget::AutoReleaseGeometry arg;
    574     if (!this->createGeom(path,
    575                           target,
    576                           &lineCnt,
    577                           &quadCnt,
    578                           &arg)) {
    579         return false;
    580     }
    581 
    582     GrDrawState::AutoDeviceCoordDraw adcd;
    583     GrDrawState* drawState = target->drawState();
    584     // createGeom transforms the geometry to device space when the matrix does not have
    585     // perspective.
    586     if (!drawState->getViewMatrix().hasPerspective()) {
    587         adcd.set(drawState);
    588         if (!adcd.succeeded()) {
    589             return false;
    590         }
    591     }
    592 
    593     // TODO: See whether rendering lines as degenerate quads improves perf
    594     // when we have a mix
    595 
    596     GrDrawState::VertexEdgeType oldEdgeType = drawState->getVertexEdgeType();
    597 
    598     target->setIndexSourceToBuffer(fLinesIndexBuffer);
    599     int lines = 0;
    600     int nBufLines = fLinesIndexBuffer->maxQuads();
    601     drawState->setVertexEdgeType(GrDrawState::kHairLine_EdgeType);
    602     while (lines < lineCnt) {
    603         int n = GrMin(lineCnt - lines, nBufLines);
    604         target->drawIndexed(kTriangles_GrPrimitiveType,
    605                             kVertsPerLineSeg*lines,    // startV
    606                             0,                         // startI
    607                             kVertsPerLineSeg*n,        // vCount
    608                             kIdxsPerLineSeg*n);        // iCount
    609         lines += n;
    610     }
    611 
    612     target->setIndexSourceToBuffer(fQuadsIndexBuffer);
    613     int quads = 0;
    614     drawState->setVertexEdgeType(GrDrawState::kHairQuad_EdgeType);
    615     while (quads < quadCnt) {
    616         int n = GrMin(quadCnt - quads, kNumQuadsInIdxBuffer);
    617         target->drawIndexed(kTriangles_GrPrimitiveType,
    618                             4 * lineCnt + kVertsPerQuad*quads, // startV
    619                             0,                                 // startI
    620                             kVertsPerQuad*n,                   // vCount
    621                             kIdxsPerQuad*n);                   // iCount
    622         quads += n;
    623     }
    624     drawState->setVertexEdgeType(oldEdgeType);
    625     return true;
    626 }
    627