From fa66294c7705831808ce7772d4328fc626d45034 Mon Sep 17 00:00:00 2001 From: "robertphillips@google.com" Date: Thu, 17 May 2012 12:20:22 +0000 Subject: First functioning version of SW-only clip mask creator http://codereview.appspot.com/6208072/ git-svn-id: http://skia.googlecode.com/svn/trunk@3984 2bbb7eff-a529-9590-31e7-b0007b416f81 --- src/gpu/GrAAConvexPathRenderer.cpp | 17 ++- src/gpu/GrAAConvexPathRenderer.h | 6 ++ src/gpu/GrAAHairLinePathRenderer.cpp | 15 ++- src/gpu/GrAAHairLinePathRenderer.h | 6 ++ src/gpu/GrClipMaskManager.cpp | 202 +++++++++++++++++++++++++++++++---- src/gpu/GrClipMaskManager.h | 2 + src/gpu/GrSoftwarePathRenderer.cpp | 31 +++--- src/gpu/GrSoftwarePathRenderer.h | 13 ++- 8 files changed, 248 insertions(+), 44 deletions(-) (limited to 'src/gpu') diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp index ca7ecdcb4a..3da4af2def 100644 --- a/src/gpu/GrAAConvexPathRenderer.cpp +++ b/src/gpu/GrAAConvexPathRenderer.cpp @@ -430,19 +430,26 @@ void create_vertices(const SegmentArray& segments, } -bool GrAAConvexPathRenderer::canDrawPath(const SkPath& path, - GrPathFill fill, - const GrDrawTarget* target, - bool antiAlias) const { +bool GrAAConvexPathRenderer::staticCanDrawPath(bool pathIsConvex, + GrPathFill fill, + const GrDrawTarget* target, + bool antiAlias) { if (!target->getCaps().fShaderDerivativeSupport || !antiAlias || kHairLine_PathFill == fill || GrIsFillInverted(fill) || - !path.isConvex()) { + !pathIsConvex) { return false; } else { return true; } } +bool GrAAConvexPathRenderer::canDrawPath(const SkPath& path, + GrPathFill fill, + const GrDrawTarget* target, + bool antiAlias) const { + return staticCanDrawPath(path.isConvex(), fill, target, antiAlias); +} + bool GrAAConvexPathRenderer::onDrawPath(const SkPath& origPath, GrPathFill fill, const GrVec* translate, diff --git a/src/gpu/GrAAConvexPathRenderer.h b/src/gpu/GrAAConvexPathRenderer.h index df0c00126a..875560c1ef 100644 --- a/src/gpu/GrAAConvexPathRenderer.h +++ b/src/gpu/GrAAConvexPathRenderer.h @@ -17,6 +17,12 @@ public: GrPathFill fill, const GrDrawTarget* target, bool antiAlias) const SK_OVERRIDE; + + static bool staticCanDrawPath(bool pathIsConvex, + GrPathFill fill, + const GrDrawTarget* target, + bool antiAlias); + protected: virtual bool onDrawPath(const SkPath& path, GrPathFill fill, diff --git a/src/gpu/GrAAHairLinePathRenderer.cpp b/src/gpu/GrAAHairLinePathRenderer.cpp index 701bf3a08d..94d94a378a 100644 --- a/src/gpu/GrAAHairLinePathRenderer.cpp +++ b/src/gpu/GrAAHairLinePathRenderer.cpp @@ -574,10 +574,10 @@ bool GrAAHairLinePathRenderer::createGeom( return true; } -bool GrAAHairLinePathRenderer::canDrawPath(const SkPath& path, - GrPathFill fill, - const GrDrawTarget* target, - bool antiAlias) const { +bool GrAAHairLinePathRenderer::staticCanDrawPath(const SkPath& path, + GrPathFill fill, + const GrDrawTarget* target, + bool antiAlias) { if (fill != kHairLine_PathFill || !antiAlias) { return false; } @@ -591,6 +591,13 @@ bool GrAAHairLinePathRenderer::canDrawPath(const SkPath& path, return true; } +bool GrAAHairLinePathRenderer::canDrawPath(const SkPath& path, + GrPathFill fill, + const GrDrawTarget* target, + bool antiAlias) const { + return staticCanDrawPath(path, fill, target, antiAlias); +} + bool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path, GrPathFill fill, const GrVec* translate, diff --git a/src/gpu/GrAAHairLinePathRenderer.h b/src/gpu/GrAAHairLinePathRenderer.h index 6dd9ea9f98..e45fa351fb 100644 --- a/src/gpu/GrAAHairLinePathRenderer.h +++ b/src/gpu/GrAAHairLinePathRenderer.h @@ -21,6 +21,12 @@ public: GrPathFill fill, const GrDrawTarget* target, bool antiAlias) const SK_OVERRIDE; + + static bool staticCanDrawPath(const SkPath& path, + GrPathFill fill, + const GrDrawTarget* target, + bool antiAlias); + protected: virtual bool onDrawPath(const SkPath& path, GrPathFill fill, diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp index f8e81a3b80..e618bf17f0 100644 --- a/src/gpu/GrClipMaskManager.cpp +++ b/src/gpu/GrClipMaskManager.cpp @@ -13,6 +13,11 @@ #include "GrPathRenderer.h" #include "GrPaint.h" #include "SkRasterClip.h" +#include "GrAAConvexPathRenderer.h" +#include "GrAAHairLinePathRenderer.h" + +// TODO: move GrSWMaskHelper out of GrSoftwarePathRender.h & remove this include +#include "GrSoftwarePathRenderer.h" //#define GR_AA_CLIP 1 //#define GR_SW_CLIP 1 @@ -55,12 +60,80 @@ void setup_drawstate_aaclip(GrGpu* gpu, GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(maskStage)); } -bool create_mask_in_sw() { - return false; } +/* + * This method traverses the clip stack to see if the GrSoftwarePathRenderer + * will be used on any element. If so, it returns true to indicate that the + * entire clip should be rendered in SW and then uploaded en masse to the gpu. + */ +bool GrClipMaskManager::useSWOnlyPath(GrGpu* gpu, const GrClip& clipIn) { + // TODO: this check is correct for the createAlphaClipMask path. + // The createStencilClipMask path does a lot more flip flopping of fill, + // etc - so this isn't quite correct in that case + + // TODO: generalize this test so that when + // a clip gets complex enough it can just be done in SW regardless + // of whether it would invoke the GrSoftwarePathRenderer. + bool useSW = false; + + for (int i = 0; i < clipIn.getElementCount(); ++i) { + + if (SkRegion::kReplace_Op == clipIn.getOp(i)) { + // Everything before a replace op can be ignored so start + // afresh w.r.t. determining if any element uses the SW path + useSW = false; + } + + if (!clipIn.getDoAA(i)) { + // non-anti-aliased rects and paths can always be drawn either + // directly or by the GrDefaultPathRenderer + continue; + } + + if (kRect_ClipType == clipIn.getElementType(i)) { + // Antialiased rects are converted to paths and then drawn with + // kEvenOdd_PathFill. + if (!GrAAConvexPathRenderer::staticCanDrawPath( + true, // always convex + kEvenOdd_PathFill, + gpu, true)) { + // if the GrAAConvexPathRenderer can't render this rect (due + // to lack of derivative support in the shaders) then + // the GrSoftwarePathRenderer will be used + useSW = true; + } + + continue; + } + + // only paths need to be considered in the rest of the loop body + + if (GrAAHairLinePathRenderer::staticCanDrawPath(clipIn.getPath(i), + clipIn.getPathFill(i), + gpu, + clipIn.getDoAA(i))) { + // the hair line path renderer can handle this one + continue; + } + + if (GrAAConvexPathRenderer::staticCanDrawPath( + clipIn.getPath(i).isConvex(), + clipIn.getPathFill(i), + gpu, + clipIn.getDoAA(i))) { + // the convex path renderer can handle this one + continue; + } + + // otherwise the GrSoftwarePathRenderer is going to be invoked + useSW = true; + } + + return useSW; } + //////////////////////////////////////////////////////////////////////////////// // sort out what kind of clip mask needs to be created: alpha, stencil, // scissor, or entirely software @@ -85,7 +158,7 @@ bool GrClipMaskManager::createClipMask(GrGpu* gpu, GrAssert(NULL != rt); #if GR_SW_CLIP - if (create_mask_in_sw()) { + if (useSWOnlyPath(gpu, clipIn)) { // The clip geometry is complex enough that it will be more // efficient to create it entirely in software GrTexture* result = NULL; @@ -133,7 +206,7 @@ bool GrClipMaskManager::createClipMask(GrGpu* gpu, GrRect bounds; GrRect rtRect; rtRect.setLTRB(0, 0, - GrIntToScalar(rt->width()), GrIntToScalar(rt->height())); + GrIntToScalar(rt->width()), GrIntToScalar(rt->height())); if (clipIn.hasConservativeBounds()) { bounds = clipIn.getConservativeBounds(); if (!bounds.intersect(rtRect)) { @@ -786,6 +859,27 @@ bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu, return true; } +namespace { + +GrPathFill invert_fill(GrPathFill fill) { + static const GrPathFill gInvertedFillTable[] = { + kInverseWinding_PathFill, // kWinding_PathFill + kInverseEvenOdd_PathFill, // kEvenOdd_PathFill + kWinding_PathFill, // kInverseWinding_PathFill + kEvenOdd_PathFill, // kInverseEvenOdd_PathFill + kHairLine_PathFill, // kHairLine_PathFill + }; + GR_STATIC_ASSERT(0 == kWinding_PathFill); + GR_STATIC_ASSERT(1 == kEvenOdd_PathFill); + GR_STATIC_ASSERT(2 == kInverseWinding_PathFill); + GR_STATIC_ASSERT(3 == kInverseEvenOdd_PathFill); + GR_STATIC_ASSERT(4 == kHairLine_PathFill); + GR_STATIC_ASSERT(5 == kPathFillCount); + return gInvertedFillTable[fill]; +} + +} + //////////////////////////////////////////////////////////////////////////////// bool GrClipMaskManager::createSoftwareClipMask(GrGpu* gpu, const GrClip& clipIn, @@ -803,30 +897,98 @@ bool GrClipMaskManager::createSoftwareClipMask(GrGpu* gpu, return false; } -#if 0 - SkRasterClip rasterClip; + GrSWMaskHelper helper(fAACache.getContext()); - // TODO: refactor GrClip out of existance and use SkCanvas's ClipVisitor - // - may have to move it to SkClipStack - for (int i = 0; i < clipIn.getElementCount(); ++i) { + helper.init(*resultBounds, NULL, false); + + int count = clipIn.getElementCount(); + + bool clearToInside; + SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning + int start = process_initial_clip_elements(clipIn, + *resultBounds, + &clearToInside, + &startOp); + + helper.clear(clearToInside ? SK_ColorWHITE : 0x00000000); + + for (int i = start; i < count; ++i) { + + SkRegion::Op op = (i == start) ? startOp : clipIn.getOp(i); + + if (SkRegion::kIntersect_Op == op || + SkRegion::kReverseDifference_Op == op) { + // Intersect and reverse difference require modifying pixels + // outside of the geometry that is being "drawn". In both cases + // we erase all the pixels outside of the geometry but + // leave the pixels inside the geometry alone. For reverse + // difference we invert all the pixels before clearing the ones + // outside the geometry. + if (SkRegion::kReverseDifference_Op == op) { + SkRect temp = SkRect::MakeLTRB( + SkIntToScalar(resultBounds->left()), + SkIntToScalar(resultBounds->top()), + SkIntToScalar(resultBounds->right()), + SkIntToScalar(resultBounds->bottom())); + + // invert the entire scene + helper.draw(temp, SkRegion::kXOR_Op, false, SK_ColorWHITE); + } + + if (kRect_ClipType == clipIn.getElementType(i)) { + + // convert the rect to a path so we can invert the fill + SkPath temp; + temp.addRect(clipIn.getRect(i)); + + helper.draw(temp, SkRegion::kReplace_Op, + kInverseEvenOdd_PathFill, clipIn.getDoAA(i), + 0x00000000); + } else { + GrAssert(kPath_ClipType == clipIn.getElementType(i)); + + helper.draw(clipIn.getPath(i), + SkRegion::kReplace_Op, + invert_fill(clipIn.getPathFill(i)), + clipIn.getDoAA(i), + 0x00000000); + } + + continue; + } + + // The other ops (union, xor, diff) only affect pixels inside + // the geometry so they can just be drawn normally if (kRect_ClipType == clipIn.getElementType(i)) { - rasterClip.op(clipIn.getRect(i), clipIn.getOp(i), clipIn.getDoAA(i)); + + helper.draw(clipIn.getRect(i), + op, + clipIn.getDoAA(i), SK_ColorWHITE); + } else { GrAssert(kPath_ClipType == clipIn.getElementType(i)); - SkIPoint deviceSize = SkIPoint::Make(resultBounds->width(), - resultBounds->height()); - - SkRasterClip::clipPathHelper(&rasterClip, - clipIn.getPath(i), - clipIn.getOp(i), - clipIn.getDoAA(i), - deviceSize); + helper.draw(clipIn.getPath(i), + op, + clipIn.getPathFill(i), + clipIn.getDoAA(i), SK_ColorWHITE); } } - // TODO: need to get pixels out of SkRasterClip & into the texture! -#endif + // Because we are using the scratch texture cache, "accum" may be + // larger than expected and have some cruft in the areas we aren't using. + // Clear it out. + + // TODO: need a simpler way to clear the texture - can we combine + // the clear and the writePixels (inside toTexture) + GrDrawState* drawState = gpu->drawState(); + GrAssert(NULL != drawState); + GrRenderTarget* temp = drawState->getRenderTarget(); + clear(gpu, accum, 0x00000000); + // can't leave the accum bound as a rendertarget + drawState->setRenderTarget(temp); + + helper.toTexture(accum); *result = accum; diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h index 02fc875f83..d5fcc831aa 100644 --- a/src/gpu/GrClipMaskManager.h +++ b/src/gpu/GrClipMaskManager.h @@ -332,6 +332,8 @@ private: GrTexture** result, GrIRect *resultBounds); + bool useSWOnlyPath(GrGpu* gpu, const GrClip& clipIn); + bool drawPath(GrGpu* gpu, const SkPath& path, GrPathFill fill, diff --git a/src/gpu/GrSoftwarePathRenderer.cpp b/src/gpu/GrSoftwarePathRenderer.cpp index 410223cd62..817f55a86d 100644 --- a/src/gpu/GrSoftwarePathRenderer.cpp +++ b/src/gpu/GrSoftwarePathRenderer.cpp @@ -101,7 +101,7 @@ bool get_path_and_clip_bounds(const GrDrawTarget* target, SkXfermode::Mode op_to_mode(SkRegion::Op op) { static const SkXfermode::Mode modeMap[] = { - SkXfermode::kSrcOut_Mode, // kDifference_Op + SkXfermode::kDstOut_Mode, // kDifference_Op SkXfermode::kMultiply_Mode, // kIntersect_Op SkXfermode::kSrcOver_Mode, // kUnion_Op SkXfermode::kXor_Mode, // kXOR_Op @@ -118,14 +118,14 @@ SkXfermode::Mode op_to_mode(SkRegion::Op op) { * Draw a single rect element of the clip stack into the accumulation bitmap */ void GrSWMaskHelper::draw(const GrRect& clientRect, SkRegion::Op op, - bool antiAlias) { + bool antiAlias, GrColor color) { SkPaint paint; SkXfermode* mode = SkXfermode::Create(op_to_mode(op)); paint.setXfermode(mode); paint.setAntiAlias(antiAlias); - paint.setColor(SK_ColorWHITE); + paint.setColor(color); fDraw.drawRect(clientRect, paint); @@ -136,7 +136,7 @@ void GrSWMaskHelper::draw(const GrRect& clientRect, SkRegion::Op op, * Draw a single path element of the clip stack into the accumulation bitmap */ void GrSWMaskHelper::draw(const SkPath& clientPath, SkRegion::Op op, - GrPathFill fill, bool antiAlias) { + GrPathFill fill, bool antiAlias, GrColor color) { SkPaint paint; SkPath tmpPath; @@ -157,23 +157,30 @@ void GrSWMaskHelper::draw(const SkPath& clientPath, SkRegion::Op op, paint.setXfermode(mode); paint.setAntiAlias(antiAlias); - paint.setColor(SK_ColorWHITE); + paint.setColor(color); fDraw.drawPath(*pathToDraw, paint); SkSafeUnref(mode); } -bool GrSWMaskHelper::init(const GrIRect& pathDevBounds, const GrPoint* translate) { - fMatrix = fContext->getMatrix(); +bool GrSWMaskHelper::init(const GrIRect& pathDevBounds, + const GrPoint* translate, + bool useMatrix) { + if (useMatrix) { + fMatrix = fContext->getMatrix(); + } else { + fMatrix.setIdentity(); + } + if (NULL != translate) { fMatrix.postTranslate(translate->fX, translate->fY); } fMatrix.postTranslate(-pathDevBounds.fLeft * SK_Scalar1, - -pathDevBounds.fTop * SK_Scalar1); + -pathDevBounds.fTop * SK_Scalar1); GrIRect bounds = GrIRect::MakeWH(pathDevBounds.width(), - pathDevBounds.height()); + pathDevBounds.height()); fBM.setConfig(SkBitmap::kA8_Config, bounds.fRight, bounds.fBottom); if (!fBM.allocPixels()) { @@ -237,11 +244,12 @@ bool sw_draw_path_to_mask_texture(const SkPath& clientPath, bool antiAlias) { GrSWMaskHelper helper(context); - if (!helper.init(pathDevBounds, translate)) { + if (!helper.init(pathDevBounds, translate, true)) { return false; } - helper.draw(clientPath, SkRegion::kReplace_Op, fill, antiAlias); + helper.draw(clientPath, SkRegion::kReplace_Op, + fill, antiAlias, SK_ColorWHITE); if (!helper.getTexture(tex)) { return false; @@ -339,4 +347,3 @@ bool GrSoftwarePathRenderer::onDrawPath(const SkPath& path, return false; } - diff --git a/src/gpu/GrSoftwarePathRenderer.h b/src/gpu/GrSoftwarePathRenderer.h index 4b75d036f3..74715f6812 100644 --- a/src/gpu/GrSoftwarePathRenderer.h +++ b/src/gpu/GrSoftwarePathRenderer.h @@ -28,17 +28,24 @@ public: } - void draw(const GrRect& clientRect, SkRegion::Op op, bool antiAlias); + void draw(const GrRect& clientRect, SkRegion::Op op, + bool antiAlias, GrColor color); void draw(const SkPath& clientPath, SkRegion::Op op, - GrPathFill fill, bool antiAlias); + GrPathFill fill, bool antiAlias, GrColor color); - bool init(const GrIRect& pathDevBounds, const GrPoint* translate); + bool init(const GrIRect& pathDevBounds, + const GrPoint* translate, + bool useMatrix); bool getTexture(GrAutoScratchTexture* tex); void toTexture(GrTexture* texture); + void clear(GrColor color) { + fBM.eraseColor(color); + } + protected: private: GrContext* fContext; -- cgit v1.2.3