aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-01-27 22:41:45 +0000
committerGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-01-27 22:41:45 +0000
commitd1829151a5f5fb69faa50c326ed018376d117b3e (patch)
treee424d35b0b3d4be436b10ca361577e18a68702ef /src
parent01260b2dbcc070006c0b960015e44ef83dcbaa56 (diff)
Perform the same analytic blur calculation on the GPU that we do on the CPU. Results in significant performance gains when using Ganesh to render drop shadows in Chrome.
BUG= R=bsalomon@google.com, reed@google.com Author: humper@google.com Review URL: https://codereview.chromium.org/119343003 git-svn-id: http://skia.googlecode.com/svn/trunk@13210 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src')
-rw-r--r--src/core/SkMaskFilter.cpp7
-rw-r--r--src/effects/SkBlurMask.cpp70
-rw-r--r--src/effects/SkBlurMask.h35
-rw-r--r--src/effects/SkBlurMaskFilter.cpp276
-rw-r--r--src/gpu/SkGpuDevice.cpp9
5 files changed, 362 insertions, 35 deletions
diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp
index ebcdbea4d2..b96743d129 100644
--- a/src/core/SkMaskFilter.cpp
+++ b/src/core/SkMaskFilter.cpp
@@ -309,6 +309,13 @@ bool SkMaskFilter::canFilterMaskGPU(const SkRect& devBounds,
return false;
}
+ bool SkMaskFilter::directFilterMaskGPU(GrContext* context,
+ GrPaint* grp,
+ const SkStrokeRec& strokeRec,
+ const SkPath& path) const {
+ return false;
+}
+
bool SkMaskFilter::filterMaskGPU(GrTexture* src,
const SkMatrix& ctm,
diff --git a/src/effects/SkBlurMask.cpp b/src/effects/SkBlurMask.cpp
index 32a3d20d31..f61536e65a 100644
--- a/src/effects/SkBlurMask.cpp
+++ b/src/effects/SkBlurMask.cpp
@@ -671,7 +671,7 @@ static float gaussianIntegral(float x) {
return 0.4375f + (-x3 / 6.0f - 3.0f * x2 * 0.25f - 1.125f * x);
}
-/* compute_profile allocates and fills in an array of floating
+/* ComputeBlurProfile allocates and fills in an array of floating
point values between 0 and 255 for the profile signature of
a blurred half-plane with the given blur radius. Since we're
going to be doing screened multiplications (i.e., 1 - (1-x)(1-y))
@@ -682,11 +682,11 @@ static float gaussianIntegral(float x) {
memory returned in profile_out.
*/
-static void compute_profile(SkScalar sigma, unsigned int **profile_out) {
+void SkBlurMask::ComputeBlurProfile(SkScalar sigma, uint8_t **profile_out) {
int size = SkScalarCeilToInt(6*sigma);
int center = size >> 1;
- unsigned int *profile = SkNEW_ARRAY(unsigned int, size);
+ uint8_t *profile = SkNEW_ARRAY(uint8_t, size);
float invr = 1.f/(2*sigma);
@@ -707,7 +707,7 @@ static void compute_profile(SkScalar sigma, unsigned int **profile_out) {
// Implementation adapted from Michael Herf's approach:
// http://stereopsis.com/shadowrect/
-static inline unsigned int profile_lookup( unsigned int *profile, int loc, int blurred_width, int sharp_width ) {
+uint8_t SkBlurMask::ProfileLookup(const uint8_t *profile, int loc, int blurred_width, int sharp_width) {
int dx = SkAbs32(((loc << 1) + 1) - blurred_width) - sharp_width; // how far are we from the original edge?
int ox = dx >> 1;
if (ox < 0) {
@@ -717,6 +717,30 @@ static inline unsigned int profile_lookup( unsigned int *profile, int loc, int b
return profile[ox];
}
+void SkBlurMask::ComputeBlurredScanline(uint8_t *pixels, const uint8_t *profile,
+ unsigned int width, SkScalar sigma) {
+
+ unsigned int profile_size = SkScalarCeilToInt(6*sigma);
+ SkAutoTMalloc<uint8_t> horizontalScanline(width);
+
+ unsigned int sw = width - profile_size;
+ // nearest odd number less than the profile size represents the center
+ // of the (2x scaled) profile
+ int center = ( profile_size & ~1 ) - 1;
+
+ int w = sw - center;
+
+ for (unsigned int x = 0 ; x < width ; ++x) {
+ if (profile_size <= sw) {
+ pixels[x] = ProfileLookup(profile, x, width, w);
+ } else {
+ float span = float(sw)/(2*sigma);
+ float giX = 1.5f - (x+.5f)/(2*sigma);
+ pixels[x] = (uint8_t) (255 * (gaussianIntegral(giX) - gaussianIntegral(giX + span)));
+ }
+ }
+}
+
bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src,
SkScalar radius, Style style,
SkIPoint *margin, SkMask::CreateMode createMode) {
@@ -757,10 +781,10 @@ bool SkBlurMask::BlurRect(SkScalar sigma, SkMask *dst,
}
return true;
}
- unsigned int *profile = NULL;
+ uint8_t *profile = NULL;
- compute_profile(sigma, &profile);
- SkAutoTDeleteArray<unsigned int> ada(profile);
+ ComputeBlurProfile(sigma, &profile);
+ SkAutoTDeleteArray<uint8_t> ada(profile);
size_t dstSize = dst->computeImageSize();
if (0 == dstSize) {
@@ -774,39 +798,17 @@ bool SkBlurMask::BlurRect(SkScalar sigma, SkMask *dst,
int dstHeight = dst->fBounds.height();
int dstWidth = dst->fBounds.width();
- // nearest odd number less than the profile size represents the center
- // of the (2x scaled) profile
- int center = ( profile_size & ~1 ) - 1;
-
- int w = sw - center;
- int h = sh - center;
-
uint8_t *outptr = dp;
SkAutoTMalloc<uint8_t> horizontalScanline(dstWidth);
-
- for (int x = 0 ; x < dstWidth ; ++x) {
- if (profile_size <= sw) {
- horizontalScanline[x] = profile_lookup(profile, x, dstWidth, w);
- } else {
- float span = float(sw)/(2*sigma);
- float giX = 1.5f - (x+.5f)/(2*sigma);
- horizontalScanline[x] = (uint8_t) (255 * (gaussianIntegral(giX) - gaussianIntegral(giX + span)));
- }
- }
+ SkAutoTMalloc<uint8_t> verticalScanline(dstHeight);
+
+ ComputeBlurredScanline(horizontalScanline, profile, dstWidth, sigma);
+ ComputeBlurredScanline(verticalScanline, profile, dstHeight, sigma);
for (int y = 0 ; y < dstHeight ; ++y) {
- unsigned int profile_y;
- if (profile_size <= sh) {
- profile_y = profile_lookup(profile, y, dstHeight, h);
- } else {
- float span = float(sh)/(2*sigma);
- float giY = 1.5f - (y+.5f)/(2*sigma);
- profile_y = (uint8_t) (255 * (gaussianIntegral(giY) - gaussianIntegral(giY + span)));
- }
-
for (int x = 0 ; x < dstWidth ; x++) {
- unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], profile_y);
+ unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], verticalScanline[y]);
*(outptr++) = maskval;
}
}
diff --git a/src/effects/SkBlurMask.h b/src/effects/SkBlurMask.h
index e0b8d54ce1..f95c110c90 100644
--- a/src/effects/SkBlurMask.h
+++ b/src/effects/SkBlurMask.h
@@ -62,6 +62,41 @@ public:
SkIPoint* margin = NULL);
static SkScalar ConvertRadiusToSigma(SkScalar radius);
+
+ /* Helper functions for analytic rectangle blurs */
+
+ /** Look up the intensity of the (one dimnensional) blurred half-plane.
+ @param profile The precomputed 1D blur profile; memory allocated by and managed by
+ ComputeBlurProfile below.
+ @param loc the location to look up; The lookup will clamp invalid inputs, but
+ meaningful data are available between 0 and blurred_width
+ @param blurred_width The width of the final, blurred rectangle
+ @param sharp_width The width of the original, unblurred rectangle.
+ */
+ static uint8_t ProfileLookup(const uint8_t* profile, int loc, int blurred_width, int sharp_width);
+
+ /** Allocate memory for and populate the profile of a 1D blurred halfplane. The caller
+ must free the memory. The amount of memory allocated will be exactly 6*sigma bytes.
+ @param sigma The standard deviation of the gaussian blur kernel
+ @param profile_out The location to store the allocated profile curve
+ */
+
+ static void ComputeBlurProfile(SkScalar sigma, uint8_t** profile_out);
+
+ /** Compute an entire scanline of a blurred step function. This is a 1D helper that
+ will produce both the horizontal and vertical profiles of the blurry rectangle.
+ @param pixels Location to store the resulting pixel data; allocated and managed by caller
+ @param profile Precomputed blur profile computed by ComputeBlurProfile above.
+ @param width Size of the pixels array.
+ @param sigma Standard deviation of the gaussian blur kernel used to compute the profile;
+ this implicitly gives the size of the pixels array.
+ */
+
+ static void ComputeBlurredScanline(uint8_t* pixels, const uint8_t* profile,
+ unsigned int width, SkScalar sigma);
+
+
+
};
#endif
diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp
index 14be6a50b3..05f9e88594 100644
--- a/src/effects/SkBlurMaskFilter.cpp
+++ b/src/effects/SkBlurMaskFilter.cpp
@@ -19,7 +19,10 @@
#if SK_SUPPORT_GPU
#include "GrContext.h"
#include "GrTexture.h"
+#include "GrEffect.h"
+#include "gl/GrGLEffect.h"
#include "effects/GrSimpleTextureEffect.h"
+#include "GrTBackendEffectFactory.h"
#include "SkGrPixelRef.h"
#endif
@@ -37,6 +40,11 @@ public:
const SkIRect& clipBounds,
const SkMatrix& ctm,
SkRect* maskRect) const SK_OVERRIDE;
+ virtual bool directFilterMaskGPU(GrContext* context,
+ GrPaint* grp,
+ const SkStrokeRec& strokeRec,
+ const SkPath& path) const SK_OVERRIDE;
+
virtual bool filterMaskGPU(GrTexture* src,
const SkMatrix& ctm,
const SkRect& maskRect,
@@ -500,6 +508,274 @@ void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) const {
#if SK_SUPPORT_GPU
+class GrGLRectBlurEffect;
+
+class GrRectBlurEffect : public GrEffect {
+public:
+ virtual ~GrRectBlurEffect();
+
+ static const char* Name() { return "RectBlur"; }
+
+ typedef GrGLRectBlurEffect GLEffect;
+
+ virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+ virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+ /**
+ * Create a simple filter effect with custom bicubic coefficients.
+ */
+ static GrEffectRef* Create(GrContext *context, const SkRect& rect,
+ float sigma) {
+ GrTexture *horizontalScanline, *verticalScanline;
+ bool createdScanlines = CreateScanlineTextures(context, sigma,
+ SkScalarCeilToInt(rect.width()),
+ SkScalarCeilToInt(rect.height()),
+ &horizontalScanline, &verticalScanline);
+ if (!createdScanlines) {
+ return NULL;
+ }
+ AutoEffectUnref effect(SkNEW_ARGS(GrRectBlurEffect, (rect, sigma,
+ horizontalScanline, verticalScanline)));
+ return CreateEffectRef(effect);
+ }
+
+ unsigned int getWidth() const { return fWidth; }
+ unsigned int getHeight() const { return fHeight; }
+ float getSigma() const { return fSigma; }
+
+private:
+ GrRectBlurEffect(const SkRect& rect, float sigma,
+ GrTexture *horizontal_scanline, GrTexture *vertical_scanline);
+ virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+ static bool CreateScanlineTextures(GrContext *context, float sigma,
+ unsigned int width, unsigned int height,
+ GrTexture **horizontalScanline,
+ GrTexture **verticalScanline);
+
+ unsigned int fWidth, fHeight;
+ float fSigma;
+ GrTextureAccess fHorizontalScanlineAccess;
+ GrTextureAccess fVerticalScanlineAccess;
+ GrCoordTransform fTransform;
+
+ GR_DECLARE_EFFECT_TEST;
+
+ typedef GrEffect INHERITED;
+};
+
+class GrGLRectBlurEffect : public GrGLEffect {
+public:
+ GrGLRectBlurEffect(const GrBackendEffectFactory& factory,
+ const GrDrawEffect&);
+ virtual void emitCode(GrGLShaderBuilder*,
+ const GrDrawEffect&,
+ EffectKey,
+ const char* outputColor,
+ const char* inputColor,
+ const TransformedCoordsArray&,
+ const TextureSamplerArray&) SK_OVERRIDE;
+
+ virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
+
+private:
+ typedef GrGLUniformManager::UniformHandle UniformHandle;
+
+ UniformHandle fWidthUni;
+ UniformHandle fHeightUni;
+
+ typedef GrGLEffect INHERITED;
+};
+
+GrGLRectBlurEffect::GrGLRectBlurEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
+ : INHERITED(factory) {
+}
+
+void GrGLRectBlurEffect::emitCode(GrGLShaderBuilder* builder,
+ const GrDrawEffect&,
+ EffectKey key,
+ const char* outputColor,
+ const char* inputColor,
+ const TransformedCoordsArray& coords,
+ const TextureSamplerArray& samplers) {
+
+ SkString texture_coords = builder->ensureFSCoords2D(coords, 0);
+
+ if (inputColor) {
+ builder->fsCodeAppendf("\tvec4 src=%s;\n", inputColor);
+ } else {
+ builder->fsCodeAppendf("\tvec4 src=vec4(1)\n;");
+ }
+
+ builder->fsCodeAppendf("\tvec4 horiz = ");
+ builder->fsAppendTextureLookup( samplers[0], texture_coords.c_str() );
+ builder->fsCodeAppendf(";\n");
+ builder->fsCodeAppendf("\tvec4 vert = ");
+ builder->fsAppendTextureLookup( samplers[1], texture_coords.c_str() );
+ builder->fsCodeAppendf(";\n");
+
+ builder->fsCodeAppendf("\tfloat final = (horiz*vert).r;\n");
+ builder->fsCodeAppendf("\t%s = final*src;\n", outputColor);
+}
+
+void GrGLRectBlurEffect::setData(const GrGLUniformManager& uman,
+ const GrDrawEffect& drawEffect) {
+}
+
+bool GrRectBlurEffect::CreateScanlineTextures(GrContext *context, float sigma,
+ unsigned int width, unsigned int height,
+ GrTexture **horizontalScanline,
+ GrTexture **verticalScanline) {
+ GrTextureParams params;
+ GrTextureDesc texDesc;
+
+ unsigned int profile_size = SkScalarFloorToInt(6*sigma);
+
+ texDesc.fWidth = width;
+ texDesc.fHeight = 1;
+ texDesc.fConfig = kAlpha_8_GrPixelConfig;
+
+ static const GrCacheID::Domain gBlurProfileDomain = GrCacheID::GenerateDomain();
+ GrCacheID::Key key;
+ memset(&key, 0, sizeof(key));
+ key.fData32[0] = profile_size;
+ key.fData32[1] = width;
+ key.fData32[2] = 1;
+ GrCacheID horizontalCacheID(gBlurProfileDomain, key);
+
+ uint8_t *profile = NULL;
+ SkAutoTDeleteArray<uint8_t> ada(profile);
+
+ *horizontalScanline = context->findAndRefTexture(texDesc, horizontalCacheID, &params);
+
+ if (NULL == *horizontalScanline) {
+
+ SkBlurMask::ComputeBlurProfile(sigma, &profile);
+
+ SkAutoTMalloc<uint8_t> horizontalPixels(width);
+ SkBlurMask::ComputeBlurredScanline(horizontalPixels, profile, width, sigma);
+
+ *horizontalScanline = context->createTexture(&params, texDesc, horizontalCacheID,
+ horizontalPixels, 0);
+
+ if (NULL == *horizontalScanline) {
+ return false;
+ }
+ }
+
+ texDesc.fWidth = 1;
+ texDesc.fHeight = height;
+ key.fData32[1] = 1;
+ key.fData32[2] = height;
+ GrCacheID verticalCacheID(gBlurProfileDomain, key);
+
+ *verticalScanline = context->findAndRefTexture(texDesc, verticalCacheID, &params);
+ if (NULL == *verticalScanline) {
+ if (NULL == profile) {
+ SkBlurMask::ComputeBlurProfile(sigma, &profile);
+ }
+
+ SkAutoTMalloc<uint8_t> verticalPixels(height);
+ SkBlurMask::ComputeBlurredScanline(verticalPixels, profile, height, sigma);
+
+ *verticalScanline = context->createTexture(&params, texDesc, verticalCacheID,
+ verticalPixels, 0);
+
+ if (NULL == *verticalScanline) {
+ return false;
+ }
+
+ }
+ return true;
+}
+
+GrRectBlurEffect::GrRectBlurEffect(const SkRect& rect, float sigma,
+ GrTexture *horizontal_scanline, GrTexture *vertical_scanline)
+ : INHERITED(),
+ fWidth(horizontal_scanline->width()),
+ fHeight(vertical_scanline->width()),
+ fSigma(sigma),
+ fHorizontalScanlineAccess(horizontal_scanline),
+ fVerticalScanlineAccess(vertical_scanline) {
+ SkMatrix mat;
+ mat.setRectToRect(rect, SkRect::MakeWH(1,1), SkMatrix::kFill_ScaleToFit);
+ fTransform = GrCoordTransform(kLocal_GrCoordSet, mat);
+ this->addTextureAccess(&fHorizontalScanlineAccess);
+ this->addTextureAccess(&fVerticalScanlineAccess);
+ this->addCoordTransform(&fTransform);
+}
+
+GrRectBlurEffect::~GrRectBlurEffect() {
+}
+
+const GrBackendEffectFactory& GrRectBlurEffect::getFactory() const {
+ return GrTBackendEffectFactory<GrRectBlurEffect>::getInstance();
+}
+
+bool GrRectBlurEffect::onIsEqual(const GrEffect& sBase) const {
+ const GrRectBlurEffect& s = CastEffect<GrRectBlurEffect>(sBase);
+ return this->getWidth() == s.getWidth() &&
+ this->getHeight() == s.getHeight() &&
+ this->getSigma() == s.getSigma();
+}
+
+void GrRectBlurEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+ *validFlags = 0;
+ return;
+}
+
+GR_DEFINE_EFFECT_TEST(GrRectBlurEffect);
+
+GrEffectRef* GrRectBlurEffect::TestCreate(SkRandom* random,
+ GrContext* context,
+ const GrDrawTargetCaps&,
+ GrTexture**) {
+ float sigma = random->nextRangeF(3,8);
+ float width = random->nextRangeF(200,300);
+ float height = random->nextRangeF(200,300);
+ return GrRectBlurEffect::Create(context, SkRect::MakeWH(width, height), sigma);
+}
+
+
+bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrContext* context,
+ GrPaint* grp,
+ const SkStrokeRec& strokeRec,
+ const SkPath& path) const {
+ if (fBlurStyle != SkBlurMaskFilter::kNormal_BlurStyle) {
+ return false;
+ }
+
+ SkRect rect;
+ if (!path.isRect(&rect)) {
+ return false;
+ }
+
+ if (!strokeRec.isFillStyle()) {
+ return false;
+ }
+
+ SkMatrix ctm = context->getMatrix();
+ SkScalar xformedSigma = this->computeXformedSigma(ctm);
+ rect.outset(3*xformedSigma, 3*xformedSigma);
+
+ SkAutoTUnref<GrEffectRef> effect(GrRectBlurEffect::Create(
+ context, rect, xformedSigma));
+ if (!effect) {
+ return false;
+ }
+
+ GrContext::AutoMatrix am;
+ if (!am.setIdentity(context, grp)) {
+ return false;
+ }
+
+
+ grp->addCoverageEffect(effect);
+
+ context->drawRect(*grp, rect);
+ return true;
+}
+
bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRect& srcBounds,
const SkIRect& clipBounds,
const SkMatrix& ctm,
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 73eb65b661..6160876dd1 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -933,7 +933,7 @@ void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
// transform the path into device space
pathPtr->transform(fContext->getMatrix(), devPathPtr);
-
+
SkRect maskRect;
if (paint.getMaskFilter()->canFilterMaskGPU(devPathPtr->getBounds(),
draw.fClip->getBounds(),
@@ -953,6 +953,13 @@ void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
// nothing to draw
return;
}
+
+ if (paint.getMaskFilter()->directFilterMaskGPU(fContext, &grPaint,
+ SkStrokeRec(paint), *devPathPtr)) {
+ // the mask filter was able to draw itself directly, so there's nothing
+ // left to do.
+ return;
+ }
GrAutoScratchTexture mask;