From 65eb4d5a210884cc92c43a8582cbd1ccbddcab57 Mon Sep 17 00:00:00 2001 From: "jvanverth@google.com" Date: Tue, 19 Mar 2013 18:51:02 +0000 Subject: [PATCH] Add stroked ovals and CircleEdgeEffect. Adds some optimizations to the circle and ellipse shaders, static effect instances for their GrEffects, and some minor changes to GrDrawState::setEffect to make GrEffect setup faster. git-svn-id: http://skia.googlecode.com/svn/trunk@8238 2bbb7eff-a529-9590-31e7-b0007b416f81 --- gyp/gpu.gypi | 2 + include/gpu/GrContext.h | 3 +- include/gpu/GrEffectStage.h | 39 ++- src/gpu/GrContext.cpp | 320 ++++++++++++++++-------- src/gpu/GrDrawState.h | 14 +- src/gpu/effects/GrCircleEdgeEffect.cpp | 87 +++++++ src/gpu/effects/GrCircleEdgeEffect.h | 65 +++++ src/gpu/effects/GrEllipseEdgeEffect.cpp | 51 +++- src/gpu/effects/GrEllipseEdgeEffect.h | 24 +- src/gpu/gl/GrGLProgram.cpp | 34 +-- tests/GLProgramsTest.cpp | 2 +- 11 files changed, 475 insertions(+), 166 deletions(-) create mode 100644 src/gpu/effects/GrCircleEdgeEffect.cpp create mode 100644 src/gpu/effects/GrCircleEdgeEffect.h diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi index d964f20a07..6b039d0636 100644 --- a/gyp/gpu.gypi +++ b/gyp/gpu.gypi @@ -122,6 +122,8 @@ '<(skia_src_path)/gpu/gr_unittests.cpp', '<(skia_src_path)/gpu/effects/Gr1DKernelEffect.h', + '<(skia_src_path)/gpu/effects/GrCircleEdgeEffect.cpp', + '<(skia_src_path)/gpu/effects/GrCircleEdgeEffect.h', '<(skia_src_path)/gpu/effects/GrConfigConversionEffect.cpp', '<(skia_src_path)/gpu/effects/GrConfigConversionEffect.h', '<(skia_src_path)/gpu/effects/GrConvolutionEffect.cpp', diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h index c5572ac3a7..79edd0b11b 100644 --- a/include/gpu/GrContext.h +++ b/include/gpu/GrContext.h @@ -894,7 +894,8 @@ private: void internalDrawPath(const GrPaint& paint, const SkPath& path, const SkStrokeRec& stroke); void internalDrawOval(const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke); - bool canDrawOval(const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke) const; + void internalDrawCircle(const GrPaint& paint, const GrRect& circle, const SkStrokeRec& stroke); + bool canDrawOval(const GrPaint& paint, const GrRect& oval, bool* isCircle) const; GrTexture* createResizedTexture(const GrTextureDesc& desc, const GrCacheID& cacheID, diff --git a/include/gpu/GrEffectStage.h b/include/gpu/GrEffectStage.h index ff118c384c..cd9a0910fc 100644 --- a/include/gpu/GrEffectStage.h +++ b/include/gpu/GrEffectStage.h @@ -116,7 +116,8 @@ public: stage.fEffectRef->get()->incDeferredRefCounts(); fEffect = stage.fEffectRef->get(); fCoordChangeMatrix = stage.fCoordChangeMatrix; - fVertexAttribIndices = stage.fVertexAttribIndices; + fVertexAttribIndices[0] = stage.fVertexAttribIndices[0]; + fVertexAttribIndices[1] = stage.fVertexAttribIndices[1]; } SkDEBUGCODE(fInitialized = true;) } @@ -127,7 +128,8 @@ public: if (NULL != fEffect) { stage->fEffectRef = GrEffect::CreateEffectRef(fEffect); stage->fCoordChangeMatrix = fCoordChangeMatrix; - stage->fVertexAttribIndices = fVertexAttribIndices; + stage->fVertexAttribIndices[0] = fVertexAttribIndices[0]; + stage->fVertexAttribIndices[1] = fVertexAttribIndices[1]; } else { stage->fEffectRef = NULL; } @@ -141,7 +143,8 @@ public: return false; } - if (fVertexAttribIndices != stage.fVertexAttribIndices) { + if (fVertexAttribIndices[0] != stage.fVertexAttribIndices[0] + || fVertexAttribIndices[1] != stage.fVertexAttribIndices[1]) { return false; } @@ -155,7 +158,8 @@ public: private: const GrEffect* fEffect; SkMatrix fCoordChangeMatrix; - SkSTArray fVertexAttribIndices; + int fVertexAttribIndices[2]; + int fVertexAttribCount; SkDEBUGCODE(bool fInitialized;) }; @@ -169,28 +173,37 @@ public: GrSafeSetNull(fEffectRef); } - const GrEffectRef* setEffect(const GrEffectRef* EffectRef, const int* attribIndices = NULL) { + const GrEffectRef* setEffect(const GrEffectRef* EffectRef) { GrAssert(0 == fSavedCoordChangeCnt); GrSafeAssign(fEffectRef, EffectRef); fCoordChangeMatrix.reset(); - fVertexAttribIndices.reset(); - int numVertexAttribs = (EffectRef == NULL) ? 0 : EffectRef->get()->numVertexAttribs(); - GrAssert(numVertexAttribs == 0 || attribIndices != NULL); - fVertexAttribIndices.push_back_n(numVertexAttribs, attribIndices); - + fVertexAttribIndices[0] = -1; + fVertexAttribIndices[1] = -1; + return EffectRef; } + const GrEffectRef* setEffect(const GrEffectRef* EffectRef, int attr0, int attr1 = -1) { + GrAssert(0 == fSavedCoordChangeCnt); + GrSafeAssign(fEffectRef, EffectRef); + fCoordChangeMatrix.reset(); + + fVertexAttribIndices[0] = attr0; + fVertexAttribIndices[1] = attr1; + + return EffectRef; + } + const GrEffectRef* getEffect() const { return fEffectRef; } - const int* getVertexAttribIndices() const { return fVertexAttribIndices.begin(); } - int getVertexAttribIndexCount() const { return fVertexAttribIndices.count(); } + const int* getVertexAttribIndices() const { return fVertexAttribIndices; } + int getVertexAttribIndexCount() const { return fEffectRef->get()->numVertexAttribs(); } private: SkMatrix fCoordChangeMatrix; const GrEffectRef* fEffectRef; - SkSTArray<2, int, true> fVertexAttribIndices; + int fVertexAttribIndices[2]; GR_DEBUGCODE(mutable int fSavedCoordChangeCnt;) }; diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index b719ec5a53..cf74c8ed60 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -12,6 +12,7 @@ #include "effects/GrConvolutionEffect.h" #include "effects/GrSingleTextureEffect.h" #include "effects/GrConfigConversionEffect.h" +#include "effects/GrCircleEdgeEffect.h" #include "effects/GrEllipseEdgeEffect.h" #include "GrBufferAllocPool.h" @@ -36,7 +37,8 @@ SK_DEFINE_INST_COUNT(GrDrawState) // It can be useful to set this to false to test whether a bug is caused by using the // InOrderDrawBuffer, to compare performance of using/not using InOrderDrawBuffer, or to make // debugging simpler. -SK_CONF_DECLARE(bool, c_Defer, "gpu.deferContext", true, "Defers rendering in GrContext via GrInOrderDrawBuffer."); +SK_CONF_DECLARE(bool, c_Defer, "gpu.deferContext", true, + "Defers rendering in GrContext via GrInOrderDrawBuffer."); #define BUFFERED_DRAW (c_Defer ? kYes_BufferedDraw : kNo_BufferedDraw) @@ -358,7 +360,8 @@ GrTexture* GrContext::createResizedTexture(const GrTextureDesc& desc, {kVec2f_GrVertexAttribType, 0}, {kVec2f_GrVertexAttribType, sizeof(GrPoint)} }; - static const GrAttribBindings kAttribBindings = GrDrawState::ExplicitTexCoordAttribBindingsBit(0); + static const GrAttribBindings kAttribBindings = + GrDrawState::ExplicitTexCoordAttribBindingsBit(0); drawState->setAttribBindings(kAttribBindings); drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs)); drawState->setAttribIndex(GrDrawState::kPosition_AttribIndex, 0); @@ -389,7 +392,8 @@ GrTexture* GrContext::createResizedTexture(const GrTextureDesc& desc, size_t stretchedRowBytes = rtDesc.fWidth * bpp; - SkDEBUGCODE(GrTexture* texture = )fGpu->createTexture(rtDesc, stretchedPixels.get(), stretchedRowBytes); + SkDEBUGCODE(GrTexture* texture = )fGpu->createTexture(rtDesc, stretchedPixels.get(), + stretchedRowBytes); GrAssert(NULL != texture); } @@ -999,6 +1003,15 @@ struct CircleVertex { SkScalar fInnerRadius; }; +struct EllipseVertex { + GrPoint fPos; + GrPoint fCenter; + SkScalar fOuterXRadius; + SkScalar fOuterXYRatio; + SkScalar fInnerXRadius; + SkScalar fInnerXYRatio; +}; + inline bool circleStaysCircle(const SkMatrix& m) { return m.isSimilarity(); } @@ -1009,64 +1022,186 @@ void GrContext::drawOval(const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke) { - if (!canDrawOval(paint, oval, stroke)) { + bool isCircle; + if (!canDrawOval(paint, oval, &isCircle)) { SkPath path; path.addOval(oval); this->drawPath(paint, path, stroke); return; } - internalDrawOval(paint, oval, stroke); + if (isCircle) { + this->internalDrawCircle(paint, oval, stroke); + } else { + this->internalDrawOval(paint, oval, stroke); + } } -bool GrContext::canDrawOval(const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke) const { +bool GrContext::canDrawOval(const GrPaint& paint, const GrRect& oval, bool* isCircle) const { + GrAssert(isCircle != NULL); if (!paint.isAntiAlias()) { return false; } - // we can draw circles in any style - bool isCircle = SkScalarNearlyEqual(oval.width(), oval.height()) - && circleStaysCircle(this->getMatrix()); - // and for now, axis-aligned ellipses only with fill or stroke-and-fill - SkStrokeRec::Style style = stroke.getStyle(); - bool isStroke = (style == SkStrokeRec::kStroke_Style || style == SkStrokeRec::kHairline_Style); - bool isFilledAxisAlignedEllipse = this->getMatrix().rectStaysRect() && !isStroke; + // we can draw circles + *isCircle = SkScalarNearlyEqual(oval.width(), oval.height()) + && circleStaysCircle(this->getMatrix()); + // and axis-aligned ellipses only + bool isAxisAlignedEllipse = this->getMatrix().rectStaysRect(); - return isCircle || isFilledAxisAlignedEllipse; + return *isCircle || isAxisAlignedEllipse; } void GrContext::internalDrawOval(const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke) { - - SkScalar xRadius = SkScalarHalf(oval.width()); - SkScalar yRadius = SkScalarHalf(oval.height()); - - SkScalar strokeWidth = stroke.getWidth(); - SkStrokeRec::Style style = stroke.getStyle(); - - bool isCircle = SkScalarNearlyEqual(xRadius, yRadius) && circleStaysCircle(this->getMatrix()); #ifdef SK_DEBUG { // we should have checked for this previously - bool isStroke = (style == SkStrokeRec::kStroke_Style || style == SkStrokeRec::kHairline_Style); - bool isFilledAxisAlignedEllipse = this->getMatrix().rectStaysRect() && !isStroke; - SkASSERT(paint.isAntiAlias() && (isCircle || isFilledAxisAlignedEllipse)); + bool isAxisAlignedEllipse = this->getMatrix().rectStaysRect(); + SkASSERT(paint.isAntiAlias() && isAxisAlignedEllipse); } #endif - GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW); GrDrawState* drawState = target->drawState(); GrDrawState::AutoStageDisable atr(fDrawState); - const SkMatrix vm = drawState->getViewMatrix(); const GrRenderTarget* rt = drawState->getRenderTarget(); if (NULL == rt) { return; } + const SkMatrix vm = drawState->getViewMatrix(); + + GrDrawState::AutoDeviceCoordDraw adcd(drawState); + if (!adcd.succeeded()) { + return; + } + + // position + edge + static const GrVertexAttrib kVertexAttribs[] = { + {kVec2f_GrVertexAttribType, 0}, + {kVec2f_GrVertexAttribType, sizeof(GrPoint)}, + {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint)} + }; + drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs)); + drawState->setAttribIndex(GrDrawState::kPosition_AttribIndex, 0); + GrAssert(sizeof(EllipseVertex) == drawState->getVertexSize()); + + GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0); + if (!geo.succeeded()) { + GrPrintf("Failed to get space for vertices!\n"); + return; + } + + EllipseVertex* verts = reinterpret_cast(geo.vertices()); + + GrPoint center = GrPoint::Make(oval.centerX(), oval.centerY()); + vm.mapPoints(¢er, 1); + + SkStrokeRec::Style style = stroke.getStyle(); + bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style); + enum { + // the edge effects share this stage with glyph rendering + // (kGlyphMaskStage in GrTextContext) && SW path rendering + // (kPathMaskStage in GrSWMaskHelper) + kEdgeEffectStage = GrPaint::kTotalStages, + }; + drawState->setAttribBindings(GrDrawState::kDefault_AttribBindings); + + GrEffectRef* effect = GrEllipseEdgeEffect::Create(isStroked); + static const int kEllipseCenterAttrIndex = 1; + static const int kEllipseEdgeAttrIndex = 2; + drawState->setEffect(kEdgeEffectStage, effect, + kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref(); + + SkRect xformedRect; + vm.mapRect(&xformedRect, oval); + + SkScalar xRadius = SkScalarHalf(xformedRect.width()); + SkScalar yRadius = SkScalarHalf(xformedRect.height()); + SkScalar innerXRadius = 0.0f; + SkScalar innerRatio = 1.0f; + + if (SkStrokeRec::kFill_Style != style) { + SkScalar strokeWidth = stroke.getWidth(); + + // do (potentially) anisotropic mapping + SkVector scaledStroke; + scaledStroke.set(strokeWidth, strokeWidth); + vm.mapVectors(&scaledStroke, 1); + + if (SkScalarNearlyZero(scaledStroke.length())) { + scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); + } else { + scaledStroke.scale(0.5f); + } + + // this is legit only if scale & translation (which should be the case at the moment) + if (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style) { + SkScalar innerYRadius = SkMaxScalar(0, yRadius - scaledStroke.fY); + if (innerYRadius > SK_ScalarNearlyZero) { + innerXRadius = SkMaxScalar(0, xRadius - scaledStroke.fX); + innerRatio = innerXRadius/innerYRadius; + } + } + xRadius += scaledStroke.fX; + yRadius += scaledStroke.fY; + } + + SkScalar outerRatio = SkScalarDiv(xRadius, yRadius); + + for (int i = 0; i < 4; ++i) { + verts[i].fCenter = center; + verts[i].fOuterXRadius = xRadius + 0.5f; + verts[i].fOuterXYRatio = outerRatio; + verts[i].fInnerXRadius = innerXRadius - 0.5f; + verts[i].fInnerXYRatio = innerRatio; + } + + SkScalar L = -xRadius; + SkScalar R = +xRadius; + SkScalar T = -yRadius; + SkScalar B = +yRadius; + + // We've extended the outer x radius out half a pixel to antialias. + // Expand the drawn rect here so all the pixels will be captured. + L += center.fX - SK_ScalarHalf; + R += center.fX + SK_ScalarHalf; + T += center.fY - SK_ScalarHalf; + B += center.fY + SK_ScalarHalf; + + verts[0].fPos = SkPoint::Make(L, T); + verts[1].fPos = SkPoint::Make(R, T); + verts[2].fPos = SkPoint::Make(L, B); + verts[3].fPos = SkPoint::Make(R, B); + + target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4); +} + +void GrContext::internalDrawCircle(const GrPaint& paint, + const GrRect& circle, + const SkStrokeRec& stroke) { + + SkScalar radius = SkScalarHalf(circle.width()); + + SkScalar strokeWidth = stroke.getWidth(); + SkStrokeRec::Style style = stroke.getStyle(); + + GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW); + + GrDrawState* drawState = target->drawState(); + GrDrawState::AutoStageDisable atr(fDrawState); + + const GrRenderTarget* rt = drawState->getRenderTarget(); + if (NULL == rt) { + return; + } + + const SkMatrix vm = drawState->getViewMatrix(); + GrDrawState::AutoDeviceCoordDraw adcd(drawState); if (!adcd.succeeded()) { return; @@ -1089,95 +1224,54 @@ void GrContext::internalDrawOval(const GrPaint& paint, CircleVertex* verts = reinterpret_cast(geo.vertices()); - GrPoint center = GrPoint::Make(oval.centerX(), oval.centerY()); + GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY()); vm.mapPoints(¢er, 1); - SkScalar L; - SkScalar R; - SkScalar T; - SkScalar B; + bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style); + enum { + // the edge effects share this stage with glyph rendering + // (kGlyphMaskStage in GrTextContext) && SW path rendering + // (kPathMaskStage in GrSWMaskHelper) + kEdgeEffectStage = GrPaint::kTotalStages, + }; + drawState->setAttribBindings(GrDrawState::kDefault_AttribBindings); + + GrEffectRef* effect = GrCircleEdgeEffect::Create(isStroked); + static const int kCircleEdgeAttrIndex = 1; + drawState->setEffect(kEdgeEffectStage, effect, kCircleEdgeAttrIndex)->unref(); - if (isCircle) { - drawState->setAttribBindings(GrDrawState::kEdge_AttribBindingsBit); - drawState->setVertexEdgeType(GrDrawState::kCircle_EdgeType); - drawState->setAttribIndex(GrDrawState::kEdge_AttribIndex, 1); + radius = vm.mapRadius(radius); - xRadius = vm.mapRadius(xRadius); - - SkScalar outerRadius = xRadius; - SkScalar innerRadius = 0; - SkScalar halfWidth = 0; - if (style != SkStrokeRec::kFill_Style) { - strokeWidth = vm.mapRadius(strokeWidth); - if (SkScalarNearlyZero(strokeWidth)) { - halfWidth = SK_ScalarHalf; - } else { - halfWidth = SkScalarHalf(strokeWidth); - } - - outerRadius += halfWidth; - if (style == SkStrokeRec::kStroke_Style || style == SkStrokeRec::kHairline_Style) { - innerRadius = SkMaxScalar(0, xRadius - halfWidth); - } + SkScalar innerRadius = -2.0f; + SkScalar outerRadius = radius; + SkScalar halfWidth = 0; + if (style != SkStrokeRec::kFill_Style) { + strokeWidth = vm.mapRadius(strokeWidth); + if (SkScalarNearlyZero(strokeWidth)) { + halfWidth = SK_ScalarHalf; + } else { + halfWidth = SkScalarHalf(strokeWidth); } - for (int i = 0; i < 4; ++i) { - verts[i].fCenter = center; - verts[i].fOuterRadius = outerRadius; - verts[i].fInnerRadius = innerRadius; + outerRadius += halfWidth; + if (isStroked) { + innerRadius = SkMaxScalar(0, radius - halfWidth); } - - L = -outerRadius; - R = +outerRadius; - T = -outerRadius; - B = +outerRadius; - } else { // is axis-aligned ellipse - drawState->setAttribBindings(GrDrawState::kDefault_AttribBindings); - - enum { - // the edge effects share this stage with glyph rendering - // (kGlyphMaskStage in GrTextContext) && SW path rendering - // (kPathMaskStage in GrSWMaskHelper) - kEdgeEffectStage = GrPaint::kTotalStages, - }; - GrEffectRef* effect = GrEllipseEdgeEffect::Create(); - static const int kEdgeAttrIndex = 1; - drawState->setEffect(kEdgeEffectStage, effect, &kEdgeAttrIndex)->unref(); - - SkRect xformedRect; - vm.mapRect(&xformedRect, oval); - - xRadius = SkScalarHalf(xformedRect.width()); - yRadius = SkScalarHalf(xformedRect.height()); - - if (style == SkStrokeRec::kStrokeAndFill_Style && strokeWidth > 0.0f) { - SkScalar halfWidth = SkScalarHalf(strokeWidth); - // do (potentially) anisotropic mapping - SkVector scaledStroke; - scaledStroke.set(halfWidth, halfWidth); - vm.mapVectors(&scaledStroke, 1); - // this is legit only if scale & translation (which should be the case at the moment) - xRadius += scaledStroke.fX; - yRadius += scaledStroke.fY; - } - - SkScalar ratio = SkScalarDiv(xRadius, yRadius); - - for (int i = 0; i < 4; ++i) { - verts[i].fCenter = center; - verts[i].fOuterRadius = xRadius; - verts[i].fInnerRadius = ratio; - } - - L = -xRadius; - R = +xRadius; - T = -yRadius; - B = +yRadius; } - // The fragment shader will extend the radius out half a pixel - // to antialias. Expand the drawn rect here so all the pixels - // will be captured. + for (int i = 0; i < 4; ++i) { + verts[i].fCenter = center; + verts[i].fOuterRadius = outerRadius + 0.5f; + verts[i].fInnerRadius = innerRadius - 0.5f; + } + + SkScalar L = -outerRadius; + SkScalar R = +outerRadius; + SkScalar T = -outerRadius; + SkScalar B = +outerRadius; + + // We've extended the outer radius out half a pixel to antialias. + // Expand the drawn rect here so all the pixels will be captured. L += center.fX - SK_ScalarHalf; R += center.fX + SK_ScalarHalf; T += center.fY - SK_ScalarHalf; @@ -1203,15 +1297,21 @@ void GrContext::drawPath(const GrPaint& paint, const SkPath& path, const SkStrok SkRect ovalRect; bool isOval = path.isOval(&ovalRect); - if (isOval && !path.isInverseFillType() && this->canDrawOval(paint, ovalRect, stroke)) { - this->drawOval(paint, ovalRect, stroke); + bool isCircle; + if (isOval && !path.isInverseFillType() && this->canDrawOval(paint, ovalRect, &isCircle)) { + if (isCircle) { + this->internalDrawCircle(paint, ovalRect, stroke); + } else { + this->internalDrawOval(paint, ovalRect, stroke); + } return; } this->internalDrawPath(paint, path, stroke); } -void GrContext::internalDrawPath(const GrPaint& paint, const SkPath& path, const SkStrokeRec& stroke) { +void GrContext::internalDrawPath(const GrPaint& paint, const SkPath& path, + const SkStrokeRec& stroke) { // Note that below we may sw-rasterize the path into a scratch texture. // Scratch textures can be recycled after they are returned to the texture diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h index 4e9747d755..6ee6df5ed2 100644 --- a/src/gpu/GrDrawState.h +++ b/src/gpu/GrDrawState.h @@ -485,9 +485,14 @@ public: /// @name Effect Stages //// + const GrEffectRef* setEffect(int stageIdx, const GrEffectRef* effect) { + fStages[stageIdx].setEffect(effect); + return effect; + } + const GrEffectRef* setEffect(int stageIdx, const GrEffectRef* effect, - const int* indices = NULL) { - fStages[stageIdx].setEffect(effect, indices); + int attr0, int attr1 = -1) { + fStages[stageIdx].setEffect(effect, attr0, attr1); return effect; } @@ -518,7 +523,7 @@ public: } void disableStage(int stageIdx) { - this->setEffect(stageIdx, NULL, NULL); + this->setEffect(stageIdx, NULL); } /** @@ -1023,9 +1028,6 @@ public: /* Same as above but for hairline quadratics. Uses unsigned distance. Coverage is min(0, 1-distance). */ kHairQuad_EdgeType, - /* Circle specified as center_x, center_y, outer_radius, inner_radius - all in window space (y-down). */ - kCircle_EdgeType, kVertexEdgeTypeCnt }; diff --git a/src/gpu/effects/GrCircleEdgeEffect.cpp b/src/gpu/effects/GrCircleEdgeEffect.cpp new file mode 100644 index 0000000000..e430464442 --- /dev/null +++ b/src/gpu/effects/GrCircleEdgeEffect.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrCircleEdgeEffect.h" +#include "gl/GrGLEffect.h" +#include "gl/GrGLEffectMatrix.h" +#include "gl/GrGLSL.h" +#include "gl/GrGLTexture.h" +#include "GrTBackendEffectFactory.h" +#include "GrTexture.h" + +#include "SkRTConf.h" + +class GrGLCircleEdgeEffect : public GrGLEffect { +public: + GrGLCircleEdgeEffect(const GrBackendEffectFactory& factory, const GrEffectRef&) + : INHERITED (factory) {} + + virtual void emitCode(GrGLShaderBuilder* builder, + const GrEffectStage& stage, + EffectKey key, + const char* vertexCoords, + const char* outputColor, + const char* inputColor, + const TextureSamplerArray& samplers) SK_OVERRIDE { + const GrCircleEdgeEffect& effect = GetEffectFromStage(stage); + + const char *vsName, *fsName; + builder->addVarying(kVec4f_GrSLType, "CircleEdge", &vsName, &fsName); + + const SkString* attrName = + builder->getEffectAttributeName(stage.getVertexAttribIndices()[0]); + builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str()); + + builder->fsCodeAppendf("\tfloat d = distance(%s.xy, %s.xy);\n", + builder->fragmentPosition(), fsName); + builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.z - d, 0.0, 1.0);\n", fsName); + if (effect.isStroked()) { + builder->fsCodeAppendf("\tfloat innerAlpha = clamp(d - %s.w, 0.0, 1.0);\n", fsName); + builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n"); + } + SkString modulate; + GrGLSLModulate4f(&modulate, inputColor, "edgeAlpha"); + builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str()); + } + + static inline EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) { + const GrCircleEdgeEffect& effect = GetEffectFromStage(stage); + + return effect.isStroked() ? 0x1 : 0x0; + } + + virtual void setData(const GrGLUniformManager& uman, const GrEffectStage& stage) SK_OVERRIDE { + } + +private: + typedef GrGLEffect INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +GrCircleEdgeEffect::GrCircleEdgeEffect(bool stroke) : GrEffect() { + this->addVertexAttrib(kVec4f_GrSLType); + fStroke = stroke; +} + +void GrCircleEdgeEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { + *validFlags = 0; +} + +const GrBackendEffectFactory& GrCircleEdgeEffect::getFactory() const { + return GrTBackendEffectFactory::getInstance(); +} + +/////////////////////////////////////////////////////////////////////////////// + +GR_DEFINE_EFFECT_TEST(GrCircleEdgeEffect); + +GrEffectRef* GrCircleEdgeEffect::TestCreate(SkMWCRandom* random, + GrContext* context, + GrTexture* textures[]) { + return GrCircleEdgeEffect::Create(random->nextBool()); +} diff --git a/src/gpu/effects/GrCircleEdgeEffect.h b/src/gpu/effects/GrCircleEdgeEffect.h new file mode 100644 index 0000000000..6bd892e135 --- /dev/null +++ b/src/gpu/effects/GrCircleEdgeEffect.h @@ -0,0 +1,65 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrCircleEdgeEffect_DEFINED +#define GrCircleEdgeEffect_DEFINED + +#include "GrEffect.h" + +class GrGLCircleEdgeEffect; + +/** + * The output of this effect is a modulation of the input color and coverage for a circle, + * specified as center_x, center_y, x_radius, inner radius and outer radius in window space + * (y-down). + */ + +class GrCircleEdgeEffect : public GrEffect { +public: + static GrEffectRef* Create(bool stroke) { + // we go through this so we only have one copy of each effect (stroked/filled) + static SkAutoTUnref gCircleStrokeEdgeEffectRef( + CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(GrCircleEdgeEffect, (true))))); + static SkAutoTUnref gCircleFillEdgeEffectRef( + CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(GrCircleEdgeEffect, (false))))); + + if (stroke) { + gCircleStrokeEdgeEffectRef.get()->ref(); + return gCircleStrokeEdgeEffectRef; + } else { + gCircleFillEdgeEffectRef.get()->ref(); + return gCircleFillEdgeEffectRef; + } + } + + virtual ~GrCircleEdgeEffect() {} + + static const char* Name() { return "CircleEdge"; } + + virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; + + typedef GrGLCircleEdgeEffect GLEffect; + + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; + + inline bool isStroked() const { return fStroke; } + +private: + GrCircleEdgeEffect(bool stroke); + + virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE { + return true; + } + + bool fStroke; + + GR_DECLARE_EFFECT_TEST; + + typedef GrEffect INHERITED; +}; + +#endif diff --git a/src/gpu/effects/GrEllipseEdgeEffect.cpp b/src/gpu/effects/GrEllipseEdgeEffect.cpp index 18301e9999..f4965f7bf8 100644 --- a/src/gpu/effects/GrEllipseEdgeEffect.cpp +++ b/src/gpu/effects/GrEllipseEdgeEffect.cpp @@ -25,27 +25,49 @@ public: const char* outputColor, const char* inputColor, const TextureSamplerArray& samplers) SK_OVERRIDE { - const char *vsName, *fsName; - builder->addVarying(kVec4f_GrSLType, "EllipseEdge", &vsName, &fsName); + const GrEllipseEdgeEffect& effect = GetEffectFromStage(stage); + + const char *vsCenterName, *fsCenterName; + const char *vsEdgeName, *fsEdgeName; - const SkString* attrName = builder->getEffectAttributeName(stage.getVertexAttribIndices()[0]); - builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str()); + builder->addVarying(kVec2f_GrSLType, "EllipseCenter", &vsCenterName, &fsCenterName); + const SkString* attr0Name = + builder->getEffectAttributeName(stage.getVertexAttribIndices()[0]); + builder->vsCodeAppendf("\t%s = %s;\n", vsCenterName, attr0Name->c_str()); + + builder->addVarying(kVec4f_GrSLType, "EllipseEdge", &vsEdgeName, &fsEdgeName); + const SkString* attr1Name = + builder->getEffectAttributeName(stage.getVertexAttribIndices()[1]); + builder->vsCodeAppendf("\t%s = %s;\n", vsEdgeName, attr1Name->c_str()); - builder->fsCodeAppend("\tfloat edgeAlpha;\n"); // translate to origin - builder->fsCodeAppendf("\tvec2 offset = (%s.xy - %s.xy);\n", builder->fragmentPosition(), fsName); + builder->fsCodeAppendf("\tvec2 outerOffset = (%s.xy - %s.xy);\n", + builder->fragmentPosition(), fsCenterName); + builder->fsCodeAppend("\tvec2 innerOffset = outerOffset;\n"); // scale y by xRadius/yRadius - builder->fsCodeAppendf("\toffset.y *= %s.w;\n", fsName); - builder->fsCodeAppend("\tfloat d = length(offset);\n"); - // compare length against xRadius - builder->fsCodeAppendf("\tedgeAlpha = smoothstep(d - 0.5, d + 0.5, %s.z);\n", fsName); + builder->fsCodeAppendf("\touterOffset.y *= %s.y;\n", fsEdgeName); + builder->fsCodeAppend("\tfloat dOuter = length(outerOffset);\n"); + // compare outer lengths against xOuterRadius + builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.x-dOuter, 0.0, 1.0);\n", fsEdgeName); + + if (effect.isStroked()) { + builder->fsCodeAppendf("\tinnerOffset.y *= %s.w;\n", fsEdgeName); + builder->fsCodeAppend("\tfloat dInner = length(innerOffset);\n"); + + // compare inner lengths against xInnerRadius + builder->fsCodeAppendf("\tfloat innerAlpha = clamp(dInner-%s.z, 0.0, 1.0);\n", fsEdgeName); + builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n"); + } + SkString modulate; GrGLSLModulate4f(&modulate, inputColor, "edgeAlpha"); builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str()); } static inline EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) { - return 0; + const GrEllipseEdgeEffect& effect = GetEffectFromStage(stage); + + return effect.isStroked() ? 0x1 : 0x0; } virtual void setData(const GrGLUniformManager& uman, const GrEffectStage& stage) SK_OVERRIDE { @@ -57,8 +79,11 @@ private: /////////////////////////////////////////////////////////////////////////////// -GrEllipseEdgeEffect::GrEllipseEdgeEffect() : GrEffect() { +GrEllipseEdgeEffect::GrEllipseEdgeEffect(bool stroke) : GrEffect() { + this->addVertexAttrib(kVec2f_GrSLType); this->addVertexAttrib(kVec4f_GrSLType); + + fStroke = stroke; } void GrEllipseEdgeEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { @@ -76,5 +101,5 @@ GR_DEFINE_EFFECT_TEST(GrEllipseEdgeEffect); GrEffectRef* GrEllipseEdgeEffect::TestCreate(SkMWCRandom* random, GrContext* context, GrTexture* textures[]) { - return GrEllipseEdgeEffect::Create(); + return GrEllipseEdgeEffect::Create(random->nextBool()); } diff --git a/src/gpu/effects/GrEllipseEdgeEffect.h b/src/gpu/effects/GrEllipseEdgeEffect.h index 31c6b09a0f..5eada2c96c 100644 --- a/src/gpu/effects/GrEllipseEdgeEffect.h +++ b/src/gpu/effects/GrEllipseEdgeEffect.h @@ -19,10 +19,20 @@ class GrGLEllipseEdgeEffect; class GrEllipseEdgeEffect : public GrEffect { public: - static GrEffectRef* Create() { - // maybe only have one static copy? - AutoEffectUnref effect(SkNEW(GrEllipseEdgeEffect)); - return CreateEffectRef(effect); + static GrEffectRef* Create(bool stroke) { + // we go through this so we only have one copy of each effect (stroked/filled) + static SkAutoTUnref gEllipseStrokeEdgeEffectRef( + CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(GrEllipseEdgeEffect, (true))))); + static SkAutoTUnref gEllipseFillEdgeEffectRef( + CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(GrEllipseEdgeEffect, (false))))); + + if (stroke) { + gEllipseStrokeEdgeEffectRef.get()->ref(); + return gEllipseStrokeEdgeEffectRef; + } else { + gEllipseFillEdgeEffectRef.get()->ref(); + return gEllipseFillEdgeEffectRef; + } } virtual ~GrEllipseEdgeEffect() {} @@ -35,13 +45,17 @@ public: virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; + inline bool isStroked() const { return fStroke; } + private: - GrEllipseEdgeEffect(); + GrEllipseEdgeEffect(bool stroke); virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE { return true; } + bool fStroke; + GR_DECLARE_EFFECT_TEST; typedef GrEffect INHERITED; diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp index 850f0bb134..7d6420ead4 100644 --- a/src/gpu/gl/GrGLProgram.cpp +++ b/src/gpu/gl/GrGLProgram.cpp @@ -23,7 +23,8 @@ SK_DEFINE_INST_COUNT(GrGLProgram) #define GL_CALL(X) GR_GL_CALL(fContext.interface(), X) #define GL_CALL_RET(R, X) GR_GL_CALL_RET(fContext.interface(), R, X) -SK_CONF_DECLARE(bool, c_PrintShaders, "gpu.printShaders", false, "Print the source code for all shaders generated."); +SK_CONF_DECLARE(bool, c_PrintShaders, "gpu.printShaders", false, + "Print the source code for all shaders generated."); #define TEX_ATTR_NAME "aTexCoord" #define COL_ATTR_NAME "aColor" @@ -69,15 +70,16 @@ void GrGLProgram::BuildDesc(const GrDrawState& drawState, desc->fEmitsPointSize = isPoints; - bool requiresAttributeColors = !skipColor && - SkToBool(desc->fAttribBindings & GrDrawState::kColor_AttribBindingsBit); - bool requiresAttributeCoverage = !skipCoverage && - SkToBool(desc->fAttribBindings & GrDrawState::kCoverage_AttribBindingsBit); + bool requiresAttributeColors = + !skipColor && SkToBool(desc->fAttribBindings & GrDrawState::kColor_AttribBindingsBit); + bool requiresAttributeCoverage = + !skipCoverage && SkToBool(desc->fAttribBindings & GrDrawState::kCoverage_AttribBindingsBit); // fColorInput/fCoverageInput records how colors are specified for the program So we strip the // bits from the bindings to avoid false negatives when searching for an existing program in the // cache. - desc->fAttribBindings &= ~(GrDrawState::kColor_AttribBindingsBit | GrDrawState::kCoverage_AttribBindingsBit); + desc->fAttribBindings &= + ~(GrDrawState::kColor_AttribBindingsBit | GrDrawState::kCoverage_AttribBindingsBit); desc->fColorFilterXfermode = skipColor ? SkXfermode::kDst_Mode : @@ -423,7 +425,8 @@ bool GrGLProgram::genEdgeCoverage(SkString* coverageVar, builder->vsCodeAppendf("\t%s = " EDGE_ATTR_NAME ";\n", vsName); switch (fDesc.fVertexEdgeType) { case GrDrawState::kHairLine_EdgeType: - builder->fsCodeAppendf("\tfloat edgeAlpha = abs(dot(vec3(%s.xy,1), %s.xyz));\n", builder->fragmentPosition(), fsName); + builder->fsCodeAppendf("\tfloat edgeAlpha = abs(dot(vec3(%s.xy,1), %s.xyz));\n", + builder->fragmentPosition(), fsName); builder->fsCodeAppendf("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n"); break; case GrDrawState::kQuad_EdgeType: @@ -433,7 +436,8 @@ bool GrGLProgram::genEdgeCoverage(SkString* coverageVar, builder->fsCodeAppendf("\tvec2 duvdy = dFdy(%s.xy);\n", fsName); builder->fsCodeAppendf("\tif (%s.z > 0.0 && %s.w > 0.0) {\n", fsName, fsName); // today we know z and w are in device space. We could use derivatives - builder->fsCodeAppendf("\t\tedgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);\n", fsName, fsName); + builder->fsCodeAppendf("\t\tedgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);\n", fsName, + fsName); builder->fsCodeAppendf ("\t} else {\n"); builder->fsCodeAppendf("\t\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n" "\t\t 2.0*%s.x*duvdy.x - duvdy.y);\n", @@ -451,20 +455,14 @@ bool GrGLProgram::genEdgeCoverage(SkString* coverageVar, builder->fsCodeAppendf("\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n" "\t 2.0*%s.x*duvdy.x - duvdy.y);\n", fsName, fsName); - builder->fsCodeAppendf("\tfloat edgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, fsName); + builder->fsCodeAppendf("\tfloat edgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, + fsName); builder->fsCodeAppend("\tedgeAlpha = sqrt(edgeAlpha*edgeAlpha / dot(gF, gF));\n"); builder->fsCodeAppend("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n"); if (kES2_GrGLBinding == fContext.info().binding()) { builder->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n"); } break; - case GrDrawState::kCircle_EdgeType: - builder->fsCodeAppend("\tfloat edgeAlpha;\n"); - builder->fsCodeAppendf("\tfloat d = distance(%s.xy, %s.xy);\n", builder->fragmentPosition(), fsName); - builder->fsCodeAppendf("\tfloat outerAlpha = smoothstep(d - 0.5, d + 0.5, %s.z);\n", fsName); - builder->fsCodeAppendf("\tfloat innerAlpha = %s.w == 0.0 ? 1.0 : smoothstep(%s.w - 0.5, %s.w + 0.5, d);\n", fsName, fsName, fsName); - builder->fsCodeAppend("\tedgeAlpha = outerAlpha * innerAlpha;\n"); - break; default: GrCrash("Unknown Edge Type!"); break; @@ -905,7 +903,9 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) { // discard if coverage is zero if (fDesc.fDiscardIfOutsideEdge && !outCoverage.isEmpty()) { - builder.fsCodeAppendf("\tif (all(lessThanEqual(%s, vec4(0.0)))) {\n\t\tdiscard;\n\t}\n", outCoverage.c_str()); + builder.fsCodeAppendf( + "\tif (all(lessThanEqual(%s, vec4(0.0)))) {\n\t\tdiscard;\n\t}\n", + outCoverage.c_str()); } } diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp index b56c788513..d12aeb57dd 100644 --- a/tests/GLProgramsTest.cpp +++ b/tests/GLProgramsTest.cpp @@ -134,7 +134,7 @@ bool GrGpuGL::programUnitTest(int maxStages) { for (int i = 0; i < effect.get()->get()->numVertexAttribs(); ++i) { attribIndices[i] = currAttribIndex++; } - stages[s].setEffect(effect.get(), attribIndices); + stages[s].setEffect(effect.get(), attribIndices[0], attribIndices[1]); } } pdesc.setRandom(&random, this, stages);