GrGaussianConvolutionFragmentProcessor.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /*
  2. * Copyright 2012 Google Inc.
  3. *
  4. * Use of this source code is governed by a BSD-style license that can be
  5. * found in the LICENSE file.
  6. */
  7. #include "src/gpu/effects/GrGaussianConvolutionFragmentProcessor.h"
  8. #include "include/gpu/GrTexture.h"
  9. #include "src/gpu/GrTextureProxy.h"
  10. #include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
  11. #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
  12. #include "src/gpu/glsl/GrGLSLProgramDataManager.h"
  13. #include "src/gpu/glsl/GrGLSLUniformHandler.h"
  14. // For brevity
  15. using UniformHandle = GrGLSLProgramDataManager::UniformHandle;
  16. using Direction = GrGaussianConvolutionFragmentProcessor::Direction;
  17. class GrGLConvolutionEffect : public GrGLSLFragmentProcessor {
  18. public:
  19. void emitCode(EmitArgs&) override;
  20. static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
  21. protected:
  22. void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
  23. private:
  24. UniformHandle fKernelUni;
  25. UniformHandle fImageIncrementUni;
  26. UniformHandle fBoundsUni;
  27. typedef GrGLSLFragmentProcessor INHERITED;
  28. };
  29. void GrGLConvolutionEffect::emitCode(EmitArgs& args) {
  30. const GrGaussianConvolutionFragmentProcessor& ce =
  31. args.fFp.cast<GrGaussianConvolutionFragmentProcessor>();
  32. GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
  33. fImageIncrementUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType,
  34. "ImageIncrement");
  35. if (ce.useBounds()) {
  36. fBoundsUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType,
  37. "Bounds");
  38. }
  39. int width = ce.width();
  40. int arrayCount = (width + 3) / 4;
  41. SkASSERT(4 * arrayCount >= width);
  42. fKernelUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag, kHalf4_GrSLType,
  43. "Kernel", arrayCount);
  44. GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
  45. SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
  46. fragBuilder->codeAppendf("%s = half4(0, 0, 0, 0);", args.fOutputColor);
  47. const GrShaderVar& kernel = uniformHandler->getUniformVariable(fKernelUni);
  48. const char* imgInc = uniformHandler->getUniformCStr(fImageIncrementUni);
  49. fragBuilder->codeAppendf("float2 coord = %s - %d.0 * %s;", coords2D.c_str(), ce.radius(), imgInc);
  50. fragBuilder->codeAppend("float2 coordSampled = half2(0, 0);");
  51. // Manually unroll loop because some drivers don't; yields 20-30% speedup.
  52. const char* kVecSuffix[4] = {".x", ".y", ".z", ".w"};
  53. for (int i = 0; i < width; i++) {
  54. SkString index;
  55. SkString kernelIndex;
  56. index.appendS32(i / 4);
  57. kernel.appendArrayAccess(index.c_str(), &kernelIndex);
  58. kernelIndex.append(kVecSuffix[i & 0x3]);
  59. fragBuilder->codeAppend("coordSampled = coord;");
  60. if (ce.useBounds()) {
  61. // We used to compute a bool indicating whether we're in bounds or not, cast it to a
  62. // float, and then mul weight*texture_sample by the float. However, the Adreno 430 seems
  63. // to have a bug that caused corruption.
  64. const char* bounds = uniformHandler->getUniformCStr(fBoundsUni);
  65. const char* component = ce.direction() == Direction::kY ? "y" : "x";
  66. switch (ce.mode()) {
  67. case GrTextureDomain::kClamp_Mode: {
  68. fragBuilder->codeAppendf("coordSampled.%s = clamp(coord.%s, %s.x, %s.y);\n",
  69. component, component, bounds, bounds);
  70. break;
  71. }
  72. case GrTextureDomain::kRepeat_Mode: {
  73. fragBuilder->codeAppendf("coordSampled.%s = "
  74. "mod(coord.%s - %s.x, %s.y - %s.x) + %s.x;\n",
  75. component, component, bounds, bounds, bounds, bounds);
  76. break;
  77. }
  78. case GrTextureDomain::kDecal_Mode: {
  79. fragBuilder->codeAppendf("if (coord.%s >= %s.x && coord.%s <= %s.y) {",
  80. component, bounds, component, bounds);
  81. break;
  82. }
  83. default: {
  84. SK_ABORT("Unsupported operation.");
  85. }
  86. }
  87. }
  88. fragBuilder->codeAppendf("%s += ", args.fOutputColor);
  89. fragBuilder->appendTextureLookup(args.fTexSamplers[0], "coordSampled");
  90. fragBuilder->codeAppendf(" * %s;\n", kernelIndex.c_str());
  91. if (GrTextureDomain::kDecal_Mode == ce.mode()) {
  92. fragBuilder->codeAppend("}");
  93. }
  94. fragBuilder->codeAppendf("coord += %s;\n", imgInc);
  95. }
  96. fragBuilder->codeAppendf("%s *= %s;\n", args.fOutputColor, args.fInputColor);
  97. }
  98. void GrGLConvolutionEffect::onSetData(const GrGLSLProgramDataManager& pdman,
  99. const GrFragmentProcessor& processor) {
  100. const GrGaussianConvolutionFragmentProcessor& conv =
  101. processor.cast<GrGaussianConvolutionFragmentProcessor>();
  102. GrSurfaceProxy* proxy = conv.textureSampler(0).proxy();
  103. GrTexture& texture = *proxy->peekTexture();
  104. float imageIncrement[2] = {0};
  105. float ySign = proxy->origin() != kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
  106. switch (conv.direction()) {
  107. case Direction::kX:
  108. imageIncrement[0] = 1.0f / texture.width();
  109. break;
  110. case Direction::kY:
  111. imageIncrement[1] = ySign / texture.height();
  112. break;
  113. default:
  114. SK_ABORT("Unknown filter direction.");
  115. }
  116. pdman.set2fv(fImageIncrementUni, 1, imageIncrement);
  117. if (conv.useBounds()) {
  118. float bounds[2] = {0};
  119. bounds[0] = conv.bounds()[0];
  120. bounds[1] = conv.bounds()[1];
  121. if (GrTextureDomain::kClamp_Mode == conv.mode()) {
  122. bounds[0] += SK_ScalarHalf;
  123. bounds[1] -= SK_ScalarHalf;
  124. }
  125. if (Direction::kX == conv.direction()) {
  126. SkScalar inv = SkScalarInvert(SkIntToScalar(texture.width()));
  127. bounds[0] *= inv;
  128. bounds[1] *= inv;
  129. } else {
  130. SkScalar inv = SkScalarInvert(SkIntToScalar(texture.height()));
  131. if (proxy->origin() != kTopLeft_GrSurfaceOrigin) {
  132. float tmp = bounds[0];
  133. bounds[0] = 1.0f - (inv * bounds[1]);
  134. bounds[1] = 1.0f - (inv * tmp);
  135. } else {
  136. bounds[0] *= inv;
  137. bounds[1] *= inv;
  138. }
  139. }
  140. SkASSERT(bounds[0] <= bounds[1]);
  141. pdman.set2f(fBoundsUni, bounds[0], bounds[1]);
  142. }
  143. int width = conv.width();
  144. int arrayCount = (width + 3) / 4;
  145. SkASSERT(4 * arrayCount >= width);
  146. pdman.set4fv(fKernelUni, arrayCount, conv.kernel());
  147. }
  148. void GrGLConvolutionEffect::GenKey(const GrProcessor& processor, const GrShaderCaps&,
  149. GrProcessorKeyBuilder* b) {
  150. const GrGaussianConvolutionFragmentProcessor& conv =
  151. processor.cast<GrGaussianConvolutionFragmentProcessor>();
  152. uint32_t key = conv.radius();
  153. key <<= 3;
  154. if (conv.useBounds()) {
  155. key |= Direction::kY == conv.direction() ? 0x4 : 0x0;
  156. }
  157. key |= static_cast<uint32_t>(conv.mode());
  158. b->add32(key);
  159. }
  160. ///////////////////////////////////////////////////////////////////////////////
  161. static void fill_in_1D_gaussian_kernel(float* kernel, int width, float gaussianSigma, int radius) {
  162. const float twoSigmaSqrd = 2.0f * gaussianSigma * gaussianSigma;
  163. if (SkScalarNearlyZero(twoSigmaSqrd, SK_ScalarNearlyZero)) {
  164. for (int i = 0; i < width; ++i) {
  165. kernel[i] = 0.0f;
  166. }
  167. return;
  168. }
  169. const float denom = 1.0f / twoSigmaSqrd;
  170. float sum = 0.0f;
  171. for (int i = 0; i < width; ++i) {
  172. float x = static_cast<float>(i - radius);
  173. // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
  174. // is dropped here, since we renormalize the kernel below.
  175. kernel[i] = sk_float_exp(-x * x * denom);
  176. sum += kernel[i];
  177. }
  178. // Normalize the kernel
  179. float scale = 1.0f / sum;
  180. for (int i = 0; i < width; ++i) {
  181. kernel[i] *= scale;
  182. }
  183. }
  184. GrGaussianConvolutionFragmentProcessor::GrGaussianConvolutionFragmentProcessor(
  185. sk_sp<GrTextureProxy> proxy,
  186. Direction direction,
  187. int radius,
  188. float gaussianSigma,
  189. GrTextureDomain::Mode mode,
  190. int bounds[2])
  191. : INHERITED(kGrGaussianConvolutionFragmentProcessor_ClassID,
  192. ModulateForSamplerOptFlags(proxy->config(),
  193. mode == GrTextureDomain::kDecal_Mode))
  194. , fCoordTransform(proxy.get())
  195. , fTextureSampler(std::move(proxy))
  196. , fRadius(radius)
  197. , fDirection(direction)
  198. , fMode(mode) {
  199. // Make sure the sampler's ctor uses the clamp wrap mode
  200. SkASSERT(fTextureSampler.samplerState().wrapModeX() == GrSamplerState::WrapMode::kClamp &&
  201. fTextureSampler.samplerState().wrapModeY() == GrSamplerState::WrapMode::kClamp);
  202. this->addCoordTransform(&fCoordTransform);
  203. this->setTextureSamplerCnt(1);
  204. SkASSERT(radius <= kMaxKernelRadius);
  205. fill_in_1D_gaussian_kernel(fKernel, this->width(), gaussianSigma, this->radius());
  206. memcpy(fBounds, bounds, sizeof(fBounds));
  207. }
  208. GrGaussianConvolutionFragmentProcessor::GrGaussianConvolutionFragmentProcessor(
  209. const GrGaussianConvolutionFragmentProcessor& that)
  210. : INHERITED(kGrGaussianConvolutionFragmentProcessor_ClassID, that.optimizationFlags())
  211. , fCoordTransform(that.fCoordTransform)
  212. , fTextureSampler(that.fTextureSampler)
  213. , fRadius(that.fRadius)
  214. , fDirection(that.fDirection)
  215. , fMode(that.fMode) {
  216. this->addCoordTransform(&fCoordTransform);
  217. this->setTextureSamplerCnt(1);
  218. memcpy(fKernel, that.fKernel, that.width() * sizeof(float));
  219. memcpy(fBounds, that.fBounds, sizeof(fBounds));
  220. }
  221. void GrGaussianConvolutionFragmentProcessor::onGetGLSLProcessorKey(const GrShaderCaps& caps,
  222. GrProcessorKeyBuilder* b) const {
  223. GrGLConvolutionEffect::GenKey(*this, caps, b);
  224. }
  225. GrGLSLFragmentProcessor* GrGaussianConvolutionFragmentProcessor::onCreateGLSLInstance() const {
  226. return new GrGLConvolutionEffect;
  227. }
  228. bool GrGaussianConvolutionFragmentProcessor::onIsEqual(const GrFragmentProcessor& sBase) const {
  229. const GrGaussianConvolutionFragmentProcessor& s =
  230. sBase.cast<GrGaussianConvolutionFragmentProcessor>();
  231. return (this->radius() == s.radius() && this->direction() == s.direction() &&
  232. this->mode() == s.mode() &&
  233. 0 == memcmp(fBounds, s.fBounds, sizeof(fBounds)) &&
  234. 0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float)));
  235. }
  236. ///////////////////////////////////////////////////////////////////////////////
  237. GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrGaussianConvolutionFragmentProcessor);
  238. #if GR_TEST_UTILS
  239. std::unique_ptr<GrFragmentProcessor> GrGaussianConvolutionFragmentProcessor::TestCreate(
  240. GrProcessorTestData* d) {
  241. int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
  242. : GrProcessorUnitTest::kAlphaTextureIdx;
  243. sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
  244. int bounds[2];
  245. int modeIdx = d->fRandom->nextRangeU(0, GrTextureDomain::kModeCount-1);
  246. Direction dir;
  247. if (d->fRandom->nextBool()) {
  248. dir = Direction::kX;
  249. bounds[0] = d->fRandom->nextRangeU(0, proxy->width()-2);
  250. bounds[1] = d->fRandom->nextRangeU(bounds[0]+1, proxy->width()-1);
  251. } else {
  252. dir = Direction::kY;
  253. bounds[0] = d->fRandom->nextRangeU(0, proxy->height()-2);
  254. bounds[1] = d->fRandom->nextRangeU(bounds[0]+1, proxy->height()-1);
  255. }
  256. int radius = d->fRandom->nextRangeU(1, kMaxKernelRadius);
  257. float sigma = radius / 3.f;
  258. return GrGaussianConvolutionFragmentProcessor::Make(
  259. d->textureProxy(texIdx),
  260. dir, radius, sigma, static_cast<GrTextureDomain::Mode>(modeIdx), bounds);
  261. }
  262. #endif