Chromium Code Reviews| Index: src/gpu/GrOvalRenderer.cpp |
| diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp |
| index b1ef8d34bda013eaa2543ed7eb7f36bfd97f7da6..b39aa08b150acdb37c3b1a27db10a4df7473ea94 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" |
| @@ -692,6 +695,198 @@ 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 { |
|
bsalomon
2015/02/10 14:31:21
\n
|
| + 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 vertex color |
|
bsalomon
2015/02/10 14:31:21
unclear... use vertex colors to avoid breaking bat
|
| + 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 +899,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 +920,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 +934,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)) { |
|
bsalomon
2015/02/10 14:31:21
wonder if fin future we will need to do this inver
joshualitt
2015/02/10 15:08:08
The model I'm kind of imagining for this is basica
|
| + 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 vertex color |
| + 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 +1217,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 +1230,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 vertex color |
| + 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 +1495,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 +1503,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 +1511,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 +1617,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 +1640,434 @@ bool GrOvalRenderer::drawDRRect(GrDrawTarget* target, |
| return true; |
| } |
| +/////////////////////////////////////////////////////////////////////////////////////////////////// |
| + |
| +class RRectCircleRendererBatch : public GrBatch { |
|
bsalomon
2015/02/10 14:31:21
wondering if circular rrects can batch with circle
|
| +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 vertex color |
| + 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 vertex color |
| + 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 +2139,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 +2166,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 +2177,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 +2224,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; |
| } |