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

Unified Diff: src/gpu/GrOvalRenderer.cpp

Issue 904753002: Ovals batch (Closed) Base URL: https://skia.googlesource.com/skia.git@convex
Patch Set: feedback inc 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 | « src/gpu/GrBatchTarget.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/gpu/GrOvalRenderer.cpp
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp
index b1ef8d34bda013eaa2543ed7eb7f36bfd97f7da6..f324018f450fa016d4a6dccf5972ca3fbac464e2 100644
--- a/src/gpu/GrOvalRenderer.cpp
+++ b/src/gpu/GrOvalRenderer.cpp
@@ -7,12 +7,15 @@
#include "GrOvalRenderer.h"
-#include "GrProcessor.h"
+#include "GrBatch.h"
+#include "GrBatchTarget.h"
+#include "GrBufferAllocPool.h"
#include "GrDrawTarget.h"
#include "GrGeometryProcessor.h"
#include "GrGpu.h"
#include "GrInvariantOutput.h"
#include "GrPipelineBuilder.h"
+#include "GrProcessor.h"
#include "SkRRect.h"
#include "SkStrokeRec.h"
#include "SkTLazy.h"
@@ -22,6 +25,8 @@
#include "gl/GrGLGeometryProcessor.h"
#include "gl/builders/GrGLProgramBuilder.h"
+// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
+
namespace {
// TODO(joshualitt) add per vertex colors
struct CircleVertex {
@@ -692,6 +697,199 @@ bool GrOvalRenderer::drawOval(GrDrawTarget* target,
///////////////////////////////////////////////////////////////////////////////
+class CircleBatch : public GrBatch {
+public:
+ struct Geometry {
+ GrColor fColor;
+ SkMatrix fViewMatrix;
+ SkScalar fInnerRadius;
+ SkScalar fOuterRadius;
+ bool fStroke;
+ SkRect fDevBounds;
+ };
+
+ static GrBatch* Create(const Geometry& geometry) {
+ return SkNEW_ARGS(CircleBatch, (geometry));
+ }
+
+ const char* name() const SK_OVERRIDE { return "CircleBatch"; }
+
+ 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();
+ }
+
+ void initBatchOpt(const GrBatchOpt& batchOpt) {
+ fBatchOpt = batchOpt;
+ }
+
+ 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;
+ }
+
+ // setup batch properties
+ fBatch.fColorIgnored = init.fColorIgnored;
+ fBatch.fColor = fGeoData[0].fColor;
+ fBatch.fStroke = fGeoData[0].fStroke;
+ fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
+ fBatch.fCoverageIgnored = init.fCoverageIgnored;
+ }
+
+ void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+ SkMatrix invert;
+ if (!this->viewMatrix().invert(&invert)) {
+ return;
+ }
+
+ // Setup geometry processor
+ SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
+ this->stroke(),
+ invert));
+
+ batchTarget->initDraw(gp, pipeline);
+
+ // TODO this is hacky, but the only way we have to initialize the GP is to use the
+ // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
+ // everywhere we can remove this nastiness
+ GrPipelineInfo init;
+ init.fColorIgnored = fBatch.fColorIgnored;
+ init.fOverrideColor = GrColor_ILLEGAL;
+ init.fCoverageIgnored = fBatch.fCoverageIgnored;
+ init.fUsesLocalCoords = this->usesLocalCoords();
+ gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
+
+ int instanceCount = fGeoData.count();
+ int vertexCount = kVertsPerCircle * instanceCount;
+ size_t vertexStride = gp->getVertexStride();
+ SkASSERT(vertexStride == sizeof(CircleVertex));
+
+ const GrVertexBuffer* vertexBuffer;
+ int firstVertex;
+
+ void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
+ vertexCount,
+ &vertexBuffer,
+ &firstVertex);
+
+ CircleVertex* verts = reinterpret_cast<CircleVertex*>(vertices);
+
+ for (int i = 0; i < instanceCount; i++) {
+ Geometry& args = fGeoData[i];
+
+ SkScalar innerRadius = args.fInnerRadius;
+ SkScalar outerRadius = args.fOuterRadius;
+
+ const SkRect& bounds = args.fDevBounds;
+
+ // The inner radius in the vertex data must be specified in normalized space.
+ innerRadius = innerRadius / outerRadius;
+ verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
+ verts[0].fOffset = SkPoint::Make(-1, -1);
+ verts[0].fOuterRadius = outerRadius;
+ verts[0].fInnerRadius = innerRadius;
+
+ verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
+ verts[1].fOffset = SkPoint::Make(-1, 1);
+ verts[1].fOuterRadius = outerRadius;
+ verts[1].fInnerRadius = innerRadius;
+
+ verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
+ verts[2].fOffset = SkPoint::Make(1, 1);
+ verts[2].fOuterRadius = outerRadius;
+ verts[2].fInnerRadius = innerRadius;
+
+ verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
+ verts[3].fOffset = SkPoint::Make(1, -1);
+ verts[3].fOuterRadius = outerRadius;
+ verts[3].fInnerRadius = innerRadius;
+
+ verts += kVertsPerCircle;
+ }
+
+ const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
+
+ GrDrawTarget::DrawInfo drawInfo;
+ drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
+ drawInfo.setStartVertex(0);
+ drawInfo.setStartIndex(0);
+ drawInfo.setVerticesPerInstance(kVertsPerCircle);
+ drawInfo.setIndicesPerInstance(kIndicesPerCircle);
+ drawInfo.adjustStartVertex(firstVertex);
+ drawInfo.setVertexBuffer(vertexBuffer);
+ drawInfo.setIndexBuffer(quadIndexBuffer);
+
+ int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
+
+ while (instanceCount) {
+ drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
+ drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
+ drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
+
+ batchTarget->draw(drawInfo);
+
+ drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
+ instanceCount -= drawInfo.instanceCount();
+ }
+ }
+
+ SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
+
+private:
+ CircleBatch(const Geometry& geometry) {
+ this->initClassID<CircleBatch>();
+ fGeoData.push_back(geometry);
+ }
+
+ bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+ CircleBatch* that = t->cast<CircleBatch>();
+
+ // TODO use vertex color to avoid breaking batches
+ if (this->color() != that->color()) {
+ return false;
+ }
+
+ if (this->stroke() != that->stroke()) {
+ 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;
+ }
+
+ GrColor color() const { return fBatch.fColor; }
+ bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
+ const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
+ bool stroke() const { return fBatch.fStroke; }
+
+ struct BatchTracker {
+ GrColor fColor;
+ bool fStroke;
+ bool fUsesLocalCoords;
+ bool fColorIgnored;
+ bool fCoverageIgnored;
+ };
+
+ static const int kVertsPerCircle = 4;
+ static const int kIndicesPerCircle = 6;
+
+ GrBatchOpt fBatchOpt;
+ BatchTracker fBatch;
+ SkSTArray<1, Geometry, true> fGeoData;
+};
+
void GrOvalRenderer::drawCircle(GrDrawTarget* target,
GrPipelineBuilder* pipelineBuilder,
GrColor color,
@@ -704,11 +902,6 @@ void GrOvalRenderer::drawCircle(GrDrawTarget* target,
SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
- SkMatrix invert;
- if (!viewMatrix.invert(&invert)) {
- return;
- }
-
SkStrokeRec::Style style = stroke.getStyle();
bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
SkStrokeRec::kHairline_Style == style;
@@ -730,18 +923,6 @@ void GrOvalRenderer::drawCircle(GrDrawTarget* target,
}
}
- SkAutoTUnref<GrGeometryProcessor> gp(
- CircleEdgeEffect::Create(color, isStrokeOnly && innerRadius > 0,invert));
-
- GrDrawTarget::AutoReleaseGeometry geo(target, 4, gp->getVertexStride(), 0);
- SkASSERT(gp->getVertexStride() == sizeof(CircleVertex));
- if (!geo.succeeded()) {
- SkDebugf("Failed to get space for vertices!\n");
- return;
- }
-
- CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
-
// The radii are outset for two reasons. First, it allows the shader to simply perform simpler
// computation because the computed alpha is zero, rather than 50%, at the radius.
// Second, the outer radius is used to compute the verts of the bounding box that is rendered
@@ -756,35 +937,220 @@ void GrOvalRenderer::drawCircle(GrDrawTarget* target,
center.fY + outerRadius
);
- // The inner radius in the vertex data must be specified in normalized space.
- innerRadius = innerRadius / outerRadius;
- verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
- verts[0].fOffset = SkPoint::Make(-1, -1);
- verts[0].fOuterRadius = outerRadius;
- verts[0].fInnerRadius = innerRadius;
-
- verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
- verts[1].fOffset = SkPoint::Make(-1, 1);
- verts[1].fOuterRadius = outerRadius;
- verts[1].fInnerRadius = innerRadius;
-
- verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
- verts[2].fOffset = SkPoint::Make(1, 1);
- verts[2].fOuterRadius = outerRadius;
- verts[2].fInnerRadius = innerRadius;
-
- verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
- verts[3].fOffset = SkPoint::Make(1, -1);
- verts[3].fOuterRadius = outerRadius;
- verts[3].fInnerRadius = innerRadius;
-
- target->setIndexSourceToBuffer(fGpu->getQuadIndexBuffer());
- target->drawIndexedInstances(pipelineBuilder, gp, kTriangles_GrPrimitiveType, 1, 4, 6, &bounds);
- target->resetIndexSource();
+ CircleBatch::Geometry geometry;
+ geometry.fViewMatrix = viewMatrix;
+ geometry.fColor = color;
+ geometry.fInnerRadius = innerRadius;
+ geometry.fOuterRadius = outerRadius;
+ geometry.fStroke = isStrokeOnly && innerRadius > 0;
+ geometry.fDevBounds = bounds;
+
+ SkAutoTUnref<GrBatch> batch(CircleBatch::Create(geometry));
+ target->drawBatch(pipelineBuilder, batch, &bounds);
}
///////////////////////////////////////////////////////////////////////////////
+class EllipseBatch : public GrBatch {
+public:
+ struct Geometry {
+ GrColor fColor;
+ SkMatrix fViewMatrix;
+ SkScalar fXRadius;
+ SkScalar fYRadius;
+ SkScalar fInnerXRadius;
+ SkScalar fInnerYRadius;
+ bool fStroke;
+ SkRect fDevBounds;
+ };
+
+ static GrBatch* Create(const Geometry& geometry) {
+ return SkNEW_ARGS(EllipseBatch, (geometry));
+ }
+
+ const char* name() const SK_OVERRIDE { return "EllipseBatch"; }
+
+ 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();
+ }
+
+ void initBatchOpt(const GrBatchOpt& batchOpt) {
+ fBatchOpt = batchOpt;
+ }
+
+ 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;
+ }
+
+ // setup batch properties
+ fBatch.fColorIgnored = init.fColorIgnored;
+ fBatch.fColor = fGeoData[0].fColor;
+ fBatch.fStroke = fGeoData[0].fStroke;
+ fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
+ fBatch.fCoverageIgnored = init.fCoverageIgnored;
+ }
+
+ void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+ SkMatrix invert;
+ if (!this->viewMatrix().invert(&invert)) {
+ return;
+ }
+
+ // Setup geometry processor
+ SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
+ this->stroke(),
+ invert));
+ SkASSERT(gp->getVertexStride() == sizeof(EllipseVertex));
+
+ batchTarget->initDraw(gp, pipeline);
+
+ // TODO this is hacky, but the only way we have to initialize the GP is to use the
+ // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
+ // everywhere we can remove this nastiness
+ GrPipelineInfo init;
+ init.fColorIgnored = fBatch.fColorIgnored;
+ init.fOverrideColor = GrColor_ILLEGAL;
+ init.fCoverageIgnored = fBatch.fCoverageIgnored;
+ init.fUsesLocalCoords = this->usesLocalCoords();
+ gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
+
+ int instanceCount = fGeoData.count();
+ int vertexCount = kVertsPerEllipse * instanceCount;
+ size_t vertexStride = gp->getVertexStride();
+ SkASSERT(vertexStride == sizeof(CircleVertex));
+
+ const GrVertexBuffer* vertexBuffer;
+ int firstVertex;
+
+ void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
+ vertexCount,
+ &vertexBuffer,
+ &firstVertex);
+
+ EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(vertices);
+
+ for (int i = 0; i < instanceCount; i++) {
+ Geometry& args = fGeoData[i];
+
+ SkScalar xRadius = args.fXRadius;
+ SkScalar yRadius = args.fYRadius;
+
+ // Compute the reciprocals of the radii here to save time in the shader
+ SkScalar xRadRecip = SkScalarInvert(xRadius);
+ SkScalar yRadRecip = SkScalarInvert(yRadius);
+ SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
+ SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
+
+ const SkRect& bounds = args.fDevBounds;
+
+ // The inner radius in the vertex data must be specified in normalized space.
+ verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
+ verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
+ verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
+ verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
+
+ verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
+ verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
+ verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
+ verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
+
+ verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
+ verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
+ verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
+ verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
+
+ verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
+ verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
+ verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
+ verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
+
+ verts += kVertsPerEllipse;
+ }
+
+ const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
+
+ GrDrawTarget::DrawInfo drawInfo;
+ drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
+ drawInfo.setStartVertex(0);
+ drawInfo.setStartIndex(0);
+ drawInfo.setVerticesPerInstance(kVertsPerEllipse);
+ drawInfo.setIndicesPerInstance(kIndicesPerEllipse);
+ drawInfo.adjustStartVertex(firstVertex);
+ drawInfo.setVertexBuffer(vertexBuffer);
+ drawInfo.setIndexBuffer(quadIndexBuffer);
+
+ int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
+
+ while (instanceCount) {
+ drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
+ drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
+ drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
+
+ batchTarget->draw(drawInfo);
+
+ drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
+ instanceCount -= drawInfo.instanceCount();
+ }
+ }
+
+ SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
+
+private:
+ EllipseBatch(const Geometry& geometry) {
+ this->initClassID<EllipseBatch>();
+ fGeoData.push_back(geometry);
+ }
+
+ bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+ EllipseBatch* that = t->cast<EllipseBatch>();
+
+ // TODO use vertex color to avoid breaking batches
+ if (this->color() != that->color()) {
+ return false;
+ }
+
+ if (this->stroke() != that->stroke()) {
+ 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;
+ }
+
+ GrColor color() const { return fBatch.fColor; }
+ bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
+ const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
+ bool stroke() const { return fBatch.fStroke; }
+
+ struct BatchTracker {
+ GrColor fColor;
+ bool fStroke;
+ bool fUsesLocalCoords;
+ bool fColorIgnored;
+ bool fCoverageIgnored;
+ };
+
+ static const int kVertsPerEllipse = 4;
+ static const int kIndicesPerEllipse = 6;
+
+ GrBatchOpt fBatchOpt;
+ BatchTracker fBatch;
+ SkSTArray<1, Geometry, true> fGeoData;
+};
+
bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
GrPipelineBuilder* pipelineBuilder,
GrColor color,
@@ -854,31 +1220,6 @@ bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
yRadius += scaledStroke.fY;
}
- SkMatrix invert;
- if (!viewMatrix.invert(&invert)) {
- return false;
- }
-
- SkAutoTUnref<GrGeometryProcessor> gp(
- EllipseEdgeEffect::Create(color,
- isStrokeOnly && innerXRadius > 0 && innerYRadius > 0,
- invert));
-
- GrDrawTarget::AutoReleaseGeometry geo(target, 4, gp->getVertexStride(), 0);
- SkASSERT(gp->getVertexStride() == sizeof(EllipseVertex));
- if (!geo.succeeded()) {
- SkDebugf("Failed to get space for vertices!\n");
- return false;
- }
-
- EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
-
- // Compute the reciprocals of the radii here to save time in the shader
- SkScalar xRadRecip = SkScalarInvert(xRadius);
- SkScalar yRadRecip = SkScalarInvert(yRadius);
- SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius);
- SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius);
-
// We've extended the outer x radius out half a pixel to antialias.
// This will also expand the rect so all the pixels will be captured.
// TODO: Consider if we should use sqrt(2)/2 instead
@@ -892,32 +1233,217 @@ bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
center.fY + yRadius
);
- verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
- verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
- verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
- verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
+ EllipseBatch::Geometry geometry;
+ geometry.fViewMatrix = viewMatrix;
+ geometry.fColor = color;
+ geometry.fXRadius = xRadius;
+ geometry.fYRadius = yRadius;
+ geometry.fInnerXRadius = innerXRadius;
+ geometry.fInnerYRadius = innerYRadius;
+ geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
+ geometry.fDevBounds = bounds;
- verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
- verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
- verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
- verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
+ SkAutoTUnref<GrBatch> batch(EllipseBatch::Create(geometry));
+ target->drawBatch(pipelineBuilder, batch, &bounds);
- verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
- verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
- verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
- verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
+ return true;
+}
- verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
- verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
- verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
- verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
+/////////////////////////////////////////////////////////////////////////////////////////////////
- target->setIndexSourceToBuffer(fGpu->getQuadIndexBuffer());
- target->drawIndexedInstances(pipelineBuilder, gp, kTriangles_GrPrimitiveType, 1, 4, 6, &bounds);
- target->resetIndexSource();
+class DIEllipseBatch : public GrBatch {
+public:
+ struct Geometry {
+ GrColor fColor;
+ SkMatrix fViewMatrix;
+ SkScalar fXRadius;
+ SkScalar fYRadius;
+ SkScalar fInnerXRadius;
+ SkScalar fInnerYRadius;
+ SkScalar fGeoDx;
+ SkScalar fGeoDy;
+ DIEllipseEdgeEffect::Mode fMode;
+ SkRect fDevBounds;
+ };
- return true;
-}
+ static GrBatch* Create(const Geometry& geometry) {
+ return SkNEW_ARGS(DIEllipseBatch, (geometry));
+ }
+
+ const char* name() const SK_OVERRIDE { return "DIEllipseBatch"; }
+
+ 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();
+ }
+
+ void initBatchOpt(const GrBatchOpt& batchOpt) {
+ fBatchOpt = batchOpt;
+ }
+
+ 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;
+ }
+
+ // setup batch properties
+ fBatch.fColorIgnored = init.fColorIgnored;
+ fBatch.fColor = fGeoData[0].fColor;
+ fBatch.fMode = fGeoData[0].fMode;
+ fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
+ fBatch.fCoverageIgnored = init.fCoverageIgnored;
+ }
+
+ void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+ // Setup geometry processor
+ SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
+ this->viewMatrix(),
+ this->mode()));
+
+ SkASSERT(gp->getVertexStride() == sizeof(DIEllipseVertex));
+
+ batchTarget->initDraw(gp, pipeline);
+
+ // TODO this is hacky, but the only way we have to initialize the GP is to use the
+ // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
+ // everywhere we can remove this nastiness
+ GrPipelineInfo init;
+ init.fColorIgnored = fBatch.fColorIgnored;
+ init.fOverrideColor = GrColor_ILLEGAL;
+ init.fCoverageIgnored = fBatch.fCoverageIgnored;
+ init.fUsesLocalCoords = this->usesLocalCoords();
+ gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
+
+ int instanceCount = fGeoData.count();
+ int vertexCount = kVertsPerEllipse * instanceCount;
+ size_t vertexStride = gp->getVertexStride();
+ SkASSERT(vertexStride == sizeof(CircleVertex));
+
+ const GrVertexBuffer* vertexBuffer;
+ int firstVertex;
+
+ void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
+ vertexCount,
+ &vertexBuffer,
+ &firstVertex);
+
+ DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(vertices);
+
+ for (int i = 0; i < instanceCount; i++) {
+ Geometry& args = fGeoData[i];
+
+ SkScalar xRadius = args.fXRadius;
+ SkScalar yRadius = args.fYRadius;
+
+ const SkRect& bounds = args.fDevBounds;
+
+ // This adjusts the "radius" to include the half-pixel border
+ SkScalar offsetDx = SkScalarDiv(args.fGeoDx, xRadius);
+ SkScalar offsetDy = SkScalarDiv(args.fGeoDy, yRadius);
+
+ SkScalar innerRatioX = SkScalarDiv(xRadius, args.fInnerXRadius);
+ SkScalar innerRatioY = SkScalarDiv(yRadius, args.fInnerYRadius);
+
+ verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
+ verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
+ verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
+
+ verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
+ verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
+ verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
+
+ verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
+ verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
+ verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
+
+ verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
+ verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
+ verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
+
+ verts += kVertsPerEllipse;
+ }
+
+ const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
+
+ GrDrawTarget::DrawInfo drawInfo;
+ drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
+ drawInfo.setStartVertex(0);
+ drawInfo.setStartIndex(0);
+ drawInfo.setVerticesPerInstance(kVertsPerEllipse);
+ drawInfo.setIndicesPerInstance(kIndicesPerEllipse);
+ drawInfo.adjustStartVertex(firstVertex);
+ drawInfo.setVertexBuffer(vertexBuffer);
+ drawInfo.setIndexBuffer(quadIndexBuffer);
+
+ int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
+
+ while (instanceCount) {
+ drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
+ drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
+ drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
+
+ batchTarget->draw(drawInfo);
+
+ drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
+ instanceCount -= drawInfo.instanceCount();
+ }
+ }
+
+ SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
+
+private:
+ DIEllipseBatch(const Geometry& geometry) {
+ this->initClassID<DIEllipseBatch>();
+ fGeoData.push_back(geometry);
+ }
+
+ bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+ DIEllipseBatch* that = t->cast<DIEllipseBatch>();
+
+ // TODO use vertex color to avoid breaking batches
+ if (this->color() != that->color()) {
+ return false;
+ }
+
+ if (this->mode() != that->mode()) {
+ 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;
+ }
+
+ GrColor color() const { return fBatch.fColor; }
+ bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
+ const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
+ DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
+
+ struct BatchTracker {
+ GrColor fColor;
+ DIEllipseEdgeEffect::Mode fMode;
+ bool fUsesLocalCoords;
+ bool fColorIgnored;
+ bool fCoverageIgnored;
+ };
+
+ static const int kVertsPerEllipse = 4;
+ static const int kIndicesPerEllipse = 6;
+
+ GrBatchOpt fBatchOpt;
+ BatchTracker fBatch;
+ SkSTArray<1, Geometry, true> fGeoData;
+};
bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
GrPipelineBuilder* pipelineBuilder,
@@ -972,19 +1498,6 @@ bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
DIEllipseEdgeEffect::kFill;
}
- SkScalar innerRatioX = SkScalarDiv(xRadius, innerXRadius);
- SkScalar innerRatioY = SkScalarDiv(yRadius, innerYRadius);
-
- SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(color, viewMatrix, mode));
-
- GrDrawTarget::AutoReleaseGeometry geo(target, 4, gp->getVertexStride(), 0);
- SkASSERT(gp->getVertexStride() == sizeof(DIEllipseVertex));
- if (!geo.succeeded()) {
- SkDebugf("Failed to get space for vertices!\n");
- return false;
- }
-
- DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(geo.vertices());
// This expands the outer rect so that after CTM we end up with a half-pixel border
SkScalar a = viewMatrix[SkMatrix::kMScaleX];
@@ -993,9 +1506,6 @@ bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
SkScalar d = viewMatrix[SkMatrix::kMScaleY];
SkScalar geoDx = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(a*a + c*c));
SkScalar geoDy = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(b*b + d*d));
- // This adjusts the "radius" to include the half-pixel border
- SkScalar offsetDx = SkScalarDiv(geoDx, xRadius);
- SkScalar offsetDy = SkScalarDiv(geoDy, yRadius);
SkRect bounds = SkRect::MakeLTRB(
center.fX - xRadius - geoDx,
@@ -1004,25 +1514,20 @@ bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
center.fY + yRadius + geoDy
);
- verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
- verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
- verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
-
- verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
- verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
- verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
-
- verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
- verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
- verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
-
- verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
- verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
- verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
-
- target->setIndexSourceToBuffer(fGpu->getQuadIndexBuffer());
- target->drawIndexedInstances(pipelineBuilder, gp, kTriangles_GrPrimitiveType, 1, 4, 6, &bounds);
- target->resetIndexSource();
+ DIEllipseBatch::Geometry geometry;
+ geometry.fViewMatrix = viewMatrix;
+ geometry.fColor = color;
+ geometry.fXRadius = xRadius;
+ geometry.fYRadius = yRadius;
+ geometry.fInnerXRadius = innerXRadius;
+ geometry.fInnerYRadius = innerYRadius;
+ geometry.fGeoDx = geoDx;
+ geometry.fGeoDy = geoDy;
+ geometry.fMode = mode;
+ geometry.fDevBounds = bounds;
+
+ SkAutoTUnref<GrBatch> batch(DIEllipseBatch::Create(geometry));
+ target->drawBatch(pipelineBuilder, batch, &bounds);
return true;
}
@@ -1115,7 +1620,7 @@ bool GrOvalRenderer::drawDRRect(GrDrawTarget* target,
}
}
GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
- kFillBW_GrProcessorEdgeType;
+ kFillBW_GrProcessorEdgeType;
GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
if (NULL == effect) {
return false;
@@ -1138,6 +1643,434 @@ bool GrOvalRenderer::drawDRRect(GrDrawTarget* target,
return true;
}
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class RRectCircleRendererBatch : public GrBatch {
+public:
+ struct Geometry {
+ GrColor fColor;
+ SkMatrix fViewMatrix;
+ SkScalar fInnerRadius;
+ SkScalar fOuterRadius;
+ bool fStroke;
+ SkRect fDevBounds;
+ };
+
+ static GrBatch* Create(const Geometry& geometry, const GrIndexBuffer* indexBuffer) {
+ return SkNEW_ARGS(RRectCircleRendererBatch, (geometry, indexBuffer));
+ }
+
+ const char* name() const SK_OVERRIDE { return "RRectCircleBatch"; }
+
+ 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();
+ }
+
+ void initBatchOpt(const GrBatchOpt& batchOpt) {
+ fBatchOpt = batchOpt;
+ }
+
+ 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;
+ }
+
+ // setup batch properties
+ fBatch.fColorIgnored = init.fColorIgnored;
+ fBatch.fColor = fGeoData[0].fColor;
+ fBatch.fStroke = fGeoData[0].fStroke;
+ fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
+ fBatch.fCoverageIgnored = init.fCoverageIgnored;
+ }
+
+ void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+ // reset to device coordinates
+ SkMatrix invert;
+ if (!this->viewMatrix().invert(&invert)) {
+ SkDebugf("Failed to invert\n");
+ return;
+ }
+
+ // Setup geometry processor
+ SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
+ this->stroke(),
+ invert));
+
+ batchTarget->initDraw(gp, pipeline);
+
+ // TODO this is hacky, but the only way we have to initialize the GP is to use the
+ // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
+ // everywhere we can remove this nastiness
+ GrPipelineInfo init;
+ init.fColorIgnored = fBatch.fColorIgnored;
+ init.fOverrideColor = GrColor_ILLEGAL;
+ init.fCoverageIgnored = fBatch.fCoverageIgnored;
+ init.fUsesLocalCoords = this->usesLocalCoords();
+ gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
+
+ int instanceCount = fGeoData.count();
+ int vertexCount = kVertsPerRRect * instanceCount;
+ size_t vertexStride = gp->getVertexStride();
+ SkASSERT(vertexStride == sizeof(CircleVertex));
+
+ const GrVertexBuffer* vertexBuffer;
+ int firstVertex;
+
+ void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
+ vertexCount,
+ &vertexBuffer,
+ &firstVertex);
+
+ CircleVertex* verts = reinterpret_cast<CircleVertex*>(vertices);
+
+ for (int i = 0; i < instanceCount; i++) {
+ Geometry& args = fGeoData[i];
+
+ SkScalar outerRadius = args.fOuterRadius;
+
+ const SkRect& bounds = args.fDevBounds;
+
+ SkScalar yCoords[4] = {
+ bounds.fTop,
+ bounds.fTop + outerRadius,
+ bounds.fBottom - outerRadius,
+ bounds.fBottom
+ };
+
+ SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
+ // The inner radius in the vertex data must be specified in normalized space.
+ SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
+ for (int i = 0; i < 4; ++i) {
+ verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
+ verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
+ verts->fOuterRadius = outerRadius;
+ verts->fInnerRadius = innerRadius;
+ verts++;
+
+ verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
+ verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
+ verts->fOuterRadius = outerRadius;
+ verts->fInnerRadius = innerRadius;
+ verts++;
+
+ verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
+ verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
+ verts->fOuterRadius = outerRadius;
+ verts->fInnerRadius = innerRadius;
+ verts++;
+
+ verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
+ verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
+ verts->fOuterRadius = outerRadius;
+ verts->fInnerRadius = innerRadius;
+ verts++;
+ }
+ }
+
+ // drop out the middle quad if we're stroked
+ int indexCnt = this->stroke() ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
+ SK_ARRAY_COUNT(gRRectIndices);
+
+
+ GrDrawTarget::DrawInfo drawInfo;
+ drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
+ drawInfo.setStartVertex(0);
+ drawInfo.setStartIndex(0);
+ drawInfo.setVerticesPerInstance(kVertsPerRRect);
+ drawInfo.setIndicesPerInstance(indexCnt);
+ drawInfo.adjustStartVertex(firstVertex);
+ drawInfo.setVertexBuffer(vertexBuffer);
+ drawInfo.setIndexBuffer(fIndexBuffer);
+
+ int maxInstancesPerDraw = kNumRRectsInIndexBuffer;
+
+ while (instanceCount) {
+ drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
+ drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
+ drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
+
+ batchTarget->draw(drawInfo);
+
+ drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
+ instanceCount -= drawInfo.instanceCount();
+ }
+ }
+
+ SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
+
+private:
+ RRectCircleRendererBatch(const Geometry& geometry, const GrIndexBuffer* indexBuffer)
+ : fIndexBuffer(indexBuffer) {
+ this->initClassID<RRectCircleRendererBatch>();
+ fGeoData.push_back(geometry);
+ }
+
+ bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+ RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
+
+ // TODO use vertex color to avoid breaking batches
+ if (this->color() != that->color()) {
+ return false;
+ }
+
+ if (this->stroke() != that->stroke()) {
+ 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;
+ }
+
+ GrColor color() const { return fBatch.fColor; }
+ bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
+ const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
+ bool stroke() const { return fBatch.fStroke; }
+
+ struct BatchTracker {
+ GrColor fColor;
+ bool fStroke;
+ bool fUsesLocalCoords;
+ bool fColorIgnored;
+ bool fCoverageIgnored;
+ };
+
+ GrBatchOpt fBatchOpt;
+ BatchTracker fBatch;
+ SkSTArray<1, Geometry, true> fGeoData;
+ const GrIndexBuffer* fIndexBuffer;
+};
+
+class RRectEllipseRendererBatch : public GrBatch {
+public:
+ struct Geometry {
+ GrColor fColor;
+ SkMatrix fViewMatrix;
+ SkScalar fXRadius;
+ SkScalar fYRadius;
+ SkScalar fInnerXRadius;
+ SkScalar fInnerYRadius;
+ bool fStroke;
+ SkRect fDevBounds;
+ };
+
+ static GrBatch* Create(const Geometry& geometry, const GrIndexBuffer* indexBuffer) {
+ return SkNEW_ARGS(RRectEllipseRendererBatch, (geometry, indexBuffer));
+ }
+
+ const char* name() const SK_OVERRIDE { return "RRectEllipseRendererBatch"; }
+
+ 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();
+ }
+
+ void initBatchOpt(const GrBatchOpt& batchOpt) {
+ fBatchOpt = batchOpt;
+ }
+
+ 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;
+ }
+
+ // setup batch properties
+ fBatch.fColorIgnored = init.fColorIgnored;
+ fBatch.fColor = fGeoData[0].fColor;
+ fBatch.fStroke = fGeoData[0].fStroke;
+ fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
+ fBatch.fCoverageIgnored = init.fCoverageIgnored;
+ }
+
+ void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+ // reset to device coordinates
+ SkMatrix invert;
+ if (!this->viewMatrix().invert(&invert)) {
+ SkDebugf("Failed to invert\n");
+ return;
+ }
+
+ // Setup geometry processor
+ SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
+ this->stroke(),
+ invert));
+
+ batchTarget->initDraw(gp, pipeline);
+
+ // TODO this is hacky, but the only way we have to initialize the GP is to use the
+ // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
+ // everywhere we can remove this nastiness
+ GrPipelineInfo init;
+ init.fColorIgnored = fBatch.fColorIgnored;
+ init.fOverrideColor = GrColor_ILLEGAL;
+ init.fCoverageIgnored = fBatch.fCoverageIgnored;
+ init.fUsesLocalCoords = this->usesLocalCoords();
+ gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
+
+ int instanceCount = fGeoData.count();
+ int vertexCount = kVertsPerRRect * instanceCount;
+ size_t vertexStride = gp->getVertexStride();
+ SkASSERT(vertexStride == sizeof(EllipseVertex));
+
+ const GrVertexBuffer* vertexBuffer;
+ int firstVertex;
+
+ void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
+ vertexCount,
+ &vertexBuffer,
+ &firstVertex);
+
+ EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(vertices);
+
+ for (int i = 0; i < instanceCount; i++) {
+ Geometry& args = fGeoData[i];
+
+ // Compute the reciprocals of the radii here to save time in the shader
+ SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
+ SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
+ SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
+ SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
+
+ // Extend the radii out half a pixel to antialias.
+ SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
+ SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
+
+ const SkRect& bounds = args.fDevBounds;
+
+ SkScalar yCoords[4] = {
+ bounds.fTop,
+ bounds.fTop + yOuterRadius,
+ bounds.fBottom - yOuterRadius,
+ bounds.fBottom
+ };
+ SkScalar yOuterOffsets[4] = {
+ yOuterRadius,
+ SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
+ SK_ScalarNearlyZero,
+ yOuterRadius
+ };
+
+ for (int i = 0; i < 4; ++i) {
+ verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
+ verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
+ verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
+ verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
+ verts++;
+
+ verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
+ verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
+ verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
+ verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
+ verts++;
+
+ verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
+ verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
+ verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
+ verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
+ verts++;
+
+ verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
+ verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
+ verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
+ verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
+ verts++;
+ }
+ }
+
+ // drop out the middle quad if we're stroked
+ int indexCnt = this->stroke() ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
+ SK_ARRAY_COUNT(gRRectIndices);
+
+ GrDrawTarget::DrawInfo drawInfo;
+ drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
+ drawInfo.setStartVertex(0);
+ drawInfo.setStartIndex(0);
+ drawInfo.setVerticesPerInstance(kVertsPerRRect);
+ drawInfo.setIndicesPerInstance(indexCnt);
+ drawInfo.adjustStartVertex(firstVertex);
+ drawInfo.setVertexBuffer(vertexBuffer);
+ drawInfo.setIndexBuffer(fIndexBuffer);
+
+ int maxInstancesPerDraw = kNumRRectsInIndexBuffer;
+
+ while (instanceCount) {
+ drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
+ drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
+ drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
+
+ batchTarget->draw(drawInfo);
+
+ drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
+ instanceCount -= drawInfo.instanceCount();
+ }
+ }
+
+ SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
+
+private:
+ RRectEllipseRendererBatch(const Geometry& geometry, const GrIndexBuffer* indexBuffer)
+ : fIndexBuffer(indexBuffer) {
+ this->initClassID<RRectEllipseRendererBatch>();
+ fGeoData.push_back(geometry);
+ }
+
+ bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+ RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
+
+ // TODO use vertex color to avoid breaking batches
+ if (this->color() != that->color()) {
+ return false;
+ }
+
+ if (this->stroke() != that->stroke()) {
+ 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;
+ }
+
+ GrColor color() const { return fBatch.fColor; }
+ bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
+ const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
+ bool stroke() const { return fBatch.fStroke; }
+
+ struct BatchTracker {
+ GrColor fColor;
+ bool fStroke;
+ bool fUsesLocalCoords;
+ bool fColorIgnored;
+ bool fCoverageIgnored;
+ };
+
+ GrBatchOpt fBatchOpt;
+ BatchTracker fBatch;
+ SkSTArray<1, Geometry, true> fGeoData;
+ const GrIndexBuffer* fIndexBuffer;
+};
+
bool GrOvalRenderer::drawRRect(GrDrawTarget* target,
GrPipelineBuilder* pipelineBuilder,
GrColor color,
@@ -1209,13 +2142,6 @@ bool GrOvalRenderer::drawRRect(GrDrawTarget* target,
return false;
}
- // reset to device coordinates
- SkMatrix invert;
- if (!viewMatrix.invert(&invert)) {
- SkDebugf("Failed to invert\n");
- return false;
- }
-
GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(isStrokeOnly);
if (NULL == indexBuffer) {
SkDebugf("Failed to create index buffer!\n");
@@ -1243,18 +2169,6 @@ bool GrOvalRenderer::drawRRect(GrDrawTarget* target,
isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
- SkAutoTUnref<GrGeometryProcessor> effect(CircleEdgeEffect::Create(color,
- isStrokeOnly,
- invert));
-
- GrDrawTarget::AutoReleaseGeometry geo(target, 16, effect->getVertexStride(), 0);
- SkASSERT(effect->getVertexStride() == sizeof(CircleVertex));
- if (!geo.succeeded()) {
- SkDebugf("Failed to get space for vertices!\n");
- return false;
- }
- CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
-
// The radii are outset for two reasons. First, it allows the shader to simply perform
// simpler computation because the computed alpha is zero, rather than 50%, at the radius.
// Second, the outer radius is used to compute the verts of the bounding box that is
@@ -1266,47 +2180,16 @@ bool GrOvalRenderer::drawRRect(GrDrawTarget* target,
// Expand the rect so all the pixels will be captured.
bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
- SkScalar yCoords[4] = {
- bounds.fTop,
- bounds.fTop + outerRadius,
- bounds.fBottom - outerRadius,
- bounds.fBottom
- };
- SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
- // The inner radius in the vertex data must be specified in normalized space.
- innerRadius = innerRadius / outerRadius;
- for (int i = 0; i < 4; ++i) {
- verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
- verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
- verts->fOuterRadius = outerRadius;
- verts->fInnerRadius = innerRadius;
- verts++;
-
- verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
- verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
- verts->fOuterRadius = outerRadius;
- verts->fInnerRadius = innerRadius;
- verts++;
-
- verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
- verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
- verts->fOuterRadius = outerRadius;
- verts->fInnerRadius = innerRadius;
- verts++;
-
- verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
- verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
- verts->fOuterRadius = outerRadius;
- verts->fInnerRadius = innerRadius;
- verts++;
- }
+ RRectCircleRendererBatch::Geometry geometry;
+ geometry.fViewMatrix = viewMatrix;
+ geometry.fColor = color;
+ geometry.fInnerRadius = innerRadius;
+ geometry.fOuterRadius = outerRadius;
+ geometry.fStroke = isStrokeOnly;
+ geometry.fDevBounds = bounds;
- // drop out the middle quad if we're stroked
- int indexCnt = isStrokeOnly ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
- SK_ARRAY_COUNT(gRRectIndices);
- target->setIndexSourceToBuffer(indexBuffer);
- target->drawIndexedInstances(pipelineBuilder, effect, kTriangles_GrPrimitiveType, 1, 16,
- indexCnt, &bounds);
+ SkAutoTUnref<GrBatch> batch(RRectCircleRendererBatch::Create(geometry, indexBuffer));
+ target->drawBatch(pipelineBuilder, batch, &bounds);
// otherwise we use the ellipse renderer
} else {
@@ -1344,79 +2227,21 @@ bool GrOvalRenderer::drawRRect(GrDrawTarget* target,
isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
- SkAutoTUnref<GrGeometryProcessor> effect(EllipseEdgeEffect::Create(color,
- isStrokeOnly,
- invert));
-
- GrDrawTarget::AutoReleaseGeometry geo(target, 16, effect->getVertexStride(), 0);
- SkASSERT(effect->getVertexStride() == sizeof(EllipseVertex));
- if (!geo.succeeded()) {
- SkDebugf("Failed to get space for vertices!\n");
- return false;
- }
-
- EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
-
- // Compute the reciprocals of the radii here to save time in the shader
- SkScalar xRadRecip = SkScalarInvert(xRadius);
- SkScalar yRadRecip = SkScalarInvert(yRadius);
- SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius);
- SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius);
-
- // Extend the radii out half a pixel to antialias.
- SkScalar xOuterRadius = xRadius + SK_ScalarHalf;
- SkScalar yOuterRadius = yRadius + SK_ScalarHalf;
-
// Expand the rect so all the pixels will be captured.
bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
- SkScalar yCoords[4] = {
- bounds.fTop,
- bounds.fTop + yOuterRadius,
- bounds.fBottom - yOuterRadius,
- bounds.fBottom
- };
- SkScalar yOuterOffsets[4] = {
- yOuterRadius,
- SK_ScalarNearlyZero, // we're using inversesqrt() in the shader, so can't be exactly 0
- SK_ScalarNearlyZero,
- yOuterRadius
- };
-
- for (int i = 0; i < 4; ++i) {
- verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
- verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
- verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
- verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
- verts++;
-
- verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
- verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
- verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
- verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
- verts++;
-
- verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
- verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
- verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
- verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
- verts++;
-
- verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
- verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
- verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
- verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
- verts++;
- }
-
- // drop out the middle quad if we're stroked
- int indexCnt = isStrokeOnly ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
- SK_ARRAY_COUNT(gRRectIndices);
- target->setIndexSourceToBuffer(indexBuffer);
- target->drawIndexedInstances(pipelineBuilder, effect, kTriangles_GrPrimitiveType, 1, 16,
- indexCnt, &bounds);
+ RRectEllipseRendererBatch::Geometry geometry;
+ geometry.fViewMatrix = viewMatrix;
+ geometry.fColor = color;
+ geometry.fXRadius = xRadius;
+ geometry.fYRadius = yRadius;
+ geometry.fInnerXRadius = innerXRadius;
+ geometry.fInnerYRadius = innerYRadius;
+ geometry.fStroke = isStrokeOnly;
+ geometry.fDevBounds = bounds;
+
+ SkAutoTUnref<GrBatch> batch(RRectEllipseRendererBatch::Create(geometry, indexBuffer));
+ target->drawBatch(pipelineBuilder, batch, &bounds);
}
-
- target->resetIndexSource();
return true;
}
« no previous file with comments | « src/gpu/GrBatchTarget.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698