From b505a128efae9debcaa9642bade90bab5525d477 Mon Sep 17 00:00:00 2001 From: "bsalomon@google.com" Date: Thu, 31 May 2012 18:40:36 +0000 Subject: [PATCH] Implement morphology as a custom effect Review URL: http://codereview.appspot.com/6250073/ git-svn-id: http://skia.googlecode.com/svn/trunk@4102 2bbb7eff-a529-9590-31e7-b0007b416f81 --- gyp/gpu.gyp | 3 + include/gpu/GrContext.h | 10 +- include/gpu/GrCustomStage.h | 2 +- include/gpu/GrProgramStageFactory.h | 8 +- include/gpu/GrSamplerState.h | 70 +--------- src/gpu/GrContext.cpp | 106 ++++++--------- src/gpu/SkGpuDevice.cpp | 4 +- src/gpu/effects/Gr1DKernelEffect.h | 50 +++++++ src/gpu/effects/GrConvolutionEffect.cpp | 165 ++++++++++++----------- src/gpu/effects/GrConvolutionEffect.h | 44 ++++-- src/gpu/effects/GrMorphologyEffect.cpp | 169 +++++++++++++++++++++++ src/gpu/effects/GrMorphologyEffect.h | 48 +++++++ src/gpu/gl/GrGLProgram.cpp | 105 +-------------- src/gpu/gl/GrGLProgram.h | 10 +- src/gpu/gl/GrGLProgramStage.cpp | 8 +- src/gpu/gl/GrGLProgramStage.h | 9 +- src/gpu/gl/GrGpuGL.cpp | 3 - src/gpu/gl/GrGpuGL_program.cpp | 171 ++++++++++++------------ 18 files changed, 547 insertions(+), 438 deletions(-) create mode 100644 src/gpu/effects/Gr1DKernelEffect.h create mode 100644 src/gpu/effects/GrMorphologyEffect.cpp create mode 100644 src/gpu/effects/GrMorphologyEffect.h diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp index 2292236d8b..859454546d 100644 --- a/gyp/gpu.gyp +++ b/gyp/gpu.gyp @@ -274,8 +274,11 @@ '../src/gpu/GrVertexBuffer.h', '../src/gpu/gr_unittests.cpp', + '../src/gpu/effects/Gr1DKernelEffect.h', '../src/gpu/effects/GrConvolutionEffect.cpp', '../src/gpu/effects/GrConvolutionEffect.h', + '../src/gpu/effects/GrMorphologyEffect.cpp', + '../src/gpu/effects/GrMorphologyEffect.h', '../src/gpu/gl/GrGLCaps.cpp', '../src/gpu/gl/GrGLCaps.h', diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h index c9c15dfcac..876852825c 100644 --- a/include/gpu/GrContext.h +++ b/include/gpu/GrContext.h @@ -616,6 +616,14 @@ public: const SkRect& rect, float sigmaX, float sigmaY); + /** + * This enum is used with the function below, applyMorphology. + */ + enum MorphologyType { + kErode_MorphologyType, + kDilate_MorphologyType, + }; + /** * Applies a 2D morphology to a given texture. * @param srcTexture The source texture to be blurred. @@ -632,7 +640,7 @@ public: GrTexture* applyMorphology(GrTexture* srcTexture, const GrRect& rect, GrTexture* temp1, GrTexture* temp2, - GrSamplerState::Filter filter, + MorphologyType type, SkISize radius); /////////////////////////////////////////////////////////////////////////// diff --git a/include/gpu/GrCustomStage.h b/include/gpu/GrCustomStage.h index cb9471e939..5eea9f4929 100644 --- a/include/gpu/GrCustomStage.h +++ b/include/gpu/GrCustomStage.h @@ -57,7 +57,7 @@ public: For equivalence (that they will generate the same shader, but perhaps have different uniforms), check equality of the stageKey produced by the GrProgramStageFactory. */ - virtual bool isEqual(const GrCustomStage *) const = 0; + virtual bool isEqual(const GrCustomStage&) const = 0; /** Human-meaningful string to identify this effect; may be embedded in generated shader code. */ diff --git a/include/gpu/GrProgramStageFactory.h b/include/gpu/GrProgramStageFactory.h index 2795deb85e..0fac7dbc07 100644 --- a/include/gpu/GrProgramStageFactory.h +++ b/include/gpu/GrProgramStageFactory.h @@ -26,9 +26,9 @@ public: kProgramStageKeyBits = 10, }; - virtual StageKey glStageKey(const GrCustomStage* stage) const = 0; + virtual StageKey glStageKey(const GrCustomStage& stage) const = 0; virtual GrGLProgramStage* createGLInstance( - const GrCustomStage* stage) const = 0; + const GrCustomStage& stage) const = 0; bool operator ==(const GrProgramStageFactory& b) const { return fStageClassID == b.fStageClassID; @@ -79,7 +79,7 @@ public: id identifies the GrCustomShader subclass. The remainder is based on the aspects of the GrCustomStage object's configuration that affect GLSL code generation. */ - virtual StageKey glStageKey(const GrCustomStage* stage) const SK_OVERRIDE { + virtual StageKey glStageKey(const GrCustomStage& stage) const SK_OVERRIDE { GrAssert(kIllegalStageClassID != fStageClassID); StageKey stageID = GLProgramStage::GenKey(stage); #if GR_DEBUG @@ -94,7 +94,7 @@ public: for the given GrCustomStage; caller is responsible for deleting the object. */ virtual GLProgramStage* createGLInstance( - const GrCustomStage* stage) const SK_OVERRIDE { + const GrCustomStage& stage) const SK_OVERRIDE { return new GLProgramStage(*this, stage); } diff --git a/include/gpu/GrSamplerState.h b/include/gpu/GrSamplerState.h index 952169c6d7..4d2d5f20f5 100644 --- a/include/gpu/GrSamplerState.h +++ b/include/gpu/GrSamplerState.h @@ -15,8 +15,6 @@ #include "GrMatrix.h" #include "GrTypes.h" -#define MAX_KERNEL_WIDTH 25 - class GrSamplerState { public: enum Filter { @@ -36,18 +34,6 @@ public: * on shader backends. */ k4x4Downsample_Filter, - /** - * Apply a separable convolution kernel. - */ - kConvolution_Filter, - /** - * Apply a dilate filter (max over a 1D radius). - */ - kDilate_Filter, - /** - * Apply an erode filter (min over a 1D radius). - */ - kErode_Filter, kDefault_Filter = kNearest_Filter }; @@ -95,17 +81,6 @@ public: kDefault_WrapMode = kClamp_WrapMode }; - /** - * For the filters which perform more than one texture sample (convolution, - * erode, dilate), this determines the direction in which the texture - * coordinates will be incremented. - */ - enum FilterDirection { - kX_FilterDirection, - kY_FilterDirection, - - kDefault_FilterDirection = kX_FilterDirection, - }; /** * Default sampler state is set to clamp, use normal sampling mode, be * unfiltered, and use identity matrix. @@ -135,7 +110,7 @@ public: (fCustomStage && s.fCustomStage && (fCustomStage->getFactory() == s.fCustomStage->getFactory()) && - fCustomStage->isEqual(s.fCustomStage))); + fCustomStage->isEqual(*s.fCustomStage))); } bool operator !=(const GrSamplerState& s) const { return !(*this == s); } @@ -143,7 +118,6 @@ public: // memcpy() breaks refcounting fWrapX = s.fWrapX; fWrapY = s.fWrapY; - fFilterDirection = s.fFilterDirection; fSampleMode = s.fSampleMode; fFilter = s.fFilter; fMatrix = s.fMatrix; @@ -154,11 +128,6 @@ public: fRadial2Radius0 = s.fRadial2Radius0; fRadial2PosRoot = s.fRadial2PosRoot; - fKernelWidth = s.fKernelWidth; - if (kConvolution_Filter == fFilter) { - memcpy(fKernel, s.fKernel, MAX_KERNEL_WIDTH * sizeof(float)); - } - fCustomStage = s.fCustomStage; SkSafeRef(fCustomStage); @@ -167,16 +136,12 @@ public: WrapMode getWrapX() const { return fWrapX; } WrapMode getWrapY() const { return fWrapY; } - FilterDirection getFilterDirection() const { return fFilterDirection; } SampleMode getSampleMode() const { return fSampleMode; } const GrMatrix& getMatrix() const { return fMatrix; } const GrRect& getTextureDomain() const { return fTextureDomain; } bool hasTextureDomain() const {return SkIntToScalar(0) != fTextureDomain.right();} Filter getFilter() const { return fFilter; } - int getKernelWidth() const { return fKernelWidth; } - const float* getKernel() const { return fKernel; } bool swapsRAndB() const { return fSwapRAndB; } - bool isGradient() const { return kRadial_SampleMode == fSampleMode || kRadial2_SampleMode == fSampleMode || @@ -186,7 +151,6 @@ public: void setWrapX(WrapMode mode) { fWrapX = mode; } void setWrapY(WrapMode mode) { fWrapY = mode; } void setSampleMode(SampleMode mode) { fSampleMode = mode; } - void setFilterDirection(FilterDirection mode) { fFilterDirection = mode; } /** * Access the sampler's matrix. See SampleMode for explanation of @@ -227,30 +191,24 @@ public: void reset(WrapMode wrapXAndY, Filter filter, - FilterDirection direction, const GrMatrix& matrix) { fWrapX = wrapXAndY; fWrapY = wrapXAndY; fSampleMode = kDefault_SampleMode; fFilter = filter; - fFilterDirection = direction; fMatrix = matrix; fTextureDomain.setEmpty(); fSwapRAndB = false; GrSafeSetNull(fCustomStage); } - void reset(WrapMode wrapXAndY, Filter filter, const GrMatrix& matrix) { - this->reset(wrapXAndY, filter, kDefault_FilterDirection, matrix); - } - void reset(WrapMode wrapXAndY, - Filter filter) { - this->reset(wrapXAndY, filter, kDefault_FilterDirection, GrMatrix::I()); + void reset(WrapMode wrapXAndY, Filter filter) { + this->reset(wrapXAndY, filter, GrMatrix::I()); } void reset(const GrMatrix& matrix) { - this->reset(kDefault_WrapMode, kDefault_Filter, kDefault_FilterDirection, matrix); + this->reset(kDefault_WrapMode, kDefault_Filter, matrix); } void reset() { - this->reset(kDefault_WrapMode, kDefault_Filter, kDefault_FilterDirection, GrMatrix::I()); + this->reset(kDefault_WrapMode, kDefault_Filter, GrMatrix::I()); } GrScalar getRadial2CenterX1() const { return fRadial2CenterX1; } @@ -273,19 +231,6 @@ public: fRadial2PosRoot = posRoot; } - void setConvolutionParams(int kernelWidth, const float* kernel) { - GrAssert(kernelWidth >= 0 && kernelWidth <= MAX_KERNEL_WIDTH); - fKernelWidth = kernelWidth; - if (NULL != kernel) { - memcpy(fKernel, kernel, kernelWidth * sizeof(float)); - } - } - - void setMorphologyRadius(int radius) { - GrAssert(radius >= 0 && radius <= MAX_KERNEL_WIDTH); - fKernelWidth = radius; - } - void setCustomStage(GrCustomStage* stage) { GrSafeAssign(fCustomStage, stage); } @@ -294,7 +239,6 @@ public: private: WrapMode fWrapX : 8; WrapMode fWrapY : 8; - FilterDirection fFilterDirection : 8; SampleMode fSampleMode : 8; Filter fFilter : 8; GrMatrix fMatrix; @@ -306,10 +250,6 @@ private: GrScalar fRadial2Radius0; SkBool8 fRadial2PosRoot; - // These are undefined unless fFilter == kConvolution_Filter - uint8_t fKernelWidth; - float fKernel[MAX_KERNEL_WIDTH]; - GrCustomStage* fCustomStage; }; diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index 80dd25ebee..b7ada614a3 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -9,9 +9,11 @@ #include "GrContext.h" +#include "effects/GrMorphologyEffect.h" +#include "effects/GrConvolutionEffect.h" + #include "GrBufferAllocPool.h" #include "GrClipIterator.h" -#include "effects/GrConvolutionEffect.h" #include "GrGpu.h" #include "GrIndexBuffer.h" #include "GrInOrderDrawBuffer.h" @@ -242,23 +244,6 @@ void gen_stencil_key_values(const GrStencilBuffer* sb, sb->numSamples(), v); } -void build_kernel(float sigma, float* kernel, int kernelWidth) { - int halfWidth = (kernelWidth - 1) / 2; - float sum = 0.0f; - float denom = 1.0f / (2.0f * sigma * sigma); - for (int i = 0; i < kernelWidth; ++i) { - float x = static_cast(i - halfWidth); - // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian - // is dropped here, since we renormalize the kernel below. - kernel[i] = sk_float_exp(- x * x * denom); - sum += kernel[i]; - } - // Normalize the kernel - float scale = 1.0f / sum; - for (int i = 0; i < kernelWidth; ++i) - kernel[i] *= scale; -} - void scale_rect(SkRect* rect, float xScale, float yScale) { rect->fLeft = SkScalarMul(rect->fLeft, SkFloatToScalar(xScale)); rect->fTop = SkScalarMul(rect->fTop, SkFloatToScalar(yScale)); @@ -266,15 +251,14 @@ void scale_rect(SkRect* rect, float xScale, float yScale) { rect->fBottom = SkScalarMul(rect->fBottom, SkFloatToScalar(yScale)); } -float adjust_sigma(float sigma, int *scaleFactor, int *halfWidth, - int *kernelWidth) { +float adjust_sigma(float sigma, int *scaleFactor, int *radius) { *scaleFactor = 1; while (sigma > MAX_BLUR_SIGMA) { *scaleFactor *= 2; sigma *= 0.5f; } - *halfWidth = static_cast(ceilf(sigma * 3.0f)); - *kernelWidth = *halfWidth * 2 + 1; + *radius = static_cast(ceilf(sigma * 3.0f)); + GrAssert(*radius <= GrConvolutionEffect::kMaxKernelRadius); return sigma; } @@ -282,10 +266,8 @@ void apply_morphology(GrGpu* gpu, GrTexture* texture, const SkRect& rect, int radius, - GrSamplerState::Filter filter, - GrSamplerState::FilterDirection direction) { - GrAssert(filter == GrSamplerState::kErode_Filter || - filter == GrSamplerState::kDilate_Filter); + GrContext::MorphologyType morphType, + Gr1DKernelEffect::Direction direction) { GrRenderTarget* target = gpu->drawState()->getRenderTarget(); GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit); @@ -293,31 +275,31 @@ void apply_morphology(GrGpu* gpu, drawState->setRenderTarget(target); GrMatrix sampleM; sampleM.setIDiv(texture->width(), texture->height()); - drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode, filter, - sampleM); - drawState->sampler(0)->setMorphologyRadius(radius); - drawState->sampler(0)->setFilterDirection(direction); + drawState->sampler(0)->reset(sampleM); + SkAutoTUnref morph( + new GrMorphologyEffect(direction, radius, morphType)); + drawState->sampler(0)->setCustomStage(morph); drawState->setTexture(0, texture); gpu->drawSimpleRect(rect, NULL, 1 << 0); } -void convolve(GrGpu* gpu, - GrTexture* texture, - const SkRect& rect, - const float* kernel, - int kernelWidth, - GrSamplerState::FilterDirection direction) { +void convolve_gaussian(GrGpu* gpu, + GrTexture* texture, + const SkRect& rect, + float sigma, + int radius, + Gr1DKernelEffect::Direction direction) { GrRenderTarget* target = gpu->drawState()->getRenderTarget(); GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit); GrDrawState* drawState = gpu->drawState(); drawState->setRenderTarget(target); GrMatrix sampleM; sampleM.setIDiv(texture->width(), texture->height()); - drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode, - GrSamplerState::kConvolution_Filter, - sampleM); - drawState->sampler(0)->setCustomStage( - new GrConvolutionEffect(direction, kernelWidth, kernel)); + drawState->sampler(0)->reset(sampleM); + SkAutoTUnref conv(new + GrConvolutionEffect(direction, radius)); + conv->setGaussianKernel(sigma); + drawState->sampler(0)->setCustomStage(conv); drawState->setTexture(0, texture); gpu->drawSimpleRect(rect, NULL, 1 << 0); } @@ -2085,10 +2067,10 @@ GrTexture* GrContext::gaussianBlur(GrTexture* srcTexture, GrTexture* origTexture = srcTexture; GrAutoMatrix avm(this, GrMatrix::I()); SkIRect clearRect; - int scaleFactorX, halfWidthX, kernelWidthX; - int scaleFactorY, halfWidthY, kernelWidthY; - sigmaX = adjust_sigma(sigmaX, &scaleFactorX, &halfWidthX, &kernelWidthX); - sigmaY = adjust_sigma(sigmaY, &scaleFactorY, &halfWidthY, &kernelWidthY); + int scaleFactorX, radiusX; + int scaleFactorY, radiusY; + sigmaX = adjust_sigma(sigmaX, &scaleFactorX, &radiusX); + sigmaY = adjust_sigma(sigmaY, &scaleFactorY, &radiusY); SkRect srcRect(rect); scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); @@ -2138,21 +2120,17 @@ GrTexture* GrContext::gaussianBlur(GrTexture* srcTexture, srcRect.roundOut(&srcIRect); if (sigmaX > 0.0f) { - SkAutoTMalloc kernelStorageX(kernelWidthX); - float* kernelX = kernelStorageX.get(); - build_kernel(sigmaX, kernelX, kernelWidthX); - if (scaleFactorX > 1) { - // Clear out a halfWidth to the right of the srcRect to prevent the + // Clear out a radius to the right of the srcRect to prevent the // X convolution from reading garbage. clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, - halfWidthX, srcIRect.height()); + radiusX, srcIRect.height()); this->clear(&clearRect, 0x0); } this->setRenderTarget(dstTexture->asRenderTarget()); - convolve(fGpu, srcTexture, srcRect, kernelX, kernelWidthX, - GrSamplerState::kX_FilterDirection); + convolve_gaussian(fGpu, srcTexture, srcRect, sigmaX, radiusX, + Gr1DKernelEffect::kX_Direction); SkTSwap(srcTexture, dstTexture); if (temp2 && dstTexture == origTexture) { dstTexture = temp2->texture(); @@ -2160,21 +2138,17 @@ GrTexture* GrContext::gaussianBlur(GrTexture* srcTexture, } if (sigmaY > 0.0f) { - SkAutoTMalloc kernelStorageY(kernelWidthY); - float* kernelY = kernelStorageY.get(); - build_kernel(sigmaY, kernelY, kernelWidthY); - if (scaleFactorY > 1 || sigmaX > 0.0f) { - // Clear out a halfWidth below the srcRect to prevent the Y + // Clear out a radius below the srcRect to prevent the Y // convolution from reading garbage. clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, - srcIRect.width(), halfWidthY); + srcIRect.width(), radiusY); this->clear(&clearRect, 0x0); } this->setRenderTarget(dstTexture->asRenderTarget()); - convolve(fGpu, srcTexture, srcRect, kernelY, kernelWidthY, - GrSamplerState::kY_FilterDirection); + convolve_gaussian(fGpu, srcTexture, srcRect, sigmaY, radiusY, + Gr1DKernelEffect::kY_Direction); SkTSwap(srcTexture, dstTexture); if (temp2 && dstTexture == origTexture) { dstTexture = temp2->texture(); @@ -2210,7 +2184,7 @@ GrTexture* GrContext::gaussianBlur(GrTexture* srcTexture, GrTexture* GrContext::applyMorphology(GrTexture* srcTexture, const GrRect& rect, GrTexture* temp1, GrTexture* temp2, - GrSamplerState::Filter filter, + MorphologyType morphType, SkISize radius) { ASSERT_OWNED_RESOURCE(srcTexture); GrRenderTarget* oldRenderTarget = this->getRenderTarget(); @@ -2220,8 +2194,8 @@ GrTexture* GrContext::applyMorphology(GrTexture* srcTexture, SkIntToScalar(srcTexture->height()))); if (radius.fWidth > 0) { this->setRenderTarget(temp1->asRenderTarget()); - apply_morphology(fGpu, srcTexture, rect, radius.fWidth, filter, - GrSamplerState::kX_FilterDirection); + apply_morphology(fGpu, srcTexture, rect, radius.fWidth, morphType, + Gr1DKernelEffect::kX_Direction); SkIRect clearRect = SkIRect::MakeXYWH( SkScalarFloorToInt(rect.fLeft), SkScalarFloorToInt(rect.fBottom), @@ -2232,8 +2206,8 @@ GrTexture* GrContext::applyMorphology(GrTexture* srcTexture, } if (radius.fHeight > 0) { this->setRenderTarget(temp2->asRenderTarget()); - apply_morphology(fGpu, srcTexture, rect, radius.fHeight, filter, - GrSamplerState::kY_FilterDirection); + apply_morphology(fGpu, srcTexture, rect, radius.fHeight, morphType, + Gr1DKernelEffect::kY_Direction); srcTexture = temp2; } this->setRenderTarget(oldRenderTarget); diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 411b2c5b8f..871dbb3846 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1451,14 +1451,14 @@ static GrTexture* filter_texture(GrContext* context, GrTexture* texture, GrAutoScratchTexture temp1(context, desc), temp2(context, desc); texture = context->applyMorphology(texture, rect, temp1.texture(), temp2.texture(), - GrSamplerState::kDilate_Filter, + GrContext::kDilate_MorphologyType, radius); texture->ref(); } else if (filter->asAnErode(&radius)) { GrAutoScratchTexture temp1(context, desc), temp2(context, desc); texture = context->applyMorphology(texture, rect, temp1.texture(), temp2.texture(), - GrSamplerState::kErode_Filter, + GrContext::kErode_MorphologyType, radius); texture->ref(); } diff --git a/src/gpu/effects/Gr1DKernelEffect.h b/src/gpu/effects/Gr1DKernelEffect.h new file mode 100644 index 0000000000..b6e116fa96 --- /dev/null +++ b/src/gpu/effects/Gr1DKernelEffect.h @@ -0,0 +1,50 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef Gr1DKernelEffect_DEFINED +#define Gr1DKernelEffect_DEFINED + +#include "GrCustomStage.h" + +/** + * Base class for 1D kernel effects. The kernel operates either in X or Y and + * has a pixel radius. The kernel is specified in the src texture's space + * and the kernel center is pinned to a texel's center. The radius specifies + * the number of texels on either side of the center texel in X or Y that are + * read. Since the center pixel is also read, the total width is one larger than + * two times the radius. + */ +class Gr1DKernelEffect : public GrCustomStage { + +public: + enum Direction { + kX_Direction, + kY_Direction, + }; + + Gr1DKernelEffect(Direction direction, + int radius) + : fDirection(direction) + , fRadius(radius) {} + + virtual ~Gr1DKernelEffect() {}; + + static int WidthFromRadius(int radius) { return 2 * radius + 1; } + + int radius() const { return fRadius; } + int width() const { return WidthFromRadius(fRadius); } + Direction direction() const { return fDirection; } + +private: + + Direction fDirection; + int fRadius; + + typedef GrCustomStage INHERITED; +}; + +#endif diff --git a/src/gpu/effects/GrConvolutionEffect.cpp b/src/gpu/effects/GrConvolutionEffect.cpp index 0dd1606c81..9296ecc93e 100644 --- a/src/gpu/effects/GrConvolutionEffect.cpp +++ b/src/gpu/effects/GrConvolutionEffect.cpp @@ -11,14 +11,11 @@ #include "gl/GrGLTexture.h" #include "GrProgramStageFactory.h" -///////////////////////////////////////////////////////////////////// - class GrGLConvolutionEffect : public GrGLProgramStage { - public: - GrGLConvolutionEffect(const GrProgramStageFactory& factory, - const GrCustomStage* stage); + const GrCustomStage& stage); + virtual void setupVariables(GrGLShaderBuilder* state, int stage) SK_OVERRIDE; virtual void emitVS(GrGLShaderBuilder* state, @@ -27,38 +24,38 @@ public: const char* outputColor, const char* inputColor, const char* samplerName) SK_OVERRIDE; + virtual void initUniforms(const GrGLInterface*, int programID) SK_OVERRIDE; - virtual void setData(const GrGLInterface*, + virtual void setData(const GrGLInterface*, const GrGLTexture&, - GrCustomStage*, + const GrCustomStage&, int stageNum) SK_OVERRIDE; - static inline StageKey GenKey(const GrCustomStage* s); - -protected: - - unsigned int fKernelWidth; - const GrGLShaderVar* fKernelVar; - const GrGLShaderVar* fImageIncrementVar; - - GrGLint fKernelLocation; - GrGLint fImageIncrementLocation; + static inline StageKey GenKey(const GrCustomStage&); private: + int width() const { return Gr1DKernelEffect::WidthFromRadius(fRadius); } + + int fRadius; + const GrGLShaderVar* fKernelVar; + GrGLint fKernelLocation; + const GrGLShaderVar* fImageIncrementVar; + GrGLint fImageIncrementLocation; typedef GrGLProgramStage INHERITED; }; -GrGLConvolutionEffect::GrGLConvolutionEffect( - const GrProgramStageFactory& factory, - const GrCustomStage* data) +GrGLConvolutionEffect::GrGLConvolutionEffect(const GrProgramStageFactory& factory, + const GrCustomStage& stage) : GrGLProgramStage(factory) , fKernelVar(NULL) - , fImageIncrementVar(NULL) , fKernelLocation(0) + , fImageIncrementVar(NULL) , fImageIncrementLocation(0) { - fKernelWidth = static_cast(data)->width(); + const GrConvolutionEffect& c = + static_cast(stage); + fRadius = c.radius(); } void GrGLConvolutionEffect::setupVariables(GrGLShaderBuilder* state, @@ -68,124 +65,132 @@ void GrGLConvolutionEffect::setupVariables(GrGLShaderBuilder* state, kVec2f_GrSLType, "uImageIncrement", stage); fKernelVar = &state->addUniform( GrGLShaderBuilder::kFragment_VariableLifetime, - kFloat_GrSLType, "uKernel", stage, fKernelWidth); + kFloat_GrSLType, "uKernel", stage, this->width()); fImageIncrementLocation = kUseUniform; fKernelLocation = kUseUniform; } void GrGLConvolutionEffect::emitVS(GrGLShaderBuilder* state, - const char* vertexCoords) { + const char* vertexCoords) { GrStringBuilder* code = &state->fVSCode; - float scale = (fKernelWidth - 1) * 0.5f; - code->appendf("\t\t%s -= vec2(%g, %g) * %s;\n", - vertexCoords, scale, scale, + code->appendf("\t\t%s -= vec2(%d, %d) * %s;\n", + vertexCoords, fRadius, fRadius, fImageIncrementVar->getName().c_str()); - } void GrGLConvolutionEffect::emitFS(GrGLShaderBuilder* state, - const char* outputColor, - const char* inputColor, - const char* samplerName) { + const char* outputColor, + const char* inputColor, + const char* samplerName) { GrStringBuilder* code = &state->fFSCode; const char* texFunc = "texture2D"; bool complexCoord = false; - GrStringBuilder modulate; - if (NULL != inputColor) { - modulate.printf(" * %s", inputColor); - } + state->fFSCode.appendf("\t\tvec4 sum = vec4(0, 0, 0, 0);\n"); + + code->appendf("\t\tvec2 coord = %s;\n", state->fSampleCoords.c_str()); + code->appendf("\t\tfor (int i = 0; i < %d; i++) {\n", this->width()); // Creates the string "kernel[i]" with workarounds for // possible driver bugs GrStringBuilder kernelIndex; fKernelVar->appendArrayAccess("i", &kernelIndex); - - code->appendf("\t\tvec4 sum = vec4(0, 0, 0, 0);\n"); - code->appendf("\t\tvec2 coord = %s;\n", state->fSampleCoords.c_str()); - code->appendf("\t\tfor (int i = 0; i < %d; i++) {\n", - fKernelWidth); - - code->appendf("\t\t\tsum += "); + state->fFSCode.appendf("\t\t\tsum += "); state->emitTextureLookup(samplerName, "coord"); - code->appendf(" * %s;\n", kernelIndex.c_str()); + state->fFSCode.appendf(" * %s;\n", kernelIndex.c_str()); code->appendf("\t\t\tcoord += %s;\n", fImageIncrementVar->getName().c_str()); code->appendf("\t\t}\n"); - code->appendf("\t\t%s = sum%s;\n", outputColor, modulate.c_str()); + + state->fFSCode.appendf("\t\t%s = sum%s;\n", + outputColor, + state->fModulate.c_str()); } void GrGLConvolutionEffect::initUniforms(const GrGLInterface* gl, int programID) { - GR_GL_CALL_RET(gl, fKernelLocation, - GetUniformLocation(programID, fKernelVar->getName().c_str())); GR_GL_CALL_RET(gl, fImageIncrementLocation, GetUniformLocation(programID, fImageIncrementVar->getName().c_str())); + GR_GL_CALL_RET(gl, fKernelLocation, + GetUniformLocation(programID, fKernelVar->getName().c_str())); } void GrGLConvolutionEffect::setData(const GrGLInterface* gl, const GrGLTexture& texture, - GrCustomStage* data, + const GrCustomStage& data, int stageNum) { - const GrConvolutionEffect* conv = - static_cast(data); - // the code we generated was for a specific kernel width - GrAssert(conv->width() == fKernelWidth); - GR_GL_CALL(gl, Uniform1fv(fKernelLocation, - fKernelWidth, - conv->kernel())); + const GrConvolutionEffect& conv = + static_cast(data); + // the code we generated was for a specific kernel radius + GrAssert(conv.radius() == fRadius); float imageIncrement[2] = { 0 }; - switch (conv->direction()) { - case GrSamplerState::kX_FilterDirection: + switch (conv.direction()) { + case Gr1DKernelEffect::kX_Direction: imageIncrement[0] = 1.0f / texture.width(); break; - case GrSamplerState::kY_FilterDirection: - imageIncrement[1] = 1.0f / texture.width(); + case Gr1DKernelEffect::kY_Direction: + imageIncrement[1] = 1.0f / texture.height(); break; default: GrCrash("Unknown filter direction."); } GR_GL_CALL(gl, Uniform2fv(fImageIncrementLocation, 1, imageIncrement)); + + GR_GL_CALL(gl, Uniform1fv(fKernelLocation, this->width(), conv.kernel())); } GrGLProgramStage::StageKey GrGLConvolutionEffect::GenKey( - const GrCustomStage* s) { - return static_cast(s)->width(); + const GrCustomStage& s) { + return static_cast(s).radius(); } -///////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// -GrConvolutionEffect::GrConvolutionEffect( - GrSamplerState::FilterDirection direction, - unsigned int kernelWidth, - const float* kernel) - : fDirection (direction) - , fKernelWidth (kernelWidth) { - GrAssert(kernelWidth <= MAX_KERNEL_WIDTH); - for (unsigned int i = 0; i < kernelWidth; i++) { - fKernel[i] = kernel[i]; +GrConvolutionEffect::GrConvolutionEffect(Direction direction, + int radius, + const float* kernel) + : Gr1DKernelEffect(direction, radius) { + GrAssert(radius <= kMaxKernelRadius); + int width = this->width(); + if (NULL != kernel) { + for (int i = 0; i < width; i++) { + fKernel[i] = kernel[i]; + } } } GrConvolutionEffect::~GrConvolutionEffect() { - } const GrProgramStageFactory& GrConvolutionEffect::getFactory() const { return GrTProgramStageFactory::getInstance(); } -bool GrConvolutionEffect::isEqual(const GrCustomStage * sBase) const { - const GrConvolutionEffect* s = - static_cast(sBase); - - return (fKernelWidth == s->fKernelWidth && - fDirection == s->fDirection && - 0 == memcmp(fKernel, s->fKernel, fKernelWidth * sizeof(float))); +bool GrConvolutionEffect::isEqual(const GrCustomStage& sBase) const { + const GrConvolutionEffect& s = + static_cast(sBase); + return (this->radius() == s.radius() && + this->direction() == s.direction() && + 0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float))); } - - +void GrConvolutionEffect::setGaussianKernel(float sigma) { + int width = this->width(); + float sum = 0.0f; + float denom = 1.0f / (2.0f * sigma * sigma); + for (int i = 0; i < width; ++i) { + float x = static_cast(i - this->radius()); + // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian + // is dropped here, since we renormalize the kernel below. + fKernel[i] = sk_float_exp(- x * x * denom); + sum += fKernel[i]; + } + // Normalize the kernel + float scale = 1.0f / sum; + for (int i = 0; i < width; ++i) { + fKernel[i] *= scale; + } +} diff --git a/src/gpu/effects/GrConvolutionEffect.h b/src/gpu/effects/GrConvolutionEffect.h index df164d51fb..fd6883b430 100644 --- a/src/gpu/effects/GrConvolutionEffect.h +++ b/src/gpu/effects/GrConvolutionEffect.h @@ -8,40 +8,58 @@ #ifndef GrConvolutionEffect_DEFINED #define GrConvolutionEffect_DEFINED -#include "GrCustomStage.h" -#include "GrSamplerState.h" // for MAX_KENEL_WIDTH, FilterDirection +#include "Gr1DKernelEffect.h" class GrGLConvolutionEffect; -class GrConvolutionEffect : public GrCustomStage { +/** + * A convolution effect. The kernel is specified as an array of 2 * half-width + * + 1 weights. Each texel is multiplied by it's weight and summed to determine + * the output color. The output color is modulated by the input color. + */ +class GrConvolutionEffect : public Gr1DKernelEffect { public: - GrConvolutionEffect(GrSamplerState::FilterDirection direction, - unsigned int kernelWidth, const float* kernel); + GrConvolutionEffect(Direction, int halfWidth, const float* kernel = NULL); virtual ~GrConvolutionEffect(); - unsigned int width() const { return fKernelWidth; } + void setKernel(const float* kernel) { + memcpy(fKernel, kernel, this->width()); + } + + /** + * Helper to set the kernel to a Gaussian. Replaces the existing kernel. + */ + void setGaussianKernel(float sigma); + const float* kernel() const { return fKernel; } - GrSamplerState::FilterDirection direction() const { return fDirection; } static const char* Name() { return "Convolution"; } typedef GrGLConvolutionEffect GLProgramStage; virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE; - virtual bool isEqual(const GrCustomStage *) const SK_OVERRIDE; + virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE; + + enum { + // This was decided based on the min allowed value for the max texture + // samples per fragment program run in DX9SM2 (32). A sigma param of 4.0 + // on a blur filter gives a kernel width of 25 while a sigma of 5.0 + // would exceed a 32 wide kernel. + kMaxKernelRadius = 12, + // With a C++11 we could have a constexpr version of WidthFromRadius() + // and not have to duplicate this calculation. + kMaxKernelWidth = 2 * kMaxKernelRadius + 1, + }; protected: - GrSamplerState::FilterDirection fDirection; - unsigned int fKernelWidth; - float fKernel[MAX_KERNEL_WIDTH]; - + float fKernel[kMaxKernelWidth]; private: - typedef GrCustomStage INHERITED; + typedef Gr1DKernelEffect INHERITED; }; #endif diff --git a/src/gpu/effects/GrMorphologyEffect.cpp b/src/gpu/effects/GrMorphologyEffect.cpp new file mode 100644 index 0000000000..614ef3253b --- /dev/null +++ b/src/gpu/effects/GrMorphologyEffect.cpp @@ -0,0 +1,169 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrMorphologyEffect.h" +#include "gl/GrGLProgramStage.h" +#include "gl/GrGLSL.h" +#include "gl/GrGLTexture.h" +#include "GrProgramStageFactory.h" + +/////////////////////////////////////////////////////////////////////////////// + +class GrGLMorphologyEffect : public GrGLProgramStage { +public: + GrGLMorphologyEffect (const GrProgramStageFactory& factory, + const GrCustomStage& stage); + + virtual void setupVariables(GrGLShaderBuilder* state, + int stage) SK_OVERRIDE; + virtual void emitVS(GrGLShaderBuilder* state, + const char* vertexCoords) SK_OVERRIDE; + virtual void emitFS(GrGLShaderBuilder* state, + const char* outputColor, + const char* inputColor, + const char* samplerName) SK_OVERRIDE; + + static inline StageKey GenKey(const GrCustomStage& s); + + virtual void initUniforms(const GrGLInterface*, int programID) SK_OVERRIDE; + virtual void setData(const GrGLInterface*, + const GrGLTexture&, + const GrCustomStage&, + int stageNum) SK_OVERRIDE; + +private: + int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); } + + int fRadius; + GrMorphologyEffect::MorphologyType fType; + const GrGLShaderVar* fImageIncrementVar; + GrGLint fImageIncrementLocation; + + typedef GrGLProgramStage INHERITED; +}; + +GrGLMorphologyEffect ::GrGLMorphologyEffect(const GrProgramStageFactory& factory, + const GrCustomStage& stage) + : GrGLProgramStage(factory) + , fImageIncrementVar(NULL) + , fImageIncrementLocation(0) { + const GrMorphologyEffect& m = static_cast(stage); + fRadius = m.radius(); + fType = m.type(); +} + +void GrGLMorphologyEffect::setupVariables(GrGLShaderBuilder* state, int stage) { + fImageIncrementVar = &state->addUniform( + GrGLShaderBuilder::kBoth_VariableLifetime, + kVec2f_GrSLType, "uImageIncrement", stage); +} + +void GrGLMorphologyEffect::emitVS(GrGLShaderBuilder* state, + const char* vertexCoords) { + GrStringBuilder* code = &state->fVSCode; + code->appendf("\t\t%s -= vec2(%d, %d) * %s;\n", + vertexCoords, fRadius, fRadius, + fImageIncrementVar->getName().c_str()); +} + +void GrGLMorphologyEffect::initUniforms(const GrGLInterface* gl, + int programID) { + GR_GL_CALL_RET(gl, fImageIncrementLocation, + GetUniformLocation(programID, + fImageIncrementVar->getName().c_str())); +} + +void GrGLMorphologyEffect ::emitFS(GrGLShaderBuilder* state, + const char* outputColor, + const char* inputColor, + const char* samplerName) { + GrStringBuilder* code = &state->fFSCode; + const char* texFunc = "texture2D"; + bool complexCoord = false; + + const char* func; + switch (fType) { + case GrContext::kErode_MorphologyType: + state->fFSCode.appendf("\t\tvec4 value = vec4(1, 1, 1, 1);\n"); + func = "min"; + break; + case GrContext::kDilate_MorphologyType: + state->fFSCode.appendf("\t\tvec4 value = vec4(0, 0, 0, 0);\n"); + func = "max"; + break; + default: + GrCrash("Unexpected type"); + break; + } + + code->appendf("\t\tvec2 coord = %s;\n", state->fSampleCoords.c_str()); + code->appendf("\t\tfor (int i = 0; i < %d; i++) {\n", this->width()); + state->fFSCode.appendf("\t\t\tvalue = %s(value, ", func); + state->emitTextureLookup(samplerName, "coord"); + state->fFSCode.appendf(");\n"); + code->appendf("\t\t\tcoord += %s;\n", + fImageIncrementVar->getName().c_str()); + code->appendf("\t\t}\n"); + + state->fFSCode.appendf("\t\t%s = value%s;\n", + outputColor, + state->fModulate.c_str()); +} + +GrGLProgramStage::StageKey GrGLMorphologyEffect::GenKey( + const GrCustomStage& s) { + const GrMorphologyEffect& m = static_cast(s); + StageKey key = static_cast(m.radius()); + key |= (m.type() << 8); + return key; +} + +void GrGLMorphologyEffect ::setData(const GrGLInterface* gl, + const GrGLTexture& texture, + const GrCustomStage& data, + int stageNum) { + const Gr1DKernelEffect& kern = + static_cast(data); + // the code we generated was for a specific kernel radius + GrAssert(kern.radius() == fRadius); + float imageIncrement[2] = { 0 }; + switch (kern.direction()) { + case Gr1DKernelEffect::kX_Direction: + imageIncrement[0] = 1.0f / texture.width(); + break; + case Gr1DKernelEffect::kY_Direction: + imageIncrement[1] = 1.0f / texture.height(); + break; + default: + GrCrash("Unknown filter direction."); + } + GR_GL_CALL(gl, Uniform2fv(fImageIncrementLocation, 1, imageIncrement)); +} + +/////////////////////////////////////////////////////////////////////////////// + +GrMorphologyEffect::GrMorphologyEffect(Direction direction, + int radius, + MorphologyType type) + : Gr1DKernelEffect(direction, radius) + , fType(type) { +} + +GrMorphologyEffect::~GrMorphologyEffect() { +} + +const GrProgramStageFactory& GrMorphologyEffect::getFactory() const { + return GrTProgramStageFactory::getInstance(); +} + +bool GrMorphologyEffect::isEqual(const GrCustomStage& sBase) const { + const GrMorphologyEffect& s = + static_cast(sBase); + return (this->radius() == s.radius() && + this->direction() == s.direction() && + this->type() == s.type()); +} diff --git a/src/gpu/effects/GrMorphologyEffect.h b/src/gpu/effects/GrMorphologyEffect.h new file mode 100644 index 0000000000..c784460819 --- /dev/null +++ b/src/gpu/effects/GrMorphologyEffect.h @@ -0,0 +1,48 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrMorphologyEffect_DEFINED +#define GrMorphologyEffect_DEFINED + +#include "GrContext.h" +#include "Gr1DKernelEffect.h" + +class GrGLMorphologyEffect; + +/** + * Morphology effects. Depending upon the type of morphology, either the + * component-wise min (Erode_Type) or max (Dilate_Type) of all pixels in the + * kernel is selected as the new color. The new color is modulated by the input + * color. + */ +class GrMorphologyEffect : public Gr1DKernelEffect { + +public: + + typedef GrContext::MorphologyType MorphologyType; + + GrMorphologyEffect(Direction, int radius, MorphologyType); + virtual ~GrMorphologyEffect(); + + MorphologyType type() const { return fType; } + + static const char* Name() { return "Morphology"; } + + typedef GrGLMorphologyEffect GLProgramStage; + + virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE; + virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE; + +protected: + + MorphologyType fType; + +private: + + typedef Gr1DKernelEffect INHERITED; +}; +#endif diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp index 9a78a6e3a7..a49aa61ec4 100644 --- a/src/gpu/gl/GrGLProgram.cpp +++ b/src/gpu/gl/GrGLProgram.cpp @@ -95,18 +95,6 @@ inline void radial2_param_name(int stage, GrStringBuilder* s) { s->appendS32(stage); } -inline void convolve_param_names(int stage, GrStringBuilder* k, GrStringBuilder* i) { - *k = "uKernel"; - k->appendS32(stage); - *i = "uImageIncrement"; - i->appendS32(stage); -} - -inline void image_increment_param_name(int stage, GrStringBuilder* i) { - *i = "uImageIncrement"; - i->appendS32(stage); -} - inline void tex_domain_name(int stage, GrStringBuilder* s) { *s = "uTexDom"; s->appendS32(stage); @@ -629,7 +617,7 @@ bool GrGLProgram::genProgram(const GrGLContextInfo& gl, const GrProgramStageFactory& factory = customStages[s]->getFactory(); programData->fCustomStage[s] = - factory.createGLInstance(customStages[s]); + factory.createGLInstance(*customStages[s]); } this->genStageCode(gl, s, @@ -752,7 +740,7 @@ bool GrGLProgram::genProgram(const GrGLContextInfo& gl, const GrProgramStageFactory& factory = customStages[s]->getFactory(); programData->fCustomStage[s] = - factory.createGLInstance(customStages[s]); + factory.createGLInstance(*customStages[s]); } this->genStageCode(gl, s, fProgramDesc.fStages[s], @@ -1186,21 +1174,6 @@ void GrGLProgram::getUniformLocationsAndInitCache(const GrGLContextInfo& gl, GrAssert(kUnusedUniform != locations.fTexDomUni); } - GrStringBuilder kernelName, imageIncrementName; - convolve_param_names(s, &kernelName, &imageIncrementName); - if (kUseUniform == locations.fKernelUni) { - GL_CALL_RET(locations.fKernelUni, - GetUniformLocation(progID, kernelName.c_str())); - GrAssert(kUnusedUniform != locations.fKernelUni); - } - - if (kUseUniform == locations.fImageIncrementUni) { - GL_CALL_RET(locations.fImageIncrementUni, - GetUniformLocation(progID, - imageIncrementName.c_str())); - GrAssert(kUnusedUniform != locations.fImageIncrementUni); - } - if (NULL != programData->fCustomStage[s]) { programData->fCustomStage[s]-> initUniforms(gl.interface(), progID); @@ -1411,65 +1384,6 @@ void gen2x2FS(int stageNum, } -void genMorphologyVS(int stageNum, - const StageDesc& desc, - GrGLShaderBuilder* segments, - GrGLProgram::StageUniLocations* locations, - const char** imageIncrementName, - const char* varyingVSName) { - - GrStringBuilder iiName; - image_increment_param_name(stageNum, &iiName); - const GrGLShaderVar* imgInc = - &segments->addUniform( - GrGLShaderBuilder::kBoth_VariableLifetime, kVec2f_GrSLType, - iiName.c_str()); - *imageIncrementName = imgInc->getName().c_str(); - - locations->fImageIncrementUni = kUseUniform; - segments->fVSCode.appendf("\t%s -= vec2(%d, %d) * %s;\n", - varyingVSName, desc.fKernelWidth, - desc.fKernelWidth, *imageIncrementName); -} - -void genMorphologyFS(int stageNum, - const StageDesc& desc, - GrGLShaderBuilder* segments, - const char* samplerName, - const char* imageIncrementName, - const char* fsOutColor, - GrStringBuilder& texFunc) { - GrStringBuilder valueVar("value"); - valueVar.appendS32(stageNum); - GrStringBuilder coordVar("coord"); - coordVar.appendS32(stageNum); - bool isDilate = StageDesc::kDilate_FetchMode == desc.fFetchMode; - - if (isDilate) { - segments->fFSCode.appendf("\tvec4 %s = vec4(0, 0, 0, 0);\n", - valueVar.c_str()); - } else { - segments->fFSCode.appendf("\tvec4 %s = vec4(1, 1, 1, 1);\n", - valueVar.c_str()); - } - segments->fFSCode.appendf("\tvec2 %s = %s;\n", - coordVar.c_str(), - segments->fSampleCoords.c_str()); - segments->fFSCode.appendf("\tfor (int i = 0; i < %d; i++) {\n", - desc.fKernelWidth * 2 + 1); - segments->fFSCode.appendf("\t\t%s = %s(%s, %s(%s, %s)%s);\n", - valueVar.c_str(), isDilate ? "max" : "min", - valueVar.c_str(), texFunc.c_str(), - samplerName, coordVar.c_str(), - segments->fSwizzle.c_str()); - segments->fFSCode.appendf("\t\t%s += %s;\n", - coordVar.c_str(), - imageIncrementName); - segments->fFSCode.appendf("\t}\n"); - segments->fFSCode.appendf("\t%s = %s%s;\n", fsOutColor, - valueVar.c_str(), segments->fModulate.c_str()); -} - } void GrGLProgram::genStageCode(const GrGLContextInfo& gl, @@ -1562,12 +1476,6 @@ void GrGLProgram::genStageCode(const GrGLContextInfo& gl, GrGLShaderVar* kernel = NULL; const char* imageIncrementName = NULL; - if (StageDesc::kDilate_FetchMode == desc.fFetchMode || - StageDesc::kErode_FetchMode == desc.fFetchMode) { - genMorphologyVS(stageNum, desc, segments, locations, - &imageIncrementName, varyingVSName); - } - if (NULL != customStage) { segments->fVSCode.appendf("\t{ // stage %d %s\n", stageNum, customStage->name()); @@ -1677,15 +1585,6 @@ void GrGLProgram::genStageCode(const GrGLContextInfo& gl, gen2x2FS(stageNum, segments, locations, samplerName.c_str(), texelSizeName, fsOutColor, texFunc); break; - case StageDesc::kConvolution_FetchMode: - GrAssert(!(desc.fInConfigFlags & kMulByAlphaMask)); - break; - case StageDesc::kDilate_FetchMode: - case StageDesc::kErode_FetchMode: - GrAssert(!(desc.fInConfigFlags & kMulByAlphaMask)); - genMorphologyFS(stageNum, desc, segments, - samplerName.c_str(), imageIncrementName, fsOutColor, texFunc); - break; default: if (desc.fInConfigFlags & kMulByAlphaMask) { // only one of the mul by alpha flags should be set diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h index 189e876ae5..bfb1f1c2e0 100644 --- a/src/gpu/gl/GrGLProgram.h +++ b/src/gpu/gl/GrGLProgram.h @@ -106,13 +106,10 @@ public: kCustomTextureDomain_OptFlagBit = 1 << 2, kIsEnabled_OptFlagBit = 1 << 7 }; - // Convolution is obsolete; left in for testing only + enum FetchMode { kSingle_FetchMode, k2x2_FetchMode, - kConvolution_FetchMode, - kErode_FetchMode, - kDilate_FetchMode, kFetchModeCnt, }; @@ -179,7 +176,6 @@ public: uint8_t fInConfigFlags; // bitfield of InConfigFlags values uint8_t fFetchMode; // casts to enum FetchMode uint8_t fCoordMapping; // casts to enum CoordMapping - uint8_t fKernelWidth; /** Non-zero if user-supplied code will write the stage's contribution to the fragment shader. */ @@ -268,16 +264,12 @@ public: GrGLint fSamplerUni; GrGLint fRadial2Uni; GrGLint fTexDomUni; - GrGLint fKernelUni; - GrGLint fImageIncrementUni; void reset() { fTextureMatrixUni = kUnusedUniform; fNormalizedTexelSizeUni = kUnusedUniform; fSamplerUni = kUnusedUniform; fRadial2Uni = kUnusedUniform; fTexDomUni = kUnusedUniform; - fKernelUni = kUnusedUniform; - fImageIncrementUni = kUnusedUniform; } }; diff --git a/src/gpu/gl/GrGLProgramStage.cpp b/src/gpu/gl/GrGLProgramStage.cpp index 70d11864e3..487b46865f 100644 --- a/src/gpu/gl/GrGLProgramStage.cpp +++ b/src/gpu/gl/GrGLProgramStage.cpp @@ -25,11 +25,9 @@ void GrGLProgramStage::initUniforms(const GrGLInterface*, int progID) { } -void GrGLProgramStage::setData(const GrGLInterface*, - const GrGLTexture&, - GrCustomStage*, +void GrGLProgramStage::setData(const GrGLInterface*, + const GrGLTexture&, + const GrCustomStage&, int stageNum) { - - } diff --git a/src/gpu/gl/GrGLProgramStage.h b/src/gpu/gl/GrGLProgramStage.h index 52d72001a1..e10d13b403 100644 --- a/src/gpu/gl/GrGLProgramStage.h +++ b/src/gpu/gl/GrGLProgramStage.h @@ -32,7 +32,12 @@ class GrGLTexture; class GrGLProgramStage { public: - typedef GrCustomStage::StageKey StageKey ; + typedef GrCustomStage::StageKey StageKey; + enum { + // the number of bits in StageKey available to GenKey + kProgramStageKeyBits = GrProgramStageFactory::kProgramStageKeyBits, + }; + // TODO: redundant with GrGLProgram.cpp enum { kUnusedUniform = -1, @@ -79,7 +84,7 @@ public: created in emit*(). */ virtual void setData(const GrGLInterface* gl, const GrGLTexture& texture, - GrCustomStage* stage, + const GrCustomStage& stage, int stageNum); const char* name() const { return fFactory.name(); } diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp index 8b28c9fb9f..29c7900c45 100644 --- a/src/gpu/gl/GrGpuGL.cpp +++ b/src/gpu/gl/GrGpuGL.cpp @@ -2102,9 +2102,6 @@ unsigned gr_to_gl_filter(GrSamplerState::Filter filter) { case GrSamplerState::k4x4Downsample_Filter: return GR_GL_LINEAR; case GrSamplerState::kNearest_Filter: - case GrSamplerState::kConvolution_Filter: - case GrSamplerState::kErode_Filter: - case GrSamplerState::kDilate_Filter: return GR_GL_NEAREST; default: GrAssert(!"Unknown filter type"); diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp index 2ac26d5675..2448fa46ec 100644 --- a/src/gpu/gl/GrGpuGL_program.cpp +++ b/src/gpu/gl/GrGpuGL_program.cpp @@ -7,8 +7,10 @@ #include "GrGpuGL.h" -#include "GrBinHashKey.h" #include "effects/GrConvolutionEffect.h" +#include "effects/GrMorphologyEffect.h" + +#include "GrBinHashKey.h" #include "GrCustomStage.h" #include "GrGLProgramStage.h" #include "GrGLSL.h" @@ -176,6 +178,75 @@ bool random_bool(GrRandom* r) { return r->nextF() > .5f; } +typedef GrGLProgram::StageDesc StageDesc; +// TODO: Effects should be able to register themselves for inclusion in the +// randomly generated shaders. They should be able to configure themselves +// randomly. +GrCustomStage* create_random_effect(StageDesc* stageDesc, + GrRandom* random) { + enum EffectType { + kConvolution_EffectType, + kErode_EffectType, + kDilate_EffectType, + + kEffectCount + }; + + // TODO: Remove this when generator doesn't apply this non-custom-stage + // notion to custom stages automatically. + static const uint32_t kMulByAlphaMask = + StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag | + StageDesc::kMulRGBByAlpha_RoundDown_InConfigFlag; + + static const Gr1DKernelEffect::Direction gKernelDirections[] = { + Gr1DKernelEffect::kX_Direction, + Gr1DKernelEffect::kY_Direction + }; + + // TODO: When matrices are property of the custom-stage then remove the + // no-persp flag code below. + int effect = random_int(random, kEffectCount); + switch (effect) { + case kConvolution_EffectType: { + int direction = random_int(random, 2); + int kernelRadius = random_int(random, 1, 4); + float kernel[GrConvolutionEffect::kMaxKernelWidth]; + for (int i = 0; i < GrConvolutionEffect::kMaxKernelWidth; i++) { + kernel[i] = random->nextF(); + } + // does not work with perspective or mul-by-alpha-mask + stageDesc->fOptFlags |= StageDesc::kNoPerspective_OptFlagBit; + stageDesc->fInConfigFlags &= ~kMulByAlphaMask; + return new GrConvolutionEffect(gKernelDirections[direction], + kernelRadius, + kernel); + } + case kErode_EffectType: { + int direction = random_int(random, 2); + int kernelRadius = random_int(random, 1, 4); + // does not work with perspective or mul-by-alpha-mask + stageDesc->fOptFlags |= StageDesc::kNoPerspective_OptFlagBit; + stageDesc->fInConfigFlags &= ~kMulByAlphaMask; + return new GrMorphologyEffect(gKernelDirections[direction], + kernelRadius, + GrContext::kErode_MorphologyType); + } + case kDilate_EffectType: { + int direction = random_int(random, 2); + int kernelRadius = random_int(random, 1, 4); + // does not work with perspective or mul-by-alpha-mask + stageDesc->fOptFlags |= StageDesc::kNoPerspective_OptFlagBit; + stageDesc->fInConfigFlags &= ~kMulByAlphaMask; + return new GrMorphologyEffect(gKernelDirections[direction], + kernelRadius, + GrContext::kDilate_MorphologyType); + } + default: + GrCrash("Unexpected custom effect type"); + } + return NULL; +} + } bool GrGpuGL::programUnitTest() { @@ -250,7 +321,7 @@ bool GrGpuGL::programUnitTest() { pdesc.fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput; } - GrCustomStage* customStages[GrDrawState::kNumStages]; + SkAutoTUnref customStages[GrDrawState::kNumStages]; for (int s = 0; s < GrDrawState::kNumStages; ++s) { // enable the stage? @@ -270,56 +341,34 @@ bool GrGpuGL::programUnitTest() { StageDesc& stage = pdesc.fStages[s]; stage.fCustomStageKey = 0; - customStages[s] = NULL; stage.fOptFlags = STAGE_OPTS[random_int(&random, GR_ARRAY_COUNT(STAGE_OPTS))]; stage.fInConfigFlags = IN_CONFIG_FLAGS[random_int(&random, GR_ARRAY_COUNT(IN_CONFIG_FLAGS))]; stage.fCoordMapping = random_int(&random, StageDesc::kCoordMappingCnt); stage.fFetchMode = random_int(&random, StageDesc::kFetchModeCnt); - // convolution shaders don't work with persp tex matrix - if (stage.fFetchMode == StageDesc::kConvolution_FetchMode || - stage.fFetchMode == StageDesc::kDilate_FetchMode || - stage.fFetchMode == StageDesc::kErode_FetchMode) { - stage.fOptFlags |= StageDesc::kNoPerspective_OptFlagBit; - } stage.setEnabled(VertexUsesStage(s, pdesc.fVertexLayout)); static const uint32_t kMulByAlphaMask = StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag | StageDesc::kMulRGBByAlpha_RoundDown_InConfigFlag; - switch (stage.fFetchMode) { - case StageDesc::kSingle_FetchMode: - stage.fKernelWidth = 0; - break; - case StageDesc::kConvolution_FetchMode: - case StageDesc::kDilate_FetchMode: - case StageDesc::kErode_FetchMode: - stage.fKernelWidth = random_int(&random, 2, 8); - stage.fInConfigFlags &= ~kMulByAlphaMask; - break; - case StageDesc::k2x2_FetchMode: - stage.fKernelWidth = 0; - stage.fInConfigFlags &= ~kMulByAlphaMask; - break; + if (StageDesc::k2x2_FetchMode == stage.fFetchMode) { + stage.fInConfigFlags &= ~kMulByAlphaMask; } - // TODO: is there a more elegant way to express this? - if (stage.fFetchMode == StageDesc::kConvolution_FetchMode) { - int direction = random_int(&random, 2); - float kernel[MAX_KERNEL_WIDTH]; - for (int i = 0; i < stage.fKernelWidth; i++) { - kernel[i] = random.nextF(); + bool useCustomEffect = random_bool(&random); + if (useCustomEffect) { + customStages[s].reset(create_random_effect(&stage, &random)); + if (NULL != customStages[s]) { + stage.fCustomStageKey = + customStages[s]->getFactory().glStageKey(*customStages[s]); } - customStages[s] = new GrConvolutionEffect( - (GrSamplerState::FilterDirection)direction, - stage.fKernelWidth, kernel); - stage.fCustomStageKey = - customStages[s]->getFactory().glStageKey(customStages[s]); } } CachedData cachedData; - if (!program.genProgram(this->glContextInfo(), customStages, - &cachedData)) { + GR_STATIC_ASSERT(sizeof(customStages) == + GrDrawState::kNumStages * sizeof(GrCustomStage*)); + GrCustomStage** stages = reinterpret_cast(&customStages); + if (!program.genProgram(this->glContextInfo(), stages, &cachedData)) { return false; } DeleteProgram(this->glInterface(), &cachedData); @@ -469,32 +518,6 @@ void GrGpuGL::flushRadial2(int s) { } } -void GrGpuGL::flushConvolution(int s) { - const GrSamplerState& sampler = this->getDrawState().getSampler(s); - int kernelUni = fProgramData->fUniLocations.fStages[s].fKernelUni; - if (GrGLProgram::kUnusedUniform != kernelUni) { - GL_CALL(Uniform1fv(kernelUni, sampler.getKernelWidth(), - sampler.getKernel())); - } - int imageIncrementUni = fProgramData->fUniLocations.fStages[s].fImageIncrementUni; - if (GrGLProgram::kUnusedUniform != imageIncrementUni) { - const GrGLTexture* texture = - static_cast(this->getDrawState().getTexture(s)); - float imageIncrement[2] = { 0 }; - switch (sampler.getFilterDirection()) { - case GrSamplerState::kX_FilterDirection: - imageIncrement[0] = 1.0f / texture->width(); - break; - case GrSamplerState::kY_FilterDirection: - imageIncrement[1] = 1.0f / texture->height(); - break; - default: - GrCrash("Unknown filter direction."); - } - GL_CALL(Uniform2fv(imageIncrementUni, 1, imageIncrement)); - } -} - void GrGpuGL::flushTexelSize(int s) { const int& uni = fProgramData->fUniLocations.fStages[s].fNormalizedTexelSizeUni; if (GrGLProgram::kUnusedUniform != uni) { @@ -692,8 +715,6 @@ bool GrGpuGL::flushGraphicsState(GrPrimitiveType type) { this->flushRadial2(s); - this->flushConvolution(s); - this->flushTexelSize(s); this->flushTextureDomain(s); @@ -706,7 +727,7 @@ bool GrGpuGL::flushGraphicsState(GrPrimitiveType type) { this->getDrawState().getTexture(s)); fProgramData->fCustomStage[s]->setData( this->glInterface(), *texture, - sampler.getCustomStage(), s); + *sampler.getCustomStage(), s); } } } @@ -875,7 +896,7 @@ void setup_custom_stage(GrGLProgram::ProgramDesc::StageDesc* stage, GrCustomStage* customStage = sampler.getCustomStage(); if (customStage) { const GrProgramStageFactory& factory = customStage->getFactory(); - stage->fCustomStageKey = factory.glStageKey(customStage); + stage->fCustomStageKey = factory.glStageKey(*customStage); customStages[index] = customStage; } else { stage->fCustomStageKey = 0; @@ -1027,16 +1048,6 @@ void GrGpuGL::buildProgram(GrPrimitiveType type, case GrSamplerState::k4x4Downsample_Filter: stage.fFetchMode = StageDesc::k2x2_FetchMode; break; - // performs fKernelWidth texture2D()s - case GrSamplerState::kConvolution_Filter: - stage.fFetchMode = StageDesc::kConvolution_FetchMode; - break; - case GrSamplerState::kDilate_Filter: - stage.fFetchMode = StageDesc::kDilate_FetchMode; - break; - case GrSamplerState::kErode_Filter: - stage.fFetchMode = StageDesc::kErode_FetchMode; - break; default: GrCrash("Unexpected filter!"); break; @@ -1083,13 +1094,6 @@ void GrGpuGL::buildProgram(GrPrimitiveType type, } } - if (sampler.getFilter() == GrSamplerState::kDilate_Filter || - sampler.getFilter() == GrSamplerState::kErode_Filter) { - stage.fKernelWidth = sampler.getKernelWidth(); - } else { - stage.fKernelWidth = 0; - } - setup_custom_stage(&stage, sampler, customStages, &fCurrentProgram, s); @@ -1098,7 +1102,6 @@ void GrGpuGL::buildProgram(GrPrimitiveType type, stage.fCoordMapping = (StageDesc::CoordMapping) 0; stage.fInConfigFlags = 0; stage.fFetchMode = (StageDesc::FetchMode) 0; - stage.fKernelWidth = 0; stage.fCustomStageKey = 0; customStages[s] = NULL; }