From b19cb7f36785f3ad3b1512c342fc662ab79e3fca Mon Sep 17 00:00:00 2001 From: "robertphillips@google.com" Date: Thu, 2 May 2013 15:37:20 +0000 Subject: [PATCH] Axis aligned shader-based rect drawing https://codereview.chromium.org/14314004/ git-svn-id: http://skia.googlecode.com/svn/trunk@8960 2bbb7eff-a529-9590-31e7-b0007b416f81 --- include/gpu/GrAARectRenderer.h | 45 +++++++-- src/gpu/GrAARectRenderer.cpp | 208 +++++++++++++++++++++++++++++++++++++---- src/gpu/GrClipMaskManager.cpp | 2 + src/gpu/GrContext.cpp | 9 +- 4 files changed, 231 insertions(+), 33 deletions(-) diff --git a/include/gpu/GrAARectRenderer.h b/include/gpu/GrAARectRenderer.h index 84dd52f..b544294 100644 --- a/include/gpu/GrAARectRenderer.h +++ b/include/gpu/GrAARectRenderer.h @@ -38,19 +38,25 @@ public: // TODO: potentialy fuse the fill & stroke methods and differentiate // between them by passing in strokeWidth (<0 means fill). - // TODO: Remove the useVertexCoverage boolean. Just use it all the time - // since we now have a coverage vertex attribute void fillAARect(GrGpu* gpu, GrDrawTarget* target, + const GrRect& rect, + const SkMatrix& combinedMatrix, const GrRect& devRect, - bool useVertexCoverage); - - void shaderFillAARect(GrGpu* gpu, - GrDrawTarget* target, - const GrRect& rect, - const SkMatrix& combinedMatrix, - const GrRect& devRect, - bool useVertexCoverage); + bool useVertexCoverage) { +#ifdef SHADER_AA_FILL_RECT + if (combinedMatrix.rectStaysRect()) { + this->shaderFillAlignedAARect(gpu, target, + combinedMatrix, devRect); + } else { + this->shaderFillAARect(gpu, target, + rect, combinedMatrix, devRect); + } +#else + this->geometryFillAARect(gpu, target, + devRect, useVertexCoverage); +#endif + } void strokeAARect(GrGpu* gpu, GrDrawTarget* target, @@ -67,6 +73,25 @@ private: static int aaStrokeRectIndexCount(); GrIndexBuffer* aaStrokeRectIndexBuffer(GrGpu* gpu); + // TODO: Remove the useVertexCoverage boolean. Just use it all the time + // since we now have a coverage vertex attribute + void geometryFillAARect(GrGpu* gpu, + GrDrawTarget* target, + const GrRect& devRect, + bool useVertexCoverage); + + void shaderFillAARect(GrGpu* gpu, + GrDrawTarget* target, + const GrRect& rect, + const SkMatrix& combinedMatrix, + const GrRect& devRect); + + void shaderFillAlignedAARect(GrGpu* gpu, + GrDrawTarget* target, + const GrRect& rect, + const SkMatrix& combinedMatrix, + const GrRect& devRect); + typedef GrRefCnt INHERITED; }; diff --git a/src/gpu/GrAARectRenderer.cpp b/src/gpu/GrAARectRenderer.cpp index 418d80d..5acb406 100644 --- a/src/gpu/GrAARectRenderer.cpp +++ b/src/gpu/GrAARectRenderer.cpp @@ -13,6 +13,108 @@ SK_DEFINE_INST_COUNT(GrAARectRenderer) +/////////////////////////////////////////////////////////////////////////////// +class GrGLAlignedRectEffect; + +// Axis Aligned special case +class GrAlignedRectEffect : public GrEffect { +public: + static GrEffectRef* Create() { + GR_CREATE_STATIC_EFFECT(gAlignedRectEffect, GrAlignedRectEffect, ()); + gAlignedRectEffect->ref(); + return gAlignedRectEffect; + } + + virtual ~GrAlignedRectEffect() {} + + static const char* Name() { return "AlignedRectEdge"; } + + virtual void getConstantColorComponents(GrColor* color, + uint32_t* validFlags) const SK_OVERRIDE { + *validFlags = 0; + } + + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { + return GrTBackendEffectFactory::getInstance(); + } + + class GLEffect : public GrGLEffect { + public: + GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) + : INHERITED (factory) {} + + virtual void emitCode(GrGLShaderBuilder* builder, + const GrDrawEffect& drawEffect, + EffectKey key, + const char* outputColor, + const char* inputColor, + const TextureSamplerArray& samplers) SK_OVERRIDE { + // setup the varying for the Axis aligned rect effect + // xy -> interpolated offset + // zw -> w/2+0.5, h/2+0.5 + const char *vsRectName, *fsRectName; + builder->addVarying(kVec4f_GrSLType, "Rect", &vsRectName, &fsRectName); + const SkString* attr0Name = + builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]); + builder->vsCodeAppendf("\t%s = %s;\n", vsRectName, attr0Name->c_str()); + + // TODO: compute these scale factors in the VS + // These scale factors adjust the coverage for < 1 pixel wide/high rects + builder->fsCodeAppendf("\tfloat wScale = max(1.0, 2.0/(0.5+%s.z));\n", + fsRectName); + builder->fsCodeAppendf("\tfloat hScale = max(1.0, 2.0/(0.5+%s.w));\n", + fsRectName); + + // Compute the coverage for the rect's width + builder->fsCodeAppendf("\tfloat coverage = clamp(wScale*(%s.z-abs(%s.x)), 0.0, 1.0);\n", + fsRectName, + fsRectName); + + // Compute the coverage for the rect's height and merge with the width + builder->fsCodeAppendf( + "\tcoverage = min(coverage, clamp(hScale*(%s.w-abs(%s.y)), 0.0, 1.0));\n", + fsRectName, + fsRectName); + + SkString modulate; + GrGLSLModulatef<4>(&modulate, inputColor, "coverage"); + builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str()); + } + + static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { + return 0; + } + + virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect&) SK_OVERRIDE {} + + private: + typedef GrGLEffect INHERITED; + }; + + +private: + GrAlignedRectEffect() : GrEffect() { + this->addVertexAttrib(kVec4f_GrSLType); + } + + virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE { return true; } + + GR_DECLARE_EFFECT_TEST; + + typedef GrEffect INHERITED; +}; + + +GR_DEFINE_EFFECT_TEST(GrAlignedRectEffect); + +GrEffectRef* GrAlignedRectEffect::TestCreate(SkMWCRandom* random, + GrContext* context, + const GrDrawTargetCaps&, + GrTexture* textures[]) { + return GrAlignedRectEffect::Create(); +} + +/////////////////////////////////////////////////////////////////////////////// class GrGLRectEffect; /** @@ -257,10 +359,10 @@ GrIndexBuffer* GrAARectRenderer::aaStrokeRectIndexBuffer(GrGpu* gpu) { return fAAStrokeRectIndexBuffer; } -void GrAARectRenderer::fillAARect(GrGpu* gpu, - GrDrawTarget* target, - const GrRect& devRect, - bool useVertexCoverage) { +void GrAARectRenderer::geometryFillAARect(GrGpu* gpu, + GrDrawTarget* target, + const GrRect& devRect, + bool useVertexCoverage) { GrDrawState* drawState = target->drawState(); set_aa_rect_vertex_attributes(drawState, useVertexCoverage); @@ -311,6 +413,9 @@ void GrAARectRenderer::fillAARect(GrGpu* gpu, target->resetIndexSource(); } +namespace { + +// Rotated struct RectVertex { GrPoint fPos; GrPoint fCenter; @@ -318,22 +423,33 @@ struct RectVertex { GrPoint fWidthHeight; }; -namespace { - +// Rotated extern const GrVertexAttrib gAARectVertexAttribs[] = { { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding }, { kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding }, { kVec2f_GrVertexAttribType, 3*sizeof(GrPoint), kEffect_GrVertexAttribBinding } }; +// Axis Aligned +struct AARectVertex { + GrPoint fPos; + GrPoint fOffset; + GrPoint fWidthHeight; +}; + +// Axis Aligned +extern const GrVertexAttrib gAAAARectVertexAttribs[] = { + { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding }, + { kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding }, +}; + }; void GrAARectRenderer::shaderFillAARect(GrGpu* gpu, GrDrawTarget* target, const GrRect& rect, const SkMatrix& combinedMatrix, - const GrRect& devRect, - bool useVertexCoverage) { + const GrRect& devRect) { GrDrawState* drawState = target->drawState(); SkPoint center = SkPoint::Make(rect.centerX(), rect.centerY()); @@ -345,15 +461,12 @@ void GrAARectRenderer::shaderFillAARect(GrGpu* gpu, // compute transformed (width, 0) and (0, height) vectors SkVector vec[2] = { - { combinedMatrix[SkMatrix::kMScaleX] * rect.width(), - combinedMatrix[SkMatrix::kMSkewY] * rect.width() }, - { combinedMatrix[SkMatrix::kMSkewX] * rect.height(), - combinedMatrix[SkMatrix::kMScaleY] * rect.height() } + { combinedMatrix[SkMatrix::kMScaleX], combinedMatrix[SkMatrix::kMSkewY] }, + { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] } }; - SkScalar newWidth = vec[0].length() / 2.0f + 0.5f; - SkScalar newHeight = vec[1].length() / 2.0f + 0.5f; - + SkScalar newWidth = SkScalarHalf(rect.width() * vec[0].length()) + SK_ScalarHalf; + SkScalar newHeight = SkScalarHalf(rect.height() * vec[1].length()) + SK_ScalarHalf; drawState->setVertexAttribs(SK_ARRAY_COUNT(gAARectVertexAttribs)); GrAssert(sizeof(RectVertex) == drawState->getVertexSize()); @@ -401,6 +514,69 @@ void GrAARectRenderer::shaderFillAARect(GrGpu* gpu, target->resetIndexSource(); } +void GrAARectRenderer::shaderFillAlignedAARect(GrGpu* gpu, + GrDrawTarget* target, + const GrRect& rect, + const SkMatrix& combinedMatrix, + const GrRect& devRect) { + GrDrawState* drawState = target->drawState(); + SkASSERT(combinedMatrix.rectStaysRect()); + + drawState->setVertexAttribs(SK_ARRAY_COUNT(gAAAARectVertexAttribs)); + GrAssert(sizeof(AARectVertex) == drawState->getVertexSize()); + + GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0); + if (!geo.succeeded()) { + GrPrintf("Failed to get space for vertices!\n"); + return; + } + + AARectVertex* verts = reinterpret_cast(geo.vertices()); + + enum { + // the edge effects share this stage with glyph rendering + // (kGlyphMaskStage in GrTextContext) && SW path rendering + // (kPathMaskStage in GrSWMaskHelper) + kEdgeEffectStage = GrPaint::kTotalStages, + }; + + GrEffectRef* effect = GrAlignedRectEffect::Create(); + static const int kOffsetIndex = 1; + drawState->setEffect(kEdgeEffectStage, effect, kOffsetIndex)->unref(); + + SkRect devBounds = { + devRect.fLeft - SK_ScalarHalf, + devRect.fTop - SK_ScalarHalf, + devRect.fRight + SK_ScalarHalf, + devRect.fBottom + SK_ScalarHalf + }; + + GrPoint widthHeight = { + SkScalarHalf(devRect.width()) + SK_ScalarHalf, + SkScalarHalf(devRect.height()) + SK_ScalarHalf + }; + + verts[0].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fTop); + verts[0].fOffset = SkPoint::Make(-widthHeight.fX, -widthHeight.fY); + verts[0].fWidthHeight = widthHeight; + + verts[1].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fBottom); + verts[1].fOffset = SkPoint::Make(-widthHeight.fX, widthHeight.fY); + verts[1].fWidthHeight = widthHeight; + + verts[2].fPos = SkPoint::Make(devBounds.fRight, devBounds.fBottom); + verts[2].fOffset = widthHeight; + verts[2].fWidthHeight = widthHeight; + + verts[3].fPos = SkPoint::Make(devBounds.fRight, devBounds.fTop); + verts[3].fOffset = SkPoint::Make(widthHeight.fX, -widthHeight.fY); + verts[3].fWidthHeight = widthHeight; + + target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer()); + target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6); + target->resetIndexSource(); +} + void GrAARectRenderer::strokeAARect(GrGpu* gpu, GrDrawTarget* target, const GrRect& devRect, @@ -423,7 +599,7 @@ void GrAARectRenderer::strokeAARect(GrGpu* gpu, if (spare <= 0) { GrRect r(devRect); r.inset(-rx, -ry); - this->fillAARect(gpu, target, r, useVertexCoverage); + this->fillAARect(gpu, target, r, SkMatrix::I(), r, useVertexCoverage); return; } diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp index ca5f378..c022b34d 100644 --- a/src/gpu/GrClipMaskManager.cpp +++ b/src/gpu/GrClipMaskManager.cpp @@ -281,6 +281,8 @@ bool GrClipMaskManager::drawElement(GrTexture* target, getContext()->getAARectRenderer()->fillAARect(fGpu, fGpu, element->getRect(), + SkMatrix::I(), + element->getRect(), false); } else { fGpu->drawSimpleRect(element->getRect(), NULL); diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index 2f4aea9..d23dd54 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -807,14 +807,9 @@ void GrContext::drawRect(const GrPaint& paint, strokeSize, useVertexCoverage); } else { // filled AA rect -#ifdef SHADER_AA_FILL_RECT - fAARectRenderer->shaderFillAARect(this->getGpu(), target, - rect, combinedMatrix, devRect, - useVertexCoverage); -#else fAARectRenderer->fillAARect(this->getGpu(), target, - devRect, useVertexCoverage); -#endif + rect, combinedMatrix, devRect, + useVertexCoverage); } return; } -- 2.7.4