void GrInOrderDrawBuffer::drawRect(const GrRect& rect,
                                   const GrMatrix* matrix,
                                   StageBitfield stageEnableBitfield,
                                   const GrRect* srcRects[],
                                   const GrMatrix* srcMatrices[]) {

    GrAssert(!(NULL == fQuadIndexBuffer && fCurrQuad));
    GrAssert(!(fDraws.empty() && fCurrQuad));
    GrAssert(!(0 != fMaxQuads && NULL == fQuadIndexBuffer));

    // if we have a quad IB then either append to the previous run of
    // rects or start a new run
    if (fMaxQuads) {

        bool appendToPreviousDraw = false;
        GrVertexLayout layout = GetRectVertexLayout(stageEnableBitfield, srcRects);
        AutoReleaseGeometry geo(this, layout, 4, 0);
        AutoViewMatrixRestore avmr(this);
        GrMatrix combinedMatrix = this->getViewMatrix();
        this->setViewMatrix(GrMatrix::I());
        if (NULL != matrix) {
            combinedMatrix.preConcat(*matrix);
        }

        SetRectVertices(rect, &combinedMatrix, srcRects, srcMatrices, layout, geo.vertices());

        // we don't want to miss an opportunity to batch rects together
        // simply because the clip has changed if the clip doesn't affect
        // the rect.
        bool disabledClip = false;
        if (this->isClipState() && fClip.isRect()) {

            GrRect clipRect = fClip.getRect(0);
            // If the clip rect touches the edge of the viewport, extended it
            // out (close) to infinity to avoid bogus intersections.
            // We might consider a more exact clip to viewport if this
            // conservative test fails.
            const GrRenderTarget* target = this->getRenderTarget();
            if (0 >= clipRect.fLeft) {
                clipRect.fLeft = GR_ScalarMin;
            }
            if (target->width() <= clipRect.fRight) {
                clipRect.fRight = GR_ScalarMax;
            }
            if (0 >= clipRect.top()) {
                clipRect.fTop = GR_ScalarMin;
            }
            if (target->height() <= clipRect.fBottom) {
                clipRect.fBottom = GR_ScalarMax;
            }
            int stride = VertexSize(layout);
            bool insideClip = true;
            for (int v = 0; v < 4; ++v) {
                const GrPoint& p = *GetVertexPoint(geo.vertices(), v, stride);
                if (!clipRect.contains(p)) {
                    insideClip = false;
                    break;
                }
            }
            if (insideClip) {
                this->disableState(kClip_StateBit);
                disabledClip = true;
            }
        }
        if (!needsNewClip() && !needsNewState() && fCurrQuad > 0 &&
            fCurrQuad < fMaxQuads && layout == fLastRectVertexLayout) {

            int vsize = VertexSize(layout);

            Draw& lastDraw = fDraws.back();

            GrAssert(lastDraw.fIndexBuffer == fQuadIndexBuffer);
            GrAssert(kTriangles_PrimitiveType == lastDraw.fPrimitiveType);
            GrAssert(0 == lastDraw.fVertexCount % 4);
            GrAssert(0 == lastDraw.fIndexCount % 6);
            GrAssert(0 == lastDraw.fStartIndex);

            GeometryPoolState& poolState = fGeoPoolStateStack.back();
            bool clearSinceLastDraw =
                            fClears.count() && 
                            fClears.back().fBeforeDrawIdx == fDraws.count();

            appendToPreviousDraw =  
                !clearSinceLastDraw &&
                lastDraw.fVertexBuffer == poolState.fPoolVertexBuffer &&
                (fCurrQuad * 4 + lastDraw.fStartVertex) == poolState.fPoolStartVertex;

            if (appendToPreviousDraw) {
                lastDraw.fVertexCount += 4;
                lastDraw.fIndexCount += 6;
                fCurrQuad += 1;
                // we reserved above, so we should be the first
                // use of this vertex reserveation.
                GrAssert(0 == poolState.fUsedPoolVertexBytes);
                poolState.fUsedPoolVertexBytes = 4 * vsize;
            }
        }
        if (!appendToPreviousDraw) {
            this->setIndexSourceToBuffer(fQuadIndexBuffer);
            drawIndexed(kTriangles_PrimitiveType, 0, 0, 4, 6);
            fCurrQuad = 1;
            fLastRectVertexLayout = layout;
        }
        if (disabledClip) {
            this->enableState(kClip_StateBit);
        }
    } else {
        INHERITED::drawRect(rect, matrix, stageEnableBitfield, srcRects, srcMatrices);
    }
}
void GrInOrderDrawBuffer::drawRect(const GrRect& rect,
                                   const SkMatrix* matrix,
                                   const GrRect* srcRects[],
                                   const SkMatrix* srcMatrices[]) {

    GrAssert(!(NULL == fQuadIndexBuffer && fCurrQuad));
    GrAssert(!(fDraws.empty() && fCurrQuad));
    GrAssert(!(0 != fMaxQuads && NULL == fQuadIndexBuffer));

    GrDrawState* drawState = this->drawState();

    // if we have a quad IB then either append to the previous run of
    // rects or start a new run
    if (fMaxQuads) {

        bool appendToPreviousDraw = false;
        GrVertexLayout layout = GetRectVertexLayout(srcRects);

        // Batching across colors means we move the draw color into the
        // rect's vertex colors to allow greater batching (a lot of rects
        // in a row differing only in color is a common occurence in tables).
        bool batchAcrossColors = true;
        if (!this->getCaps().dualSourceBlendingSupport()) {
            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
                if (this->getDrawState().isStageEnabled(s)) {
                    // We disable batching across colors when there is a texture
                    // present because (by pushing the the color to the vertices)
                    // Ganesh loses track of the rect's opacity. This, in turn, can
                    // cause some of the blending optimizations to be disabled. This
                    // becomes a huge problem on some of the smaller devices where
                    // shader derivatives and dual source blending aren't supported.
                    // In those cases paths are often drawn to a texture and then
                    // drawn as a texture (using this method). Because dual source
                    // blending is disabled (and the blend optimizations are short
                    // circuited) some of the more esoteric blend modes can no longer
                    // be supported.
                    // TODO: add tracking of batchAcrossColors's opacity
                    batchAcrossColors = false;
                    break;
                }
            }
        }

        if (batchAcrossColors) {
            layout |= GrDrawState::kColor_VertexLayoutBit;
        }

        AutoReleaseGeometry geo(this, layout, 4, 0);
        if (!geo.succeeded()) {
            GrPrintf("Failed to get space for vertices!\n");
            return;
        }
        SkMatrix combinedMatrix = drawState->getViewMatrix();
        // We go to device space so that matrix changes allow us to concat
        // rect draws. When the caller has provided explicit source rects
        // then we don't want to modify the stages' matrices. Otherwise
        // we have to account for the view matrix change in the stage
        // matrices.
        uint32_t explicitCoordMask = 0;
        if (srcRects) {
            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
                if (srcRects[s]) {
                    explicitCoordMask |= (1 << s);
                }
            }
        }
        GrDrawState::AutoDeviceCoordDraw adcd(this->drawState(), explicitCoordMask);
        if (!adcd.succeeded()) {
            return;
        }
        if (NULL != matrix) {
            combinedMatrix.preConcat(*matrix);
        }

        SetRectVertices(rect, &combinedMatrix, srcRects, srcMatrices,
                        this->getDrawState().getColor(), layout, geo.vertices());

        // Now that the paint's color is stored in the vertices set it to
        // white so that the following code can batch all the rects regardless
        // of paint color
        GrDrawState::AutoColorRestore acr(this->drawState(),
                                          batchAcrossColors ? SK_ColorWHITE
                                                            : this->getDrawState().getColor());

        // we don't want to miss an opportunity to batch rects together
        // simply because the clip has changed if the clip doesn't affect
        // the rect.
        bool disabledClip = false;

        if (drawState->isClipState()) {

            GrRect devClipRect;
            bool isIntersectionOfRects = false;
            const GrClipData* clip = this->getClip();
            clip->fClipStack->getConservativeBounds(-clip->fOrigin.fX,
                                                    -clip->fOrigin.fY,
                                                    drawState->getRenderTarget()->width(),
                                                    drawState->getRenderTarget()->height(),
                                                    &devClipRect,
                                                    &isIntersectionOfRects);

            if (isIntersectionOfRects) {
                // If the clip rect touches the edge of the viewport, extended it
                // out (close) to infinity to avoid bogus intersections.
                // We might consider a more exact clip to viewport if this
                // conservative test fails.
                const GrRenderTarget* target = drawState->getRenderTarget();
                if (0 >= devClipRect.fLeft) {
                    devClipRect.fLeft = SK_ScalarMin;
                }
                if (target->width() <= devClipRect.fRight) {
                    devClipRect.fRight = SK_ScalarMax;
                }
                if (0 >= devClipRect.top()) {
                    devClipRect.fTop = SK_ScalarMin;
                }
                if (target->height() <= devClipRect.fBottom) {
                    devClipRect.fBottom = SK_ScalarMax;
                }
                int stride = GrDrawState::VertexSize(layout);
                bool insideClip = true;
                for (int v = 0; v < 4; ++v) {
                    const GrPoint& p = *GrDrawState::GetVertexPoint(geo.vertices(), v, stride);
                    if (!devClipRect.contains(p)) {
                        insideClip = false;
                        break;
                    }
                }
                if (insideClip) {
                    drawState->disableState(GrDrawState::kClip_StateBit);
                    disabledClip = true;
                }
            }
        }

        if (!this->needsNewClip() &&
            !this->needsNewState() &&
            fCurrQuad > 0 &&
            fCurrQuad < fMaxQuads &&
            layout == fLastRectVertexLayout) {

            int vsize = GrDrawState::VertexSize(layout);

            Draw& lastDraw = fDraws.back();

            GrAssert(lastDraw.fIndexBuffer == fQuadIndexBuffer);
            GrAssert(kTriangles_GrPrimitiveType == lastDraw.fPrimitiveType);
            GrAssert(0 == lastDraw.fVertexCount % 4);
            GrAssert(0 == lastDraw.fIndexCount % 6);
            GrAssert(0 == lastDraw.fStartIndex);

            GeometryPoolState& poolState = fGeoPoolStateStack.back();

            appendToPreviousDraw =
                kDraw_Cmd == fCmds.back() &&
                lastDraw.fVertexBuffer == poolState.fPoolVertexBuffer &&
                (fCurrQuad * 4 + lastDraw.fStartVertex) == poolState.fPoolStartVertex;

            if (appendToPreviousDraw) {
                lastDraw.fVertexCount += 4;
                lastDraw.fIndexCount += 6;
                fCurrQuad += 1;
                // we reserved above, so we should be the first
                // use of this vertex reservation.
                GrAssert(0 == poolState.fUsedPoolVertexBytes);
                poolState.fUsedPoolVertexBytes = 4 * vsize;
            }
        }
        if (!appendToPreviousDraw) {
            this->setIndexSourceToBuffer(fQuadIndexBuffer);
            this->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 4, 6);
            fCurrQuad = 1;
            fLastRectVertexLayout = layout;
        }
        if (disabledClip) {
            drawState->enableState(GrDrawState::kClip_StateBit);
        }
        fInstancedDrawTracker.reset();
    } else {
        INHERITED::drawRect(rect, matrix, srcRects, srcMatrices);
    }
}