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; |
} |