Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(322)

Unified Diff: src/gpu/effects/GrDashingEffect.cpp

Issue 925673002: Dash batch (Closed) Base URL: https://skia.googlesource.com/skia.git@default
Patch Set: feedback incorporated Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/gpu/effects/GrDashingEffect.cpp
diff --git a/src/gpu/effects/GrDashingEffect.cpp b/src/gpu/effects/GrDashingEffect.cpp
index afeff437f127c6ef1efe129604bdb906f7387dcf..f9d526f7e7b34438a85c0896d7bc97b18dd8f768 100644
--- a/src/gpu/effects/GrDashingEffect.cpp
+++ b/src/gpu/effects/GrDashingEffect.cpp
@@ -7,8 +7,9 @@
#include "GrDashingEffect.h"
-#include "../GrAARectRenderer.h"
-
+#include "GrBatch.h"
+#include "GrBatchTarget.h"
+#include "GrBufferAllocPool.h"
#include "GrGeometryProcessor.h"
#include "GrContext.h"
#include "GrCoordTransform.h"
@@ -114,21 +115,21 @@ static void align_to_x_axis(const SkPoint pts[2], SkMatrix* rotMatrix, SkPoint p
}
// Assumes phase < sum of all intervals
-static SkScalar calc_start_adjustment(const SkPathEffect::DashInfo& info) {
- SkASSERT(info.fPhase < info.fIntervals[0] + info.fIntervals[1]);
- if (info.fPhase >= info.fIntervals[0] && info.fPhase != 0) {
- SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1];
- return srcIntervalLen - info.fPhase;
+static SkScalar calc_start_adjustment(const SkScalar intervals[2], SkScalar phase) {
+ SkASSERT(phase < intervals[0] + intervals[1]);
+ if (phase >= intervals[0] && phase != 0) {
+ SkScalar srcIntervalLen = intervals[0] + intervals[1];
+ return srcIntervalLen - phase;
}
return 0;
}
-static SkScalar calc_end_adjustment(const SkPathEffect::DashInfo& info, const SkPoint pts[2],
+static SkScalar calc_end_adjustment(const SkScalar intervals[2], const SkPoint pts[2],
SkScalar phase, SkScalar* endingInt) {
if (pts[1].fX <= pts[0].fX) {
return 0;
}
- SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1];
+ SkScalar srcIntervalLen = intervals[0] + intervals[1];
SkScalar totalLen = pts[1].fX - pts[0].fX;
SkScalar temp = SkScalarDiv(totalLen, srcIntervalLen);
SkScalar numFullIntervals = SkScalarFloorToScalar(temp);
@@ -138,11 +139,11 @@ static SkScalar calc_end_adjustment(const SkPathEffect::DashInfo& info, const Sk
if (0 == *endingInt) {
*endingInt = srcIntervalLen;
}
- if (*endingInt > info.fIntervals[0]) {
- if (0 == info.fIntervals[0]) {
+ if (*endingInt > intervals[0]) {
+ if (0 == intervals[0]) {
*endingInt -= 0.01f; // make sure we capture the last zero size pnt (used if has caps)
}
- return *endingInt - info.fIntervals[0];
+ return *endingInt - intervals[0];
}
return 0;
}
@@ -237,285 +238,502 @@ static GrGeometryProcessor* create_dash_gp(GrColor,
DashCap cap,
const SkMatrix& localMatrix);
-bool GrDashingEffect::DrawDashLine(GrGpu* gpu, GrDrawTarget* target,
- GrPipelineBuilder* pipelineBuilder, GrColor color,
- const SkMatrix& viewMatrix, const SkPoint pts[2],
- const GrPaint& paint, const GrStrokeInfo& strokeInfo) {
- if (!can_fast_path_dash(pts, strokeInfo, *target, *pipelineBuilder, viewMatrix)) {
- return false;
+class DashBatch : public GrBatch {
+public:
+ struct Geometry {
+ GrColor fColor;
+ SkMatrix fViewMatrix;
+ SkMatrix fSrcRotInv;
+ SkPoint fPtsRot[2];
+ SkScalar fSrcStrokeWidth;
+ SkScalar fPhase;
+ SkScalar fIntervals[2];
+ SkScalar fParallelScale;
+ SkScalar fPerpendicularScale;
+ SkDEBUGCODE(SkRect fDevBounds;)
+ };
+
+ static GrBatch* Create(const Geometry& geometry, SkPaint::Cap cap, bool useAA, bool fullDash) {
+ return SkNEW_ARGS(DashBatch, (geometry, cap, useAA, fullDash));
}
- const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo();
+ const char* name() const SK_OVERRIDE { return "DashBatch"; }
- SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap();
+ void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
+ // When this is called on a batch, there is only one geometry bundle
+ out->setKnownFourComponents(fGeoData[0].fColor);
+ }
+ void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
+ out->setUnknownSingleComponent();
+ }
- SkScalar srcStrokeWidth = strokeInfo.getStrokeRec().getWidth();
+ void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
+ // Handle any color overrides
+ if (init.fColorIgnored) {
+ fGeoData[0].fColor = GrColor_ILLEGAL;
+ } else if (GrColor_ILLEGAL != init.fOverrideColor) {
+ fGeoData[0].fColor = init.fOverrideColor;
+ }
- // the phase should be normalized to be [0, sum of all intervals)
- SkASSERT(info.fPhase >= 0 && info.fPhase < info.fIntervals[0] + info.fIntervals[1]);
+ // setup batch properties
+ fBatch.fColorIgnored = init.fColorIgnored;
+ fBatch.fColor = fGeoData[0].fColor;
+ fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
+ fBatch.fCoverageIgnored = init.fCoverageIgnored;
+ }
- SkScalar srcPhase = info.fPhase;
+ struct DashDraw {
+ SkScalar fStartOffset;
+ SkScalar fStrokeWidth;
+ SkScalar fLineLength;
+ SkScalar fHalfDevStroke;
+ SkScalar fDevBloat;
+ bool fLineDone;
+ bool fHasStartRect;
+ bool fHasEndRect;
+ };
+
+ void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+ int instanceCount = fGeoData.count();
+
+ SkMatrix invert;
+ if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
+ SkDebugf("Failed to invert\n");
+ return;
+ }
- // Rotate the src pts so they are aligned horizontally with pts[0].fX < pts[1].fX
- SkMatrix srcRotInv;
- SkPoint ptsRot[2];
- if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) {
- SkMatrix rotMatrix;
- align_to_x_axis(pts, &rotMatrix, ptsRot);
- if(!rotMatrix.invert(&srcRotInv)) {
- SkDebugf("Failed to create invertible rotation matrix!\n");
- return false;
+ SkPaint::Cap cap = this->cap();
+
+ SkAutoTUnref<const GrGeometryProcessor> gp;
+
+ bool isRoundCap = SkPaint::kRound_Cap == cap;
+ DashCap capType = isRoundCap ? kRound_DashCap : kNonRound_DashCap;
+ if (this->fullDash()) {
+ GrPrimitiveEdgeType edgeType = this->useAA() ? kFillAA_GrProcessorEdgeType :
+ kFillBW_GrProcessorEdgeType;
+ gp.reset(create_dash_gp(this->color(), edgeType, capType, invert));
+ } else {
+ // Set up the vertex data for the line and start/end dashes
+ gp.reset(GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType,
+ this->color(),
+ SkMatrix::I(),
+ invert));
}
- } else {
- srcRotInv.reset();
- memcpy(ptsRot, pts, 2 * sizeof(SkPoint));
- }
- bool useAA = paint.isAntiAlias();
+ batchTarget->initDraw(gp, pipeline);
+
+ // TODO remove this when batch is everywhere
+ GrPipelineInfo init;
+ init.fColorIgnored = fBatch.fColorIgnored;
+ init.fOverrideColor = GrColor_ILLEGAL;
+ init.fCoverageIgnored = fBatch.fCoverageIgnored;
+ init.fUsesLocalCoords = this->usesLocalCoords();
+ gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
+
+ bool useAA = this->useAA();
+ bool fullDash = this->fullDash();
+
+ // We do two passes over all of the dashes. First we setup the start, end, and bounds,
+ // rectangles. We preserve all of this work in the rects / draws arrays below. Then we
+ // iterate again over these decomposed dashes to generate vertices
+ SkSTArray<128, SkRect, true> rects;
+ SkSTArray<128, DashDraw, true> draws;
+
+ int totalRectCount = 0;
+ for (int i = 0; i < instanceCount; i++) {
+ Geometry& args = fGeoData[i];
+
+ bool hasCap = SkPaint::kButt_Cap != cap && 0 != args.fSrcStrokeWidth;
+
+ // We always want to at least stroke out half a pixel on each side in device space
+ // so 0.5f / perpScale gives us this min in src space
+ SkScalar halfSrcStroke =
+ SkMaxScalar(args.fSrcStrokeWidth * 0.5f, 0.5f / args.fPerpendicularScale);
+
+ SkScalar strokeAdj;
+ if (!hasCap) {
+ strokeAdj = 0.f;
+ } else {
+ strokeAdj = halfSrcStroke;
+ }
- // Scale corrections of intervals and stroke from view matrix
- SkScalar parallelScale;
- SkScalar perpScale;
- calc_dash_scaling(&parallelScale, &perpScale, viewMatrix, ptsRot);
+ SkScalar startAdj = 0;
+
+ SkMatrix& combinedMatrix = args.fSrcRotInv;
+ combinedMatrix.postConcat(args.fViewMatrix);
+
+ bool lineDone = false;
+
+ // Too simplify the algorithm, we always push back rects for start and end rect.
+ // Otherwise we'd have to track start / end rects for each individual geometry
+ SkRect& bounds = rects.push_back();
+ SkRect& startRect = rects.push_back();
+ SkRect& endRect = rects.push_back();
+
+ bool hasStartRect = false;
+ // If we are using AA, check to see if we are drawing a partial dash at the start. If so
+ // draw it separately here and adjust our start point accordingly
+ if (useAA) {
+ if (args.fPhase > 0 && args.fPhase < args.fIntervals[0]) {
+ SkPoint startPts[2];
+ startPts[0] = args.fPtsRot[0];
+ startPts[1].fY = startPts[0].fY;
+ startPts[1].fX = SkMinScalar(startPts[0].fX + args.fIntervals[0] - args.fPhase,
+ args.fPtsRot[1].fX);
+ startRect.set(startPts, 2);
+ startRect.outset(strokeAdj, halfSrcStroke);
+
+ hasStartRect = true;
+ startAdj = args.fIntervals[0] + args.fIntervals[1] - args.fPhase;
+ }
+ }
- bool hasCap = SkPaint::kButt_Cap != cap && 0 != srcStrokeWidth;
+ // adjustments for start and end of bounding rect so we only draw dash intervals
+ // contained in the original line segment.
+ startAdj += calc_start_adjustment(args.fIntervals, args.fPhase);
+ if (startAdj != 0) {
+ args.fPtsRot[0].fX += startAdj;
+ args.fPhase = 0;
+ }
+ SkScalar endingInterval = 0;
+ SkScalar endAdj = calc_end_adjustment(args.fIntervals, args.fPtsRot, args.fPhase,
+ &endingInterval);
+ args.fPtsRot[1].fX -= endAdj;
+ if (args.fPtsRot[0].fX >= args.fPtsRot[1].fX) {
+ lineDone = true;
+ }
- // We always want to at least stroke out half a pixel on each side in device space
- // so 0.5f / perpScale gives us this min in src space
- SkScalar halfSrcStroke = SkMaxScalar(srcStrokeWidth * 0.5f, 0.5f / perpScale);
+ bool hasEndRect = false;
+ // If we are using AA, check to see if we are drawing a partial dash at then end. If so
+ // draw it separately here and adjust our end point accordingly
+ if (useAA && !lineDone) {
+ // If we adjusted the end then we will not be drawing a partial dash at the end.
+ // If we didn't adjust the end point then we just need to make sure the ending
+ // dash isn't a full dash
+ if (0 == endAdj && endingInterval != args.fIntervals[0]) {
+ SkPoint endPts[2];
+ endPts[1] = args.fPtsRot[1];
+ endPts[0].fY = endPts[1].fY;
+ endPts[0].fX = endPts[1].fX - endingInterval;
+
+ endRect.set(endPts, 2);
+ endRect.outset(strokeAdj, halfSrcStroke);
+
+ hasEndRect = true;
+ endAdj = endingInterval + args.fIntervals[1];
+
+ args.fPtsRot[1].fX -= endAdj;
+ if (args.fPtsRot[0].fX >= args.fPtsRot[1].fX) {
+ lineDone = true;
+ }
+ }
+ }
- SkScalar strokeAdj;
- if (!hasCap) {
- strokeAdj = 0.f;
- } else {
- strokeAdj = halfSrcStroke;
- }
+ if (startAdj != 0) {
+ args.fPhase = 0;
+ }
- SkScalar startAdj = 0;
-
- SkMatrix combinedMatrix = srcRotInv;
- combinedMatrix.postConcat(viewMatrix);
-
- bool lineDone = false;
- SkRect startRect;
- bool hasStartRect = false;
- // If we are using AA, check to see if we are drawing a partial dash at the start. If so
- // draw it separately here and adjust our start point accordingly
- if (useAA) {
- if (srcPhase > 0 && srcPhase < info.fIntervals[0]) {
- SkPoint startPts[2];
- startPts[0] = ptsRot[0];
- startPts[1].fY = startPts[0].fY;
- startPts[1].fX = SkMinScalar(startPts[0].fX + info.fIntervals[0] - srcPhase,
- ptsRot[1].fX);
- startRect.set(startPts, 2);
- startRect.outset(strokeAdj, halfSrcStroke);
-
- hasStartRect = true;
- startAdj = info.fIntervals[0] + info.fIntervals[1] - srcPhase;
- }
- }
+ // Change the dashing info from src space into device space
+ SkScalar* devIntervals = args.fIntervals;
+ devIntervals[0] = args.fIntervals[0] * args.fParallelScale;
+ devIntervals[1] = args.fIntervals[1] * args.fParallelScale;
+ SkScalar devPhase = args.fPhase * args.fParallelScale;
+ SkScalar strokeWidth = args.fSrcStrokeWidth * args.fPerpendicularScale;
- // adjustments for start and end of bounding rect so we only draw dash intervals
- // contained in the original line segment.
- startAdj += calc_start_adjustment(info);
- if (startAdj != 0) {
- ptsRot[0].fX += startAdj;
- srcPhase = 0;
- }
- SkScalar endingInterval = 0;
- SkScalar endAdj = calc_end_adjustment(info, ptsRot, srcPhase, &endingInterval);
- ptsRot[1].fX -= endAdj;
- if (ptsRot[0].fX >= ptsRot[1].fX) {
- lineDone = true;
- }
+ if ((strokeWidth < 1.f && !useAA) || 0.f == strokeWidth) {
+ strokeWidth = 1.f;
+ }
+
+ SkScalar halfDevStroke = strokeWidth * 0.5f;
- SkRect endRect;
- bool hasEndRect = false;
- // If we are using AA, check to see if we are drawing a partial dash at then end. If so
- // draw it separately here and adjust our end point accordingly
- if (useAA && !lineDone) {
- // If we adjusted the end then we will not be drawing a partial dash at the end.
- // If we didn't adjust the end point then we just need to make sure the ending
- // dash isn't a full dash
- if (0 == endAdj && endingInterval != info.fIntervals[0]) {
- SkPoint endPts[2];
- endPts[1] = ptsRot[1];
- endPts[0].fY = endPts[1].fY;
- endPts[0].fX = endPts[1].fX - endingInterval;
-
- endRect.set(endPts, 2);
- endRect.outset(strokeAdj, halfSrcStroke);
-
- hasEndRect = true;
- endAdj = endingInterval + info.fIntervals[1];
-
- ptsRot[1].fX -= endAdj;
- if (ptsRot[0].fX >= ptsRot[1].fX) {
+ if (SkPaint::kSquare_Cap == cap && 0 != args.fSrcStrokeWidth) {
+ // add cap to on interval and remove from off interval
+ devIntervals[0] += strokeWidth;
+ devIntervals[1] -= strokeWidth;
+ }
+ SkScalar startOffset = devIntervals[1] * 0.5f + devPhase;
+
+ SkScalar bloatX = useAA ? 0.5f / args.fParallelScale : 0.f;
+ SkScalar bloatY = useAA ? 0.5f / args.fPerpendicularScale : 0.f;
+
+ SkScalar devBloat = useAA ? 0.5f : 0.f;
+
+ if (devIntervals[1] <= 0.f && useAA) {
+ // Case when we end up drawing a solid AA rect
+ // Reset the start rect to draw this single solid rect
+ // but it requires to upload a new intervals uniform so we can mimic
+ // one giant dash
+ args.fPtsRot[0].fX -= hasStartRect ? startAdj : 0;
+ args.fPtsRot[1].fX += hasEndRect ? endAdj : 0;
+ startRect.set(args.fPtsRot, 2);
+ startRect.outset(strokeAdj, halfSrcStroke);
+ hasStartRect = true;
+ hasEndRect = false;
lineDone = true;
+
+ SkPoint devicePts[2];
+ args.fViewMatrix.mapPoints(devicePts, args.fPtsRot, 2);
+ SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
+ if (hasCap) {
+ lineLength += 2.f * halfDevStroke;
+ }
+ devIntervals[0] = lineLength;
+ }
+
+ totalRectCount += !lineDone ? 1 : 0;
+ totalRectCount += hasStartRect ? 1 : 0;
+ totalRectCount += hasEndRect ? 1 : 0;
+
+ if (SkPaint::kRound_Cap == cap && 0 != args.fSrcStrokeWidth) {
+ // need to adjust this for round caps to correctly set the dashPos attrib on
+ // vertices
+ startOffset -= halfDevStroke;
+ }
+
+ DashDraw& draw = draws.push_back();
+ if (!lineDone) {
+ SkPoint devicePts[2];
+ args.fViewMatrix.mapPoints(devicePts, args.fPtsRot, 2);
+ draw.fLineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
+ if (hasCap) {
+ draw.fLineLength += 2.f * halfDevStroke;
+ }
+
+ bounds.set(args.fPtsRot[0].fX, args.fPtsRot[0].fY,
+ args.fPtsRot[1].fX, args.fPtsRot[1].fY);
+ bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke);
+ }
+
+ if (hasStartRect) {
+ SkASSERT(useAA); // so that we know bloatX and bloatY have been set
+ startRect.outset(bloatX, bloatY);
+ }
+
+ if (hasEndRect) {
+ SkASSERT(useAA); // so that we know bloatX and bloatY have been set
+ endRect.outset(bloatX, bloatY);
+ }
+
+ draw.fStartOffset = startOffset;
+ draw.fDevBloat = devBloat;
+ draw.fHalfDevStroke = halfDevStroke;
+ draw.fStrokeWidth = strokeWidth;
+ draw.fHasStartRect = hasStartRect;
+ draw.fLineDone = lineDone;
+ draw.fHasEndRect = hasEndRect;
+ }
+
+ const GrVertexBuffer* vertexBuffer;
+ int firstVertex;
+
+ size_t vertexStride = gp->getVertexStride();
+ void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
+ totalRectCount * kVertsPerDash,
+ &vertexBuffer,
+ &firstVertex);
+
+ int curVIdx = 0;
+ int rectIndex = 0;
+ for (int i = 0; i < instanceCount; i++) {
+ Geometry& args = fGeoData[i];
+
+ if (!draws[i].fLineDone) {
+ if (fullDash) {
+ setup_dashed_rect(rects[rectIndex], vertices, curVIdx, args.fSrcRotInv,
+ draws[i].fStartOffset, draws[i].fDevBloat,
+ draws[i].fLineLength, draws[i].fHalfDevStroke,
+ args.fIntervals[0], args.fIntervals[1], draws[i].fStrokeWidth,
+ capType, gp->getVertexStride());
+ } else {
+ SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
+ SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
+ setup_dashed_rect_pos(rects[rectIndex], curVIdx, args.fSrcRotInv, verts);
+ }
+ curVIdx += 4;
+ }
+ rectIndex++;
+
+ if (draws[i].fHasStartRect) {
+ if (fullDash) {
+ setup_dashed_rect(rects[rectIndex], vertices, curVIdx, args.fSrcRotInv,
+ draws[i].fStartOffset, draws[i].fDevBloat, args.fIntervals[0],
+ draws[i].fHalfDevStroke, args.fIntervals[0],
+ args.fIntervals[1], draws[i].fStrokeWidth, capType,
+ gp->getVertexStride());
+ } else {
+ SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
+ SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
+ setup_dashed_rect_pos(rects[rectIndex], curVIdx, args.fSrcRotInv, verts);
+ }
+
+ curVIdx += 4;
}
+ rectIndex++;
+
+ if (draws[i].fHasEndRect) {
+ if (fullDash) {
+ setup_dashed_rect(rects[rectIndex], vertices, curVIdx, args.fSrcRotInv,
+ draws[i].fStartOffset, draws[i].fDevBloat, args.fIntervals[0],
+ draws[i].fHalfDevStroke, args.fIntervals[0],
+ args.fIntervals[1], draws[i].fStrokeWidth, capType,
+ gp->getVertexStride());
+ } else {
+ SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
+ SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
+ setup_dashed_rect_pos(rects[rectIndex], curVIdx, args.fSrcRotInv, verts);
+ }
+ curVIdx += 4;
+ }
+ rectIndex++;
}
- }
- if (startAdj != 0) {
- srcPhase = 0;
+ const GrIndexBuffer* dashIndexBuffer = batchTarget->quadIndexBuffer();
+
+ GrDrawTarget::DrawInfo drawInfo;
+ drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
+ drawInfo.setStartVertex(0);
+ drawInfo.setStartIndex(0);
+ drawInfo.setVerticesPerInstance(kVertsPerDash);
+ drawInfo.setIndicesPerInstance(kIndicesPerDash);
+ drawInfo.adjustStartVertex(firstVertex);
+ drawInfo.setVertexBuffer(vertexBuffer);
+ drawInfo.setIndexBuffer(dashIndexBuffer);
+
+ int maxInstancesPerDraw = dashIndexBuffer->maxQuads();
+ while (totalRectCount) {
+ drawInfo.setInstanceCount(SkTMin(totalRectCount, maxInstancesPerDraw));
+ drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
+ drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
+
+ batchTarget->draw(drawInfo);
+
+ drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
+ totalRectCount -= drawInfo.instanceCount();
+ }
}
- // Change the dashing info from src space into device space
- SkScalar devIntervals[2];
- devIntervals[0] = info.fIntervals[0] * parallelScale;
- devIntervals[1] = info.fIntervals[1] * parallelScale;
- SkScalar devPhase = srcPhase * parallelScale;
- SkScalar strokeWidth = srcStrokeWidth * perpScale;
+ SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
+
+private:
+ DashBatch(const Geometry& geometry, SkPaint::Cap cap, bool useAA, bool fullDash) {
+ this->initClassID<DashBatch>();
+ fGeoData.push_back(geometry);
- if ((strokeWidth < 1.f && !useAA) || 0.f == strokeWidth) {
- strokeWidth = 1.f;
+ fBatch.fUseAA = useAA;
+ fBatch.fCap = cap;
+ fBatch.fFullDash = fullDash;
}
- SkScalar halfDevStroke = strokeWidth * 0.5f;
+ bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+ DashBatch* that = t->cast<DashBatch>();
- if (SkPaint::kSquare_Cap == cap && 0 != srcStrokeWidth) {
- // add cap to on interveal and remove from off interval
- devIntervals[0] += strokeWidth;
- devIntervals[1] -= strokeWidth;
- }
- SkScalar startOffset = devIntervals[1] * 0.5f + devPhase;
-
- SkScalar bloatX = useAA ? 0.5f / parallelScale : 0.f;
- SkScalar bloatY = useAA ? 0.5f / perpScale : 0.f;
-
- SkScalar devBloat = useAA ? 0.5f : 0.f;
-
- if (devIntervals[1] <= 0.f && useAA) {
- // Case when we end up drawing a solid AA rect
- // Reset the start rect to draw this single solid rect
- // but it requires to upload a new intervals uniform so we can mimic
- // one giant dash
- ptsRot[0].fX -= hasStartRect ? startAdj : 0;
- ptsRot[1].fX += hasEndRect ? endAdj : 0;
- startRect.set(ptsRot, 2);
- startRect.outset(strokeAdj, halfSrcStroke);
- hasStartRect = true;
- hasEndRect = false;
- lineDone = true;
-
- SkPoint devicePts[2];
- viewMatrix.mapPoints(devicePts, ptsRot, 2);
- SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
- if (hasCap) {
- lineLength += 2.f * halfDevStroke;
+ if (this->useAA() != that->useAA()) {
+ return false;
}
- devIntervals[0] = lineLength;
- }
- // reset to device coordinates
- SkMatrix invert;
- if (!viewMatrix.invert(&invert)) {
- SkDebugf("Failed to invert\n");
- return false;
- }
+ if (this->fullDash() != that->fullDash()) {
+ return false;
+ }
- bool isRoundCap = SkPaint::kRound_Cap == cap;
- DashCap capType = isRoundCap ? kRound_DashCap : kNonRound_DashCap;
-
- SkAutoTUnref<const GrGeometryProcessor> gp;
- bool fullDash = devIntervals[1] > 0.f || useAA;
- if (fullDash) {
- SkPathEffect::DashInfo devInfo;
- devInfo.fPhase = devPhase;
- devInfo.fCount = 2;
- devInfo.fIntervals = devIntervals;
- GrPrimitiveEdgeType edgeType = useAA ? kFillAA_GrProcessorEdgeType :
- kFillBW_GrProcessorEdgeType;
- gp.reset(create_dash_gp(color, edgeType, capType, invert));
- } else {
- // Set up the vertex data for the line and start/end dashes
- gp.reset(GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType,
- color,
- SkMatrix::I(),
- invert));
+ if (this->cap() != that->cap()) {
+ return false;
+ }
+
+ // TODO vertex color
+ if (this->color() != that->color()) {
+ return false;
+ }
+
+ SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
+ if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
+ return false;
+ }
+
+ fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+ return true;
}
- int totalRectCnt = 0;
+ GrColor color() const { return fBatch.fColor; }
+ bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
+ const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
+ bool useAA() const { return fBatch.fUseAA; }
+ bool fullDash() const { return fBatch.fFullDash; }
+ SkPaint::Cap cap() const { return fBatch.fCap; }
+
+ struct BatchTracker {
+ GrColor fColor;
+ bool fUsesLocalCoords;
+ bool fColorIgnored;
+ bool fCoverageIgnored;
+ SkPaint::Cap fCap;
+ bool fUseAA;
+ bool fFullDash;
+ };
+
+ static const int kVertsPerDash = 4;
+ static const int kIndicesPerDash = 6;
+
+ BatchTracker fBatch;
+ SkSTArray<1, Geometry, true> fGeoData;
+};
- totalRectCnt += !lineDone ? 1 : 0;
- totalRectCnt += hasStartRect ? 1 : 0;
- totalRectCnt += hasEndRect ? 1 : 0;
- GrDrawTarget::AutoReleaseGeometry geo(target,
- totalRectCnt * 4,
- gp->getVertexStride(), 0);
- if (!geo.succeeded()) {
- SkDebugf("Failed to get space for vertices!\n");
+bool GrDashingEffect::DrawDashLine(GrGpu* gpu, GrDrawTarget* target,
+ GrPipelineBuilder* pipelineBuilder, GrColor color,
+ const SkMatrix& viewMatrix, const SkPoint pts[2],
+ const GrPaint& paint, const GrStrokeInfo& strokeInfo) {
+ if (!can_fast_path_dash(pts, strokeInfo, *target, *pipelineBuilder, viewMatrix)) {
return false;
}
- int curVIdx = 0;
+ const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo();
- if (SkPaint::kRound_Cap == cap && 0 != srcStrokeWidth) {
- // need to adjust this for round caps to correctly set the dashPos attrib on vertices
- startOffset -= halfDevStroke;
- }
+ SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap();
- // Draw interior part of dashed line
- if (!lineDone) {
- SkPoint devicePts[2];
- viewMatrix.mapPoints(devicePts, ptsRot, 2);
- SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
- if (hasCap) {
- lineLength += 2.f * halfDevStroke;
- }
+ DashBatch::Geometry geometry;
+ geometry.fSrcStrokeWidth = strokeInfo.getStrokeRec().getWidth();
- SkRect bounds;
- bounds.set(ptsRot[0].fX, ptsRot[0].fY, ptsRot[1].fX, ptsRot[1].fY);
- bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke);
- if (fullDash) {
- setup_dashed_rect(bounds, geo.vertices(), curVIdx, combinedMatrix, startOffset,
- devBloat, lineLength, halfDevStroke, devIntervals[0], devIntervals[1],
- strokeWidth, capType, gp->getVertexStride());
- } else {
- SkPoint* verts = reinterpret_cast<SkPoint*>(geo.vertices());
- SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
- setup_dashed_rect_pos(bounds, curVIdx, combinedMatrix, verts);
+ // the phase should be normalized to be [0, sum of all intervals)
+ SkASSERT(info.fPhase >= 0 && info.fPhase < info.fIntervals[0] + info.fIntervals[1]);
+
+ // Rotate the src pts so they are aligned horizontally with pts[0].fX < pts[1].fX
+ if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) {
+ SkMatrix rotMatrix;
+ align_to_x_axis(pts, &rotMatrix, geometry.fPtsRot);
+ if(!rotMatrix.invert(&geometry.fSrcRotInv)) {
+ SkDebugf("Failed to create invertible rotation matrix!\n");
+ return false;
}
- curVIdx += 4;
+ } else {
+ geometry.fSrcRotInv.reset();
+ memcpy(geometry.fPtsRot, pts, 2 * sizeof(SkPoint));
}
- if (hasStartRect) {
- SkASSERT(useAA); // so that we know bloatX and bloatY have been set
- startRect.outset(bloatX, bloatY);
- if (fullDash) {
- setup_dashed_rect(startRect, geo.vertices(), curVIdx, combinedMatrix, startOffset,
- devBloat, devIntervals[0], halfDevStroke, devIntervals[0],
- devIntervals[1], strokeWidth, capType, gp->getVertexStride());
- } else {
- SkPoint* verts = reinterpret_cast<SkPoint*>(geo.vertices());
- SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
- setup_dashed_rect_pos(startRect, curVIdx, combinedMatrix, verts);
- }
+ // Scale corrections of intervals and stroke from view matrix
+ calc_dash_scaling(&geometry.fParallelScale, &geometry.fPerpendicularScale, viewMatrix,
+ geometry.fPtsRot);
- curVIdx += 4;
+ SkScalar offInterval = info.fIntervals[1] * geometry.fParallelScale;
+ SkScalar strokeWidth = geometry.fSrcStrokeWidth * geometry.fPerpendicularScale;
+
+ if (SkPaint::kSquare_Cap == cap && 0 != geometry.fSrcStrokeWidth) {
+ // add cap to on interveal and remove from off interval
+ offInterval -= strokeWidth;
}
- if (hasEndRect) {
- SkASSERT(useAA); // so that we know bloatX and bloatY have been set
- endRect.outset(bloatX, bloatY);
- if (fullDash) {
- setup_dashed_rect(endRect, geo.vertices(), curVIdx, combinedMatrix, startOffset,
- devBloat, devIntervals[0], halfDevStroke, devIntervals[0],
- devIntervals[1], strokeWidth, capType, gp->getVertexStride());
- } else {
- SkPoint* verts = reinterpret_cast<SkPoint*>(geo.vertices());
- SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
- setup_dashed_rect_pos(endRect, curVIdx, combinedMatrix, verts);
- }
+ bool useAA = paint.isAntiAlias();
+ // TODO we can do a real rect call if not using fulldash(ie no off interval, not using AA)
+ bool fullDash = offInterval > 0.f || useAA;
- }
+ geometry.fColor = color;
+ geometry.fViewMatrix = viewMatrix;
+ geometry.fPhase = info.fPhase;
+ geometry.fIntervals[0] = info.fIntervals[0];
+ geometry.fIntervals[1] = info.fIntervals[1];
+
+ SkAutoTUnref<GrBatch> batch(DashBatch::Create(geometry, cap, useAA, fullDash));
+ target->drawBatch(pipelineBuilder, batch);
- target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer());
- target->drawIndexedInstances(pipelineBuilder, gp, kTriangles_GrPrimitiveType, totalRectCnt,
- 4, 6);
- target->resetIndexSource();
return true;
}
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698