Index: src/gpu/effects/GrConvolutionEffect.cpp |
diff --git a/src/gpu/effects/GrConvolutionEffect.cpp b/src/gpu/effects/GrConvolutionEffect.cpp |
index 1e1c477cd5bf6da947a6b4022be99404e054c860..f5b5e22ce15dfe127bb4b32308eb086fbe0477a8 100644 |
--- a/src/gpu/effects/GrConvolutionEffect.cpp |
+++ b/src/gpu/effects/GrConvolutionEffect.cpp |
@@ -13,24 +13,32 @@ |
// For brevity |
typedef GrGLProgramDataManager::UniformHandle UniformHandle; |
-/** |
- * Base class with shared functionality for GrGLBoundedConvolutionEffect and |
- * GrGLLerpConvolutionEffect. |
- */ |
class GrGLConvolutionEffect : public GrGLFragmentProcessor { |
public: |
GrGLConvolutionEffect(const GrProcessor&); |
+ |
+ virtual void emitCode(GrGLFPBuilder*, |
+ const GrFragmentProcessor&, |
+ const char* outputColor, |
+ const char* inputColor, |
+ const TransformedCoordsArray&, |
+ const TextureSamplerArray&) override; |
+ |
+ void setData(const GrGLProgramDataManager& pdman, const GrProcessor&) override; |
+ |
static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*); |
-protected: |
- int radius() const { return fRadius; } |
+private: |
int width() const { return Gr1DKernelEffect::WidthFromRadius(fRadius); } |
+ bool useBounds() const { return fUseBounds; } |
Gr1DKernelEffect::Direction direction() const { return fDirection; } |
- void getImageIncrement(const GrConvolutionEffect&, float (*)[2]) const; |
- |
-private: |
- int fRadius; |
- Gr1DKernelEffect::Direction fDirection; |
+ |
+ int fRadius; |
+ bool fUseBounds; |
+ Gr1DKernelEffect::Direction fDirection; |
+ UniformHandle fKernelUni; |
+ UniformHandle fImageIncrementUni; |
+ UniformHandle fBoundsUni; |
typedef GrGLFragmentProcessor INHERITED; |
}; |
@@ -38,11 +46,101 @@ |
GrGLConvolutionEffect::GrGLConvolutionEffect(const GrProcessor& processor) { |
const GrConvolutionEffect& c = processor.cast<GrConvolutionEffect>(); |
fRadius = c.radius(); |
+ fUseBounds = c.useBounds(); |
fDirection = c.direction(); |
} |
-void GrGLConvolutionEffect::GenKey(const GrProcessor& processor, |
- const GrGLSLCaps&, |
+void GrGLConvolutionEffect::emitCode(GrGLFPBuilder* builder, |
+ const GrFragmentProcessor&, |
+ const char* outputColor, |
+ const char* inputColor, |
+ const TransformedCoordsArray& coords, |
+ const TextureSamplerArray& samplers) { |
+ fImageIncrementUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, |
+ kVec2f_GrSLType, kDefault_GrSLPrecision, |
+ "ImageIncrement"); |
+ if (this->useBounds()) { |
+ fBoundsUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, |
+ kVec2f_GrSLType, kDefault_GrSLPrecision, |
+ "Bounds"); |
+ } |
+ fKernelUni = builder->addUniformArray(GrGLProgramBuilder::kFragment_Visibility, |
+ kFloat_GrSLType, kDefault_GrSLPrecision, |
+ "Kernel", this->width()); |
+ |
+ GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder(); |
+ SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0); |
+ |
+ fsBuilder->codeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor); |
+ |
+ int width = this->width(); |
+ const GrGLShaderVar& kernel = builder->getUniformVariable(fKernelUni); |
+ const char* imgInc = builder->getUniformCStr(fImageIncrementUni); |
+ |
+ fsBuilder->codeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords2D.c_str(), fRadius, imgInc); |
+ |
+ // Manually unroll loop because some drivers don't; yields 20-30% speedup. |
+ for (int i = 0; i < width; i++) { |
+ SkString index; |
+ SkString kernelIndex; |
+ index.appendS32(i); |
+ kernel.appendArrayAccess(index.c_str(), &kernelIndex); |
+ |
+ if (this->useBounds()) { |
+ // We used to compute a bool indicating whether we're in bounds or not, cast it to a |
+ // float, and then mul weight*texture_sample by the float. However, the Adreno 430 seems |
+ // to have a bug that caused corruption. |
+ const char* bounds = builder->getUniformCStr(fBoundsUni); |
+ const char* component = this->direction() == Gr1DKernelEffect::kY_Direction ? "y" : "x"; |
+ fsBuilder->codeAppendf("if (coord.%s >= %s.x && coord.%s <= %s.y) {", |
+ component, bounds, component, bounds); |
+ } |
+ fsBuilder->codeAppendf("\t\t%s += ", outputColor); |
+ fsBuilder->appendTextureLookup(samplers[0], "coord"); |
+ fsBuilder->codeAppendf(" * %s;\n", kernelIndex.c_str()); |
+ if (this->useBounds()) { |
+ fsBuilder->codeAppend("}"); |
+ } |
+ fsBuilder->codeAppendf("\t\tcoord += %s;\n", imgInc); |
+ } |
+ |
+ SkString modulate; |
+ GrGLSLMulVarBy4f(&modulate, outputColor, inputColor); |
+ fsBuilder->codeAppend(modulate.c_str()); |
+} |
+ |
+void GrGLConvolutionEffect::setData(const GrGLProgramDataManager& pdman, |
+ const GrProcessor& processor) { |
+ const GrConvolutionEffect& conv = processor.cast<GrConvolutionEffect>(); |
+ GrTexture& texture = *conv.texture(0); |
+ // the code we generated was for a specific kernel radius |
+ SkASSERT(conv.radius() == fRadius); |
+ float imageIncrement[2] = { 0 }; |
+ float ySign = texture.origin() != kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f; |
+ switch (conv.direction()) { |
+ case Gr1DKernelEffect::kX_Direction: |
+ imageIncrement[0] = 1.0f / texture.width(); |
+ break; |
+ case Gr1DKernelEffect::kY_Direction: |
+ imageIncrement[1] = ySign / texture.height(); |
+ break; |
+ default: |
+ SkFAIL("Unknown filter direction."); |
+ } |
+ pdman.set2fv(fImageIncrementUni, 1, imageIncrement); |
+ if (conv.useBounds()) { |
+ const float* bounds = conv.bounds(); |
+ if (Gr1DKernelEffect::kY_Direction == conv.direction() && |
+ texture.origin() != kTopLeft_GrSurfaceOrigin) { |
+ pdman.set2f(fBoundsUni, 1.0f - bounds[1], 1.0f - bounds[0]); |
+ } else { |
+ pdman.set2f(fBoundsUni, bounds[0], bounds[1]); |
+ } |
+ } |
+ pdman.set1fv(fKernelUni, this->width(), conv.kernel()); |
+} |
+ |
+void GrGLConvolutionEffect::GenKey(const GrProcessor& processor, const GrGLSLCaps&, |
GrProcessorKeyBuilder* b) { |
const GrConvolutionEffect& conv = processor.cast<GrConvolutionEffect>(); |
uint32_t key = conv.radius(); |
@@ -54,261 +152,6 @@ |
b->add32(key); |
} |
-void GrGLConvolutionEffect::getImageIncrement(const GrConvolutionEffect& conv, |
- float (*imageIncrement)[2]) const { |
- GrTexture& texture = *conv.texture(0); |
- (*imageIncrement)[0] = (*imageIncrement)[1] = 0; |
- float ySign = texture.origin() != kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f; |
- switch (conv.direction()) { |
- case Gr1DKernelEffect::kX_Direction: |
- (*imageIncrement)[0] = 1.0f / texture.width(); |
- break; |
- case Gr1DKernelEffect::kY_Direction: |
- (*imageIncrement)[1] = ySign / texture.height(); |
- break; |
- default: |
- SkFAIL("Unknown filter direction."); |
- } |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-/** |
- * Applies a convolution effect which restricts samples to the provided bounds |
- * using shader logic. |
- */ |
-class GrGLBoundedConvolutionEffect : public GrGLConvolutionEffect { |
-public: |
- GrGLBoundedConvolutionEffect(const GrProcessor& processor) : INHERITED(processor) {} |
- |
- virtual void emitCode(GrGLFPBuilder*, |
- const GrFragmentProcessor&, |
- const char* outputColor, |
- const char* inputColor, |
- const TransformedCoordsArray&, |
- const TextureSamplerArray&) override; |
- |
- void setData(const GrGLProgramDataManager& pdman, const GrProcessor&) override; |
- |
-private: |
- UniformHandle fKernelUni; |
- UniformHandle fImageIncrementUni; |
- UniformHandle fBoundsUni; |
- |
- typedef GrGLConvolutionEffect INHERITED; |
-}; |
- |
-void GrGLBoundedConvolutionEffect::emitCode(GrGLFPBuilder* builder, |
- const GrFragmentProcessor& processor, |
- const char* outputColor, |
- const char* inputColor, |
- const TransformedCoordsArray& coords, |
- const TextureSamplerArray& samplers) { |
- fImageIncrementUni = |
- builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec2f_GrSLType, |
- kDefault_GrSLPrecision, "ImageIncrement"); |
- |
- fBoundsUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec2f_GrSLType, |
- kDefault_GrSLPrecision, "Bounds"); |
- |
- fKernelUni = builder->addUniformArray(GrGLProgramBuilder::kFragment_Visibility, kFloat_GrSLType, |
- kDefault_GrSLPrecision, "Kernel", this->width()); |
- |
- GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder(); |
- SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0); |
- |
- fsBuilder->codeAppendf("%s = vec4(0, 0, 0, 0);\n", outputColor); |
- |
- int width = this->width(); |
- const GrGLShaderVar& kernel = builder->getUniformVariable(fKernelUni); |
- const char* imgInc = builder->getUniformCStr(fImageIncrementUni); |
- |
- fsBuilder->codeAppendf("vec2 coord = %s - %d.0 * %s;\n", coords2D.c_str(), this->radius(), |
- imgInc); |
- |
- // Manually unroll loop because some drivers don't; yields 20-30% speedup. |
- for (int i = 0; i < width; i++) { |
- SkString index; |
- SkString kernelIndex; |
- index.appendS32(i); |
- kernel.appendArrayAccess(index.c_str(), &kernelIndex); |
- // We used to compute a bool indicating whether we're in bounds or not, cast it to a |
- // float, and then mul weight*texture_sample by the float. However, the Adreno 430 seems |
- // to have a bug that caused corruption. |
- const char* bounds = builder->getUniformCStr(fBoundsUni); |
- const char* component = this->direction() == Gr1DKernelEffect::kY_Direction ? "y" : "x"; |
- fsBuilder->codeAppendf("if (coord.%s >= %s.x && coord.%s <= %s.y) {", |
- component, bounds, component, bounds); |
- fsBuilder->codeAppendf("%s += ", outputColor); |
- fsBuilder->appendTextureLookup(samplers[0], "coord"); |
- fsBuilder->codeAppendf(" * %s;\n", kernelIndex.c_str()); |
- fsBuilder->codeAppend("}"); |
- fsBuilder->codeAppendf("coord += %s;\n", imgInc); |
- } |
- |
- SkString modulate; |
- GrGLSLMulVarBy4f(&modulate, outputColor, inputColor); |
- fsBuilder->codeAppend(modulate.c_str()); |
-} |
- |
-void GrGLBoundedConvolutionEffect::setData(const GrGLProgramDataManager& pdman, |
- const GrProcessor& processor) { |
- const GrConvolutionEffect& conv = processor.cast<GrConvolutionEffect>(); |
- |
- // the code we generated was for a specific kernel radius |
- SkASSERT(conv.radius() == this->radius()); |
- |
- // the code we generated was for a specific bounding mode. |
- SkASSERT(conv.useBounds()); |
- |
- GrTexture& texture = *conv.texture(0); |
- float imageIncrement[2]; |
- getImageIncrement(conv, &imageIncrement); |
- pdman.set2fv(fImageIncrementUni, 1, imageIncrement); |
- const float* bounds = conv.bounds(); |
- if (Gr1DKernelEffect::kY_Direction == conv.direction() && |
- texture.origin() != kTopLeft_GrSurfaceOrigin) { |
- pdman.set2f(fBoundsUni, 1.0f - bounds[1], 1.0f - bounds[0]); |
- } else { |
- pdman.set2f(fBoundsUni, bounds[0], bounds[1]); |
- } |
- pdman.set1fv(fKernelUni, this->width(), conv.kernel()); |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-/** |
- * Applies a convolution effect which applies the convolution using a linear |
- * interpolation optimization to use half as many samples. |
- */ |
-class GrGLLerpConvolutionEffect : public GrGLConvolutionEffect { |
-public: |
- GrGLLerpConvolutionEffect(const GrProcessor& processor) : INHERITED(processor) {} |
- |
- virtual void emitCode(GrGLFPBuilder*, |
- const GrFragmentProcessor&, |
- const char* outputColor, |
- const char* inputColor, |
- const TransformedCoordsArray&, |
- const TextureSamplerArray&) override; |
- |
- void setData(const GrGLProgramDataManager& pdman, const GrProcessor&) override; |
- |
-private: |
- int bilerpSampleCount() const; |
- |
- // Bounded uniforms |
- UniformHandle fSampleWeightUni; |
- UniformHandle fSampleOffsetUni; |
- |
- typedef GrGLConvolutionEffect INHERITED; |
-}; |
- |
-void GrGLLerpConvolutionEffect::emitCode(GrGLFPBuilder* builder, |
- const GrFragmentProcessor& processor, |
- const char* outputColor, |
- const char* inputColor, |
- const TransformedCoordsArray& coords, |
- const TextureSamplerArray& samplers) { |
- int sampleCount = bilerpSampleCount(); |
- |
- // We use 2 * sampleCount uniforms. The maximum allowed by PS2.0 is 32, so |
- // ensure we don't exceed this. Note that it is currently impossible to |
- // exceed this as bilerpSampleCount = (kernelWidth + 1) / 2, and kernelWidth |
- // maxes out at 25, resulting in a max sampleCount of 26. |
- SkASSERT(sampleCount < 16); |
- |
- fSampleOffsetUni = |
- builder->addUniformArray(GrGLProgramBuilder::kFragment_Visibility, kVec2f_GrSLType, |
- kDefault_GrSLPrecision, "SampleOffset", sampleCount); |
- fSampleWeightUni = |
- builder->addUniformArray(GrGLProgramBuilder::kFragment_Visibility, kFloat_GrSLType, |
- kDefault_GrSLPrecision, "SampleWeight", sampleCount); |
- |
- GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder(); |
- SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0); |
- |
- fsBuilder->codeAppendf("%s = vec4(0, 0, 0, 0);\n", outputColor); |
- |
- const GrGLShaderVar& kernel = builder->getUniformVariable(fSampleWeightUni); |
- const GrGLShaderVar& imgInc = builder->getUniformVariable(fSampleOffsetUni); |
- |
- fsBuilder->codeAppendf("vec2 coord; \n"); |
- |
- // Manually unroll loop because some drivers don't; yields 20-30% speedup. |
- for (int i = 0; i < sampleCount; i++) { |
- SkString index; |
- SkString weightIndex; |
- SkString offsetIndex; |
- index.appendS32(i); |
- kernel.appendArrayAccess(index.c_str(), &weightIndex); |
- imgInc.appendArrayAccess(index.c_str(), &offsetIndex); |
- fsBuilder->codeAppendf("coord = %s + %s;\n", coords2D.c_str(), offsetIndex.c_str()); |
- fsBuilder->codeAppendf("%s += ", outputColor); |
- fsBuilder->appendTextureLookup(samplers[0], "coord"); |
- fsBuilder->codeAppendf(" * %s;\n", weightIndex.c_str()); |
- } |
- |
- SkString modulate; |
- GrGLSLMulVarBy4f(&modulate, outputColor, inputColor); |
- fsBuilder->codeAppend(modulate.c_str()); |
-} |
- |
-void GrGLLerpConvolutionEffect::setData(const GrGLProgramDataManager& pdman, |
- const GrProcessor& processor) { |
- const GrConvolutionEffect& conv = processor.cast<GrConvolutionEffect>(); |
- |
- // the code we generated was for a specific kernel radius |
- SkASSERT(conv.radius() == this->radius()); |
- |
- // the code we generated was for a specific bounding mode. |
- SkASSERT(!conv.useBounds()); |
- |
- int sampleCount = bilerpSampleCount(); |
- SkAutoTArray<float> imageIncrements(sampleCount * 2); // X and Y floats per sample. |
- SkAutoTArray<float> kernel(sampleCount); |
- |
- float baseImageIncrement[2]; |
- getImageIncrement(conv, &baseImageIncrement); |
- |
- for (int i = 0; i < sampleCount; i++) { |
- int sampleIndex1 = i * 2; |
- int sampleIndex2 = sampleIndex1 + 1; |
- |
- // If we have an odd number of samples in our filter, the last sample won't use |
- // the linear interpolation optimization (it will be pixel aligned). |
- if (sampleIndex2 >= this->width()) { |
- sampleIndex2 = sampleIndex1; |
- } |
- |
- float kernelWeight1 = conv.kernel()[sampleIndex1]; |
- float kernelWeight2 = conv.kernel()[sampleIndex2]; |
- |
- float totalKernelWeight = |
- (sampleIndex1 == sampleIndex2) ? kernelWeight1 : (kernelWeight1 + kernelWeight2); |
- |
- float sampleRatio = |
- (sampleIndex1 == sampleIndex2) ? 0 : kernelWeight2 / (kernelWeight1 + kernelWeight2); |
- |
- imageIncrements[i * 2] = (-this->radius() + i * 2 + sampleRatio) * baseImageIncrement[0]; |
- imageIncrements[i * 2 + 1] = |
- (-this->radius() + i * 2 + sampleRatio) * baseImageIncrement[1]; |
- |
- kernel[i] = totalKernelWeight; |
- } |
- pdman.set2fv(fSampleOffsetUni, sampleCount, imageIncrements.get()); |
- pdman.set1fv(fSampleWeightUni, sampleCount, kernel.get()); |
-} |
- |
-int GrGLLerpConvolutionEffect::bilerpSampleCount() const { |
- // We use a linear interpolation optimization to only sample once for each |
- // two pixel aligned samples in the kernel. If we have an odd number of |
- // samples, we will have to skip this optimization for the last sample. |
- // Because of this we always round up our sample count (by adding 1 before |
- // dividing). |
- return (this->width() + 1) / 2; |
-} |
- |
/////////////////////////////////////////////////////////////////////////////// |
GrConvolutionEffect::GrConvolutionEffect(GrProcessorDataManager* procDataManager, |
@@ -318,13 +161,7 @@ |
const float* kernel, |
bool useBounds, |
float bounds[2]) |
- : INHERITED(procDataManager, |
- texture, |
- direction, |
- radius, |
- useBounds ? GrTextureParams::FilterMode::kNone_FilterMode |
- : GrTextureParams::FilterMode::kBilerp_FilterMode) |
- , fUseBounds(useBounds) { |
+ : INHERITED(procDataManager, texture, direction, radius), fUseBounds(useBounds) { |
this->initClassID<GrConvolutionEffect>(); |
SkASSERT(radius <= kMaxKernelRadius); |
SkASSERT(kernel); |
@@ -342,13 +179,7 @@ |
float gaussianSigma, |
bool useBounds, |
float bounds[2]) |
- : INHERITED(procDataManager, |
- texture, |
- direction, |
- radius, |
- useBounds ? GrTextureParams::FilterMode::kNone_FilterMode |
- : GrTextureParams::FilterMode::kBilerp_FilterMode) |
- , fUseBounds(useBounds) { |
+ : INHERITED(procDataManager, texture, direction, radius), fUseBounds(useBounds) { |
this->initClassID<GrConvolutionEffect>(); |
SkASSERT(radius <= kMaxKernelRadius); |
int width = this->width(); |
@@ -379,15 +210,7 @@ |
} |
GrGLFragmentProcessor* GrConvolutionEffect::createGLInstance() const { |
- // We support a linear interpolation optimization which (when feasible) uses |
- // half the number of samples to apply the kernel. This is not always |
- // applicable, as the linear interpolation optimization does not support |
- // bounded sampling. |
- if (this->useBounds()) { |
- return SkNEW_ARGS(GrGLBoundedConvolutionEffect, (*this)); |
- } else { |
- return SkNEW_ARGS(GrGLLerpConvolutionEffect, (*this)); |
- } |
+ return SkNEW_ARGS(GrGLConvolutionEffect, (*this)); |
} |
bool GrConvolutionEffect::onIsEqual(const GrFragmentProcessor& sBase) const { |