From: robertphillips Date: Wed, 10 Aug 2016 14:14:55 +0000 (-0700) Subject: Create blurred RRect mask on GPU (rather than uploading it) X-Git-Tag: accepted/tizen/5.0/unified/20181102.025319~116^2~310 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=94b5c5a41160e0f55e267fc3d830df65736fac50;p=platform%2Fupstream%2FlibSkiaSharp.git Create blurred RRect mask on GPU (rather than uploading it) This CL doesn't try to resolve any of the larger issues. It just moves the computation of the blurred RRect to the gpu and sets up to start using vertex attributes for a nine patch draw (i.e., returning the texture coordinates) All blurred rrects using the "analytic" path will change slightly with this CL. GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2222083004 Committed: https://skia.googlesource.com/skia/+/75ccdc77a70ec2083141bf9ba98eb2f01ece2479 Review-Url: https://codereview.chromium.org/2222083004 --- diff --git a/include/core/SkMaskFilter.h b/include/core/SkMaskFilter.h index 908226c..e30fc62 100644 --- a/include/core/SkMaskFilter.h +++ b/include/core/SkMaskFilter.h @@ -17,6 +17,7 @@ #include "SkStrokeRec.h" class GrClip; +class GrContext; class GrDrawContext; class GrPaint; class GrRenderTarget; @@ -122,7 +123,7 @@ public: * Try to directly render a rounded rect mask filter into the target. Returns * true if drawing was successful. */ - virtual bool directFilterRRectMaskGPU(GrTextureProvider* texProvider, + virtual bool directFilterRRectMaskGPU(GrContext*, GrDrawContext* drawContext, GrPaint* grp, const GrClip&, diff --git a/include/effects/SkBlurMaskFilter.h b/include/effects/SkBlurMaskFilter.h index 3ba9177..4b037e7 100644 --- a/include/effects/SkBlurMaskFilter.h +++ b/include/effects/SkBlurMaskFilter.h @@ -65,6 +65,16 @@ public: SkScalar blurRadius); #endif + static bool ComputeBlurredRRectParams(const SkRRect& rrect, + SkScalar sigma, + SkRRect* rrectToDraw, + SkISize* widthHeight, + SkScalar xs[4], + int* numXs, + SkScalar ys[4], + int* numYs); + + SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() private: diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp index fccce45..352cf71 100644 --- a/src/core/SkMaskFilter.cpp +++ b/src/core/SkMaskFilter.cpp @@ -324,7 +324,7 @@ bool SkMaskFilter::canFilterMaskGPU(const SkRRect& devRRect, } -bool SkMaskFilter::directFilterRRectMaskGPU(GrTextureProvider* texProvider, +bool SkMaskFilter::directFilterRRectMaskGPU(GrContext*, GrDrawContext* drawContext, GrPaint* grp, const GrClip&, diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp index 4215733..31433dd 100644 --- a/src/effects/SkBlurMaskFilter.cpp +++ b/src/effects/SkBlurMaskFilter.cpp @@ -22,7 +22,7 @@ #include "GrTexture.h" #include "GrFragmentProcessor.h" #include "GrInvariantOutput.h" -#include "SkDraw.h" +#include "GrStyle.h" #include "effects/GrSimpleTextureEffect.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" @@ -56,7 +56,7 @@ public: const SkMatrix& viewMatrix, const SkStrokeRec& strokeRec, const SkPath& path) const override; - bool directFilterRRectMaskGPU(GrTextureProvider* texProvider, + bool directFilterRRectMaskGPU(GrContext*, GrDrawContext* drawContext, GrPaint* grp, const GrClip&, @@ -910,7 +910,7 @@ bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrTextureProvider* texProvider, class GrRRectBlurEffect : public GrFragmentProcessor { public: - static sk_sp Make(GrTextureProvider*, float sigma, const SkRRect&); + static sk_sp Make(GrContext*, float sigma, const SkRRect&); virtual ~GrRRectBlurEffect() {}; const char* name() const override { return "GrRRectBlur"; } @@ -939,13 +939,110 @@ private: typedef GrFragmentProcessor INHERITED; }; +bool SkBlurMaskFilter::ComputeBlurredRRectParams(const SkRRect& rrect, + SkScalar sigma, + SkRRect* rrectToDraw, + SkISize* widthHeight, + SkScalar xs[4], + int* numXs, + SkScalar ys[4], + int* numYs) { + unsigned int blurRadius = 3*SkScalarCeilToInt(sigma-1/6.0f); + + const SkRect& orig = rrect.getBounds(); + const SkVector& radiiUL = rrect.radii(SkRRect::kUpperLeft_Corner); + const SkVector& radiiUR = rrect.radii(SkRRect::kUpperRight_Corner); + const SkVector& radiiLR = rrect.radii(SkRRect::kLowerRight_Corner); + const SkVector& radiiLL = rrect.radii(SkRRect::kLowerLeft_Corner); + + const int left = SkScalarCeilToInt(SkTMax(radiiUL.fX, radiiLL.fX)); + const int top = SkScalarCeilToInt(SkTMax(radiiUL.fY, radiiUR.fY)); + const int right = SkScalarCeilToInt(SkTMax(radiiUR.fX, radiiLR.fX)); + const int bot = SkScalarCeilToInt(SkTMax(radiiLL.fY, radiiLR.fY)); + + // This is a conservative check for nine-patchability + if (orig.fLeft + left + blurRadius >= orig.fRight - right - blurRadius || + orig.fTop + top + blurRadius >= orig.fBottom - bot - blurRadius) { + return false; + } -sk_sp GrRRectBlurEffect::Make(GrTextureProvider* texProvider, float sigma, - const SkRRect& rrect) { + int newRRWidth, newRRHeight; + + // 3x3 case + newRRWidth = 2*blurRadius + left + right + 1; + newRRHeight = 2*blurRadius + top + bot + 1; + widthHeight->fWidth = newRRWidth + 2 * blurRadius; + widthHeight->fHeight = newRRHeight + 2 * blurRadius; + // TODO: need to return non-normalized indices + xs[0] = 0.0f; + xs[1] = (blurRadius + left) / (float) widthHeight->fWidth; + xs[2] = (blurRadius + left + 1.0f) / widthHeight->fWidth; + xs[3] = 1.0f; + *numXs = 4; + ys[0] = 0.0f; + ys[1] = (blurRadius + top) / (float) widthHeight->fHeight; + ys[2] = (blurRadius + top + 1.0f) / widthHeight->fHeight; + ys[3] = 1.0f; + *numYs = 4; + + const SkRect newRect = SkRect::MakeXYWH(SkIntToScalar(blurRadius), SkIntToScalar(blurRadius), + SkIntToScalar(newRRWidth), SkIntToScalar(newRRHeight)); + SkVector newRadii[4]; + newRadii[0] = { SkScalarCeilToScalar(radiiUL.fX), SkScalarCeilToScalar(radiiUL.fY) }; + newRadii[1] = { SkScalarCeilToScalar(radiiUR.fX), SkScalarCeilToScalar(radiiUR.fY) }; + newRadii[2] = { SkScalarCeilToScalar(radiiLR.fX), SkScalarCeilToScalar(radiiLR.fY) }; + newRadii[3] = { SkScalarCeilToScalar(radiiLL.fX), SkScalarCeilToScalar(radiiLL.fY) }; + + rrectToDraw->setRectRadii(newRect, newRadii); + return true; +} + +static sk_sp make_rrect_blur_mask(GrContext* context, + const SkRRect& rrect, + float sigma) { + SkRRect rrectToDraw; + SkISize size; + SkScalar xs[4], ys[4]; + int numXs, numYs; + + SkBlurMaskFilter::ComputeBlurredRRectParams(rrect, sigma, &rrectToDraw, &size, + xs, &numXs, ys, &numYs); + + // TODO: this could be approx but the texture coords will need to be updated + sk_sp dc(context->makeDrawContext(SkBackingFit::kExact, + size.fWidth, size.fHeight, + kAlpha_8_GrPixelConfig, nullptr)); + if (!dc) { + return nullptr; + } + + GrPaint grPaint; + + dc->clear(nullptr, SK_ColorTRANSPARENT, true); + dc->drawRRect(GrNoClip(), grPaint, SkMatrix::I(), rrectToDraw, GrStyle::SimpleFill()); + + sk_sp dc2(SkGpuBlurUtils::GaussianBlur(context, + dc->asTexture().release(), + nullptr, + SkIRect::MakeWH(size.fWidth, + size.fHeight), + nullptr, + sigma, sigma, SkBackingFit::kExact)); + if (!dc2) { + return nullptr; + } + + return dc2->asTexture(); +} + +sk_sp GrRRectBlurEffect::Make(GrContext* context, float sigma, + const SkRRect& rrect) { if (rrect.isCircle()) { - return GrCircleBlurFragmentProcessor::Make(texProvider, rrect.rect(), sigma); + return GrCircleBlurFragmentProcessor::Make(context->textureProvider(), + rrect.rect(), sigma); } + // TODO: loosen this up if (!rrect.isSimpleCircular()) { return nullptr; } @@ -968,55 +1065,17 @@ sk_sp GrRRectBlurEffect::Make(GrTextureProvider* texProvide builder[1] = cornerRadius; builder.finish(); - SkAutoTUnref blurNinePatchTexture(texProvider->findAndRefTextureByUniqueKey(key)); + sk_sp blurNinePatchTexture( + context->textureProvider()->findAndRefTextureByUniqueKey(key)); if (!blurNinePatchTexture) { - SkMask mask; - - unsigned int smallRectSide = 2*(blurRadius + cornerRadius) + 1; - - mask.fBounds = SkIRect::MakeWH(smallRectSide, smallRectSide); - mask.fFormat = SkMask::kA8_Format; - mask.fRowBytes = mask.fBounds.width(); - mask.fImage = SkMask::AllocImage(mask.computeTotalImageSize()); - SkAutoMaskFreeImage amfi(mask.fImage); - - memset(mask.fImage, 0, mask.computeTotalImageSize()); - - SkRect smallRect; - smallRect.setWH(SkIntToScalar(smallRectSide), SkIntToScalar(smallRectSide)); - - SkRRect smallRRect; - smallRRect.setRectXY(smallRect, SkIntToScalar(cornerRadius), SkIntToScalar(cornerRadius)); - - SkPath path; - path.addRRect(smallRRect); - - SkDraw::DrawToMask(path, &mask.fBounds, nullptr, nullptr, &mask, - SkMask::kJustRenderImage_CreateMode, SkStrokeRec::kFill_InitStyle); - - SkMask blurredMask; - if (!SkBlurMask::BoxBlur(&blurredMask, mask, sigma, kNormal_SkBlurStyle, - kHigh_SkBlurQuality, nullptr, true)) { - return nullptr; - } - - unsigned int texSide = smallRectSide + 2*blurRadius; - GrSurfaceDesc texDesc; - texDesc.fWidth = texSide; - texDesc.fHeight = texSide; - texDesc.fConfig = kAlpha_8_GrPixelConfig; - texDesc.fIsMipMapped = false; - - blurNinePatchTexture.reset( - texProvider->createTexture(texDesc, SkBudgeted::kYes , blurredMask.fImage, 0)); - SkMask::FreeImage(blurredMask.fImage); + blurNinePatchTexture = make_rrect_blur_mask(context, rrect, sigma); if (!blurNinePatchTexture) { return nullptr; } - texProvider->assignUniqueKeyToTexture(key, blurNinePatchTexture); + context->textureProvider()->assignUniqueKeyToTexture(key, blurNinePatchTexture.get()); } - return sk_sp(new GrRRectBlurEffect(sigma, rrect, blurNinePatchTexture)); + return sk_sp(new GrRRectBlurEffect(sigma, rrect, blurNinePatchTexture.get())); } void GrRRectBlurEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const { @@ -1050,7 +1109,7 @@ sk_sp GrRRectBlurEffect::TestCreate(GrProcessorTestData* d) SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f); SkRRect rrect; rrect.setRectXY(SkRect::MakeWH(w, h), r, r); - return GrRRectBlurEffect::Make(d->fContext->textureProvider(), sigma, rrect); + return GrRRectBlurEffect::Make(d->fContext, sigma, rrect); } ////////////////////////////////////////////////////////////////////////////// @@ -1153,7 +1212,7 @@ GrGLSLFragmentProcessor* GrRRectBlurEffect::onCreateGLSLInstance() const { return new GrGLRRectBlurEffect; } -bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrTextureProvider* texProvider, +bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrContext* context, GrDrawContext* drawContext, GrPaint* grp, const GrClip& clip, @@ -1172,7 +1231,7 @@ bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrTextureProvider* texProvid SkScalar xformedSigma = this->computeXformedSigma(viewMatrix); - sk_sp fp(GrRRectBlurEffect::Make(texProvider, xformedSigma, rrect)); + sk_sp fp(GrRRectBlurEffect::Make(context, xformedSigma, rrect)); if (!fp) { return false; } diff --git a/src/effects/SkGpuBlurUtils.cpp b/src/effects/SkGpuBlurUtils.cpp index 869cd76..ebb480d 100644 --- a/src/effects/SkGpuBlurUtils.cpp +++ b/src/effects/SkGpuBlurUtils.cpp @@ -186,7 +186,8 @@ sk_sp GaussianBlur(GrContext* context, const SkIRect& dstBounds, const SkIRect* srcBounds, float sigmaX, - float sigmaY) { + float sigmaY, + SkBackingFit fit) { SkASSERT(context); SkIRect clearRect; int scaleFactorX, radiusX; @@ -226,7 +227,7 @@ sk_sp GaussianBlur(GrContext* context, const int height = dstBounds.height(); const GrPixelConfig config = srcTexture->config(); - sk_sp dstDrawContext(context->makeDrawContext(SkBackingFit::kApprox, + sk_sp dstDrawContext(context->makeDrawContext(fit, width, height, config, colorSpace, 0, kDefault_GrSurfaceOrigin)); if (!dstDrawContext) { @@ -246,7 +247,7 @@ sk_sp GaussianBlur(GrContext* context, return dstDrawContext; } - sk_sp tmpDrawContext(context->makeDrawContext(SkBackingFit::kApprox, + sk_sp tmpDrawContext(context->makeDrawContext(fit, width, height, config, colorSpace, 0, kDefault_GrSurfaceOrigin)); if (!tmpDrawContext) { diff --git a/src/effects/SkGpuBlurUtils.h b/src/effects/SkGpuBlurUtils.h index a5de6a2..a12a088 100644 --- a/src/effects/SkGpuBlurUtils.h +++ b/src/effects/SkGpuBlurUtils.h @@ -29,6 +29,7 @@ namespace SkGpuBlurUtils { * no pixels will be sampled outside of this rectangle. * @param sigmaX The blur's standard deviation in X. * @param sigmaY The blur's standard deviation in Y. + * @param fit backing fit for the returned draw context * @return The drawContext containing the blurred result. */ sk_sp GaussianBlur(GrContext* context, @@ -37,7 +38,8 @@ namespace SkGpuBlurUtils { const SkIRect& dstBounds, const SkIRect* srcBounds, float sigmaX, - float sigmaY); + float sigmaY, + SkBackingFit fit = SkBackingFit::kApprox); }; #endif diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index cd34b1f..fb513f4 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -436,7 +436,7 @@ void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rrect, // clipped out return; } - if (paint.getMaskFilter()->directFilterRRectMaskGPU(fContext->textureProvider(), + if (paint.getMaskFilter()->directFilterRRectMaskGPU(fContext, fDrawContext.get(), &grPaint, fClip, diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp index 2843c31..e141cc2 100644 --- a/src/gpu/SkGpuDevice_drawTexture.cpp +++ b/src/gpu/SkGpuDevice_drawTexture.cpp @@ -226,7 +226,7 @@ void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer, // First see if we can do the draw + mask filter direct to the dst. SkStrokeRec rec(SkStrokeRec::kFill_InitStyle); - if (mf->directFilterRRectMaskGPU(fContext->textureProvider(), + if (mf->directFilterRRectMaskGPU(fContext, fDrawContext.get(), &grPaint, clip, diff --git a/tests/BlurTest.cpp b/tests/BlurTest.cpp index 6ccb047..32e2930 100644 --- a/tests/BlurTest.cpp +++ b/tests/BlurTest.cpp @@ -574,4 +574,66 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SmallBoxBlurBug, reporter, ctxInfo) { #endif + +DEF_TEST(BlurredRRectNinePatchComputation, reporter) { + const SkRect r = SkRect::MakeXYWH(10, 10, 100, 100); + + bool ninePatchable; + SkRRect rrectToDraw; + SkISize size; + SkScalar xs[4], ys[4]; + int numXs, numYs; + + // not nine-patchable + { + SkVector radii[4] = { { 100, 100 }, { 0, 0 }, { 100, 100 }, { 0, 0 } }; + + SkRRect rr; + rr.setRectRadii(r, radii); + + ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, 3.0f, &rrectToDraw, &size, + xs, &numXs, ys, &numYs); + REPORTER_ASSERT(reporter, !ninePatchable); + } + + // simple circular + { + SkRRect rr; + rr.setRectXY(r, 10, 10); + + ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, 3.0f, &rrectToDraw, &size, + xs, &numXs, ys, &numYs); + REPORTER_ASSERT(reporter, ninePatchable); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fWidth), 57.0f)); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fHeight), 57.0)); + REPORTER_ASSERT(reporter, 4 == numXs && 4 == numYs); + for (int i = 0; i < numXs; ++i) { + REPORTER_ASSERT(reporter, xs[i] >= 0.0f && xs[i] <= 1.0f); + } + for (int i = 0; i < numYs; ++i) { + REPORTER_ASSERT(reporter, ys[i] >= 0.0f && ys[i] <= 1.0f); + } + } + + // simple elliptical + { + SkRRect rr; + rr.setRectXY(r, 2, 10); + + ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, 3.0f, &rrectToDraw, &size, + xs, &numXs, ys, &numYs); + REPORTER_ASSERT(reporter, ninePatchable); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fWidth), 41.0f)); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fHeight), 57.0)); + REPORTER_ASSERT(reporter, 4 == numXs && 4 == numYs); + for (int i = 0; i < numXs; ++i) { + REPORTER_ASSERT(reporter, xs[i] >= 0.0f && xs[i] <= 1.0f); + } + for (int i = 0; i < numYs; ++i) { + REPORTER_ASSERT(reporter, ys[i] >= 0.0f && ys[i] <= 1.0f); + } + } + +} + ///////////////////////////////////////////////////////////////////////////////////////////