Batch all circular rrects together

When possible, this change will place all circular rrect
types in the same batch. If the batch is all fills, we
use the fill shader, otherwise we use the stroke shader.

GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2283973002

Review-Url: https://codereview.chromium.org/2283973002
This commit is contained in:
jvanverth 2016-08-29 10:16:40 -07:00 committed by Commit bot
parent 1748432796
commit 84839f6fb3

View File

@ -1362,7 +1362,7 @@ private:
// //
// We don't draw the center quad from the fill rect in this case. // We don't draw the center quad from the fill rect in this case.
static const uint16_t gRRectOverstrokeIndices[] = { static const uint16_t gOverstrokeRRectIndices[] = {
// overstroke quads // overstroke quads
// we place this at the beginning so that we can skip these indices when rendering normally // we place this at the beginning so that we can skip these indices when rendering normally
16, 17, 19, 16, 19, 18, 16, 17, 19, 16, 19, 18,
@ -1386,46 +1386,52 @@ static const uint16_t gRRectOverstrokeIndices[] = {
// we place this at the end so that we can ignore these indices when not rendering as filled // we place this at the end so that we can ignore these indices when not rendering as filled
5, 6, 10, 5, 10, 9, 5, 6, 10, 5, 10, 9,
}; };
// fill and standard stroke indices skip the overstroke "ring"
static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6*4;
static const uint16_t* gRRectIndices = gRRectOverstrokeIndices + 6*4; // overstroke count is arraysize minus the center indices
static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
// overstroke count is arraysize // fill count skips overstroke indices and includes center
static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectOverstrokeIndices) - 6;
static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6*4 + 6; static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6*4 + 6;
// stroke count is fill count minus center indices
static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6; static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
static const int kVertsPerStandardRRect = 16; static const int kVertsPerStandardRRect = 16;
static const int kVertsPerOverstrokeRRect = 24; static const int kVertsPerOverstrokeRRect = 24;
static const int kNumRRectsInIndexBuffer = 256;
enum RRectType { enum RRectType {
kFill_RRectType, kFill_RRectType,
kStroke_RRectType, kStroke_RRectType,
kOverstroke_RRectType kOverstroke_RRectType,
}; };
GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey); static int rrect_type_to_vert_count(RRectType type) {
GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey); static const int kTypeToVertCount[] = {
GR_DECLARE_STATIC_UNIQUE_KEY(gOverstrokeRRectOnlyIndexBufferKey); kVertsPerStandardRRect,
static const GrBuffer* ref_rrect_index_buffer(RRectType type, kVertsPerStandardRRect,
GrResourceProvider* resourceProvider) { kVertsPerOverstrokeRRect,
GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
GR_DEFINE_STATIC_UNIQUE_KEY(gOverstrokeRRectOnlyIndexBufferKey);
switch (type) {
case kFill_RRectType:
default:
return resourceProvider->findOrCreateInstancedIndexBuffer(
gRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
case kStroke_RRectType:
return resourceProvider->findOrCreateInstancedIndexBuffer(
gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
case kOverstroke_RRectType:
return resourceProvider->findOrCreateInstancedIndexBuffer(
gRRectOverstrokeIndices, kIndicesPerOverstrokeRRect, kNumRRectsInIndexBuffer,
kVertsPerOverstrokeRRect, gOverstrokeRRectOnlyIndexBufferKey);
}; };
return kTypeToVertCount[type];
}
static int rrect_type_to_index_count(RRectType type) {
static const int kTypeToIndexCount[] = {
kIndicesPerFillRRect,
kIndicesPerStrokeRRect,
kIndicesPerOverstrokeRRect,
};
return kTypeToIndexCount[type];
}
static const uint16_t* rrect_type_to_indices(RRectType type) {
static const uint16_t* kTypeToIndices[] = {
gStandardRRectIndices,
gStandardRRectIndices,
gOverstrokeRRectIndices,
};
return kTypeToIndices[type];
} }
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
@ -1445,7 +1451,7 @@ public:
SkScalar innerRadius = 0.0f; SkScalar innerRadius = 0.0f;
SkScalar outerRadius = devRadius; SkScalar outerRadius = devRadius;
SkScalar halfWidth = 0; SkScalar halfWidth = 0;
fType = kFill_RRectType; RRectType type = kFill_RRectType;
if (devStrokeWidth > 0) { if (devStrokeWidth > 0) {
if (SkScalarNearlyZero(devStrokeWidth)) { if (SkScalarNearlyZero(devStrokeWidth)) {
halfWidth = SK_ScalarHalf; halfWidth = SK_ScalarHalf;
@ -1461,7 +1467,7 @@ public:
if (devStrokeWidth <= devRect.width() && if (devStrokeWidth <= devRect.width() &&
devStrokeWidth <= devRect.height()) { devStrokeWidth <= devRect.height()) {
innerRadius = devRadius - halfWidth; innerRadius = devRadius - halfWidth;
fType = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType; type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
} }
} }
outerRadius += halfWidth; outerRadius += halfWidth;
@ -1481,7 +1487,10 @@ public:
// Expand the rect for aa to generate correct vertices. // Expand the rect for aa to generate correct vertices.
bounds.outset(SK_ScalarHalf, SK_ScalarHalf); bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
fGeoData.emplace_back(Geometry { color, innerRadius, outerRadius, bounds }); fGeoData.emplace_back(Geometry{ color, innerRadius, outerRadius, bounds, type });
fVertCount = rrect_type_to_vert_count(type);
fIndexCount = rrect_type_to_index_count(type);
fAllFill = (kFill_RRectType == type);
} }
const char* name() const override { return "RRectCircleBatch"; } const char* name() const override { return "RRectCircleBatch"; }
@ -1526,10 +1535,9 @@ private:
} }
// Setup geometry processor // Setup geometry processor
SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(kFill_RRectType != fType, SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fAllFill,
false, false, false, false,
false, localMatrix)); false, localMatrix));
struct CircleVertex { struct CircleVertex {
SkPoint fPos; SkPoint fPos;
GrColor fColor; GrColor fColor;
@ -1541,29 +1549,27 @@ private:
int instanceCount = fGeoData.count(); int instanceCount = fGeoData.count();
size_t vertexStride = gp->getVertexStride(); size_t vertexStride = gp->getVertexStride();
SkASSERT(vertexStride == sizeof(CircleVertex)); SkASSERT(sizeof(CircleVertex) == vertexStride);
// drop out the middle quad if we're stroked const GrBuffer* vertexBuffer;
int indicesPerInstance = kIndicesPerFillRRect; int firstVertex;
if (kStroke_RRectType == fType) {
indicesPerInstance = kIndicesPerStrokeRRect;
} else if (kOverstroke_RRectType == fType) {
indicesPerInstance = kIndicesPerOverstrokeRRect;
}
SkAutoTUnref<const GrBuffer> indexBuffer(
ref_rrect_index_buffer(fType, target->resourceProvider()));
InstancedHelper helper; CircleVertex* verts = (CircleVertex*) target->makeVertexSpace(vertexStride, fVertCount,
int vertexCount = (kOverstroke_RRectType == fType) ? kVertsPerOverstrokeRRect &vertexBuffer, &firstVertex);
: kVertsPerStandardRRect; if (!verts) {
CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target,
kTriangles_GrPrimitiveType, vertexStride, indexBuffer, vertexCount,
indicesPerInstance, instanceCount));
if (!verts || !indexBuffer) {
SkDebugf("Could not allocate vertices\n"); SkDebugf("Could not allocate vertices\n");
return; return;
} }
const GrBuffer* indexBuffer = nullptr;
int firstIndex = 0;
uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
if (!indices) {
SkDebugf("Could not allocate indices\n");
return;
}
int currStartVertex = 0;
for (int i = 0; i < instanceCount; i++) { for (int i = 0; i < instanceCount; i++) {
const Geometry& args = fGeoData[i]; const Geometry& args = fGeoData[i];
@ -1581,7 +1587,10 @@ private:
SkScalar yOuterRadii[4] = {-1, 0, 0, 1 }; SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
// The inner radius in the vertex data must be specified in normalized space. // The inner radius in the vertex data must be specified in normalized space.
SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius; // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
SkScalar innerRadius = args.fType != kFill_RRectType
? args.fInnerRadius / args.fOuterRadius
: -1.0f / args.fOuterRadius;
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]); verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
verts->fColor = color; verts->fColor = color;
@ -1619,11 +1628,11 @@ private:
// //
// Also, the outer offset is a constant vector pointing to the right, which // Also, the outer offset is a constant vector pointing to the right, which
// guarantees that the distance value along the outer rectangle is constant. // guarantees that the distance value along the outer rectangle is constant.
if (kOverstroke_RRectType == fType) { if (kOverstroke_RRectType == args.fType) {
SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius; SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
// this is the normalized distance from the outer rectangle of this // this is the normalized distance from the outer rectangle of this
// geometry to the outer edge // geometry to the outer edge
SkScalar maxOffset = -args.fInnerRadius/overstrokeOuterRadius; SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius;
verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[1]); verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[1]);
verts->fColor = color; verts->fColor = color;
@ -1685,9 +1694,20 @@ private:
verts->fInnerRadius = 0; verts->fInnerRadius = 0;
verts++; verts++;
} }
const uint16_t* primIndices = rrect_type_to_indices(args.fType);
const int primIndexCount = rrect_type_to_index_count(args.fType);
for (int i = 0; i < primIndexCount; ++i) {
*indices++ = primIndices[i] + currStartVertex;
}
currStartVertex += rrect_type_to_vert_count(args.fType);
} }
helper.recordDraw(target, gp); GrMesh mesh;
mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
firstIndex, fVertCount, fIndexCount);
target->draw(gp.get(), mesh);
} }
bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override { bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
@ -1697,16 +1717,15 @@ private:
return false; return false;
} }
if (fType != that->fType) {
return false;
}
if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) { if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
return false; return false;
} }
fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
this->joinBounds(*that); this->joinBounds(*that);
fVertCount += that->fVertCount;
fIndexCount += that->fIndexCount;
fAllFill = fAllFill && that->fAllFill;
return true; return true;
} }
@ -1715,15 +1734,41 @@ private:
SkScalar fInnerRadius; SkScalar fInnerRadius;
SkScalar fOuterRadius; SkScalar fOuterRadius;
SkRect fDevBounds; SkRect fDevBounds;
RRectType fType;
}; };
RRectType fType;
SkMatrix fViewMatrixIfUsingLocalCoords;
SkSTArray<1, Geometry, true> fGeoData; SkSTArray<1, Geometry, true> fGeoData;
SkMatrix fViewMatrixIfUsingLocalCoords;
int fVertCount;
int fIndexCount;
bool fAllFill;
typedef GrVertexBatch INHERITED; typedef GrVertexBatch INHERITED;
}; };
static const int kNumRRectsInIndexBuffer = 256;
GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
static const GrBuffer* ref_rrect_index_buffer(RRectType type,
GrResourceProvider* resourceProvider) {
GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
switch (type) {
case kFill_RRectType:
return resourceProvider->findOrCreateInstancedIndexBuffer(
gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
case kStroke_RRectType:
return resourceProvider->findOrCreateInstancedIndexBuffer(
gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
default:
SkASSERT(false);
return nullptr;
};
}
class RRectEllipseRendererBatch : public GrVertexBatch { class RRectEllipseRendererBatch : public GrVertexBatch {
public: public:
DEFINE_BATCH_CLASS_ID DEFINE_BATCH_CLASS_ID