From 9b80ffc77b8000b493eb1226dc6ec21f1e99edfc Mon Sep 17 00:00:00 2001 From: Ethan Nicholas Date: Fri, 7 Jul 2017 10:13:31 -0400 Subject: converted GrCircleBlurFragmentProcessor to sksl Bug: skia: Change-Id: I1b70ba2003c9e9de2b5b9acadaf25c9ed59b1198 Reviewed-on: https://skia-review.googlesource.com/21727 Commit-Queue: Ethan Nicholas Reviewed-by: Brian Salomon --- src/effects/GrAlphaThresholdFragmentProcessor.cpp | 5 +- src/effects/GrAlphaThresholdFragmentProcessor.fp | 11 - src/effects/GrAlphaThresholdFragmentProcessor.h | 7 +- src/effects/GrCircleBlurFragmentProcessor.cpp | 595 ++++++++++------------ src/effects/GrCircleBlurFragmentProcessor.fp | 289 +++++++++++ src/effects/GrCircleBlurFragmentProcessor.h | 83 ++- src/gpu/effects/GrDitherEffect.cpp | 2 + src/gpu/effects/GrDitherEffect.h | 3 + src/sksl/SkSLCPPCodeGenerator.cpp | 4 +- src/sksl/SkSLHCodeGenerator.cpp | 5 +- 10 files changed, 613 insertions(+), 391 deletions(-) create mode 100644 src/effects/GrCircleBlurFragmentProcessor.fp (limited to 'src') diff --git a/src/effects/GrAlphaThresholdFragmentProcessor.cpp b/src/effects/GrAlphaThresholdFragmentProcessor.cpp index 51869f3c46..dd26ac5ffd 100644 --- a/src/effects/GrAlphaThresholdFragmentProcessor.cpp +++ b/src/effects/GrAlphaThresholdFragmentProcessor.cpp @@ -9,8 +9,8 @@ * This file was autogenerated from GrAlphaThresholdFragmentProcessor.fp; do not modify. */ #include "GrAlphaThresholdFragmentProcessor.h" +#if SK_SUPPORT_GPU - #if SK_SUPPORT_GPU inline GrFragmentProcessor::OptimizationFlags GrAlphaThresholdFragmentProcessor::optFlags( float outerThreshold) { if (outerThreshold >= 1.0) { @@ -98,5 +98,4 @@ sk_sp GrAlphaThresholdFragmentProcessor::TestCreate(GrProce bounds); } #endif - - #endif +#endif diff --git a/src/effects/GrAlphaThresholdFragmentProcessor.fp b/src/effects/GrAlphaThresholdFragmentProcessor.fp index 1cc38c6d76..b576ecf086 100644 --- a/src/effects/GrAlphaThresholdFragmentProcessor.fp +++ b/src/effects/GrAlphaThresholdFragmentProcessor.fp @@ -45,17 +45,10 @@ in uniform float outerThreshold; } @header { - #include "SkTypes.h" - #if SK_SUPPORT_GPU #include "GrColorSpaceXform.h" } -@headerEnd { - #endif -} - @cpp { - #if SK_SUPPORT_GPU inline GrFragmentProcessor::OptimizationFlags GrAlphaThresholdFragmentProcessor::optFlags( float outerThreshold) { if (outerThreshold >= 1.0) { @@ -67,10 +60,6 @@ in uniform float outerThreshold; } } -@cppEnd { - #endif -} - void main() { vec4 color = texture(image, sk_TransformedCoords2D[0], colorXform); vec4 mask_color = texture(mask, sk_TransformedCoords2D[1]); diff --git a/src/effects/GrAlphaThresholdFragmentProcessor.h b/src/effects/GrAlphaThresholdFragmentProcessor.h index 80e4e0b718..702b6aeb88 100644 --- a/src/effects/GrAlphaThresholdFragmentProcessor.h +++ b/src/effects/GrAlphaThresholdFragmentProcessor.h @@ -10,9 +10,9 @@ */ #ifndef GrAlphaThresholdFragmentProcessor_DEFINED #define GrAlphaThresholdFragmentProcessor_DEFINED +#include "SkTypes.h" +#if SK_SUPPORT_GPU - #include "SkTypes.h" - #if SK_SUPPORT_GPU #include "GrColorSpaceXform.h" #include "GrFragmentProcessor.h" #include "GrCoordTransform.h" @@ -75,6 +75,5 @@ private: float fOuterThreshold; typedef GrFragmentProcessor INHERITED; }; - - #endif +#endif #endif diff --git a/src/effects/GrCircleBlurFragmentProcessor.cpp b/src/effects/GrCircleBlurFragmentProcessor.cpp index d99f0c78ba..b5cb961d5e 100644 --- a/src/effects/GrCircleBlurFragmentProcessor.cpp +++ b/src/effects/GrCircleBlurFragmentProcessor.cpp @@ -1,359 +1,316 @@ /* - * Copyright 2015 Google Inc. + * Copyright 2017 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ +/* + * This file was autogenerated from GrCircleBlurFragmentProcessor.fp; do not modify. + */ #include "GrCircleBlurFragmentProcessor.h" - #if SK_SUPPORT_GPU -#include "GrContext.h" -#include "GrResourceProvider.h" -#include "glsl/GrGLSLFragmentProcessor.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramDataManager.h" -#include "glsl/GrGLSLUniformHandler.h" - -#include "SkFixed.h" - -class GrCircleBlurFragmentProcessor::GLSLProcessor : public GrGLSLFragmentProcessor { -public: - void emitCode(EmitArgs&) override; - -protected: - void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; - -private: - GrGLSLProgramDataManager::UniformHandle fDataUniform; - - typedef GrGLSLFragmentProcessor INHERITED; -}; - -void GrCircleBlurFragmentProcessor::GLSLProcessor::emitCode(EmitArgs& args) { - const char *dataName; - - // The data is formatted as: - // x,y - the center of the circle - // z - inner radius that should map to 0th entry in the texture. - // w - the inverse of the distance over which the texture is stretched. - fDataUniform = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, - kVec4f_GrSLType, - kDefault_GrSLPrecision, - "data", - &dataName); - - GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; - - if (args.fInputColor) { - fragBuilder->codeAppendf("vec4 src=%s;", args.fInputColor); - } else { - fragBuilder->codeAppendf("vec4 src=vec4(1);"); - } - - // We just want to compute "(length(vec) - %s.z + 0.5) * %s.w" but need to rearrange - // for precision. - fragBuilder->codeAppendf("vec2 vec = vec2( (sk_FragCoord.x - %s.x) * %s.w, " - "(sk_FragCoord.y - %s.y) * %s.w );", - dataName, dataName, dataName, dataName); - fragBuilder->codeAppendf("float dist = length(vec) + (0.5 - %s.z) * %s.w;", - dataName, dataName); - - fragBuilder->codeAppendf("float intensity = "); - fragBuilder->appendTextureLookup(args.fTexSamplers[0], "vec2(dist, 0.5)"); - fragBuilder->codeAppend(".a;"); - - fragBuilder->codeAppendf("%s = src * intensity;\n", args.fOutputColor ); -} - -void GrCircleBlurFragmentProcessor::GLSLProcessor::onSetData(const GrGLSLProgramDataManager& pdman, - const GrFragmentProcessor& proc) { - const GrCircleBlurFragmentProcessor& cbfp = proc.cast(); - const SkRect& circle = cbfp.fCircle; - - // The data is formatted as: - // x,y - the center of the circle - // z - inner radius that should map to 0th entry in the texture. - // w - the inverse of the distance over which the profile texture is stretched. - pdman.set4f(fDataUniform, circle.centerX(), circle.centerY(), cbfp.fSolidRadius, - 1.f / cbfp.fTextureRadius); -} - -/////////////////////////////////////////////////////////////////////////////// - -GrCircleBlurFragmentProcessor::GrCircleBlurFragmentProcessor(const SkRect& circle, - float textureRadius, - float solidRadius, - sk_sp blurProfile) - : INHERITED(kCompatibleWithCoverageAsAlpha_OptimizationFlag) - , fCircle(circle) - , fSolidRadius(solidRadius) - , fTextureRadius(textureRadius) - , fBlurProfileSampler(std::move(blurProfile), GrSamplerParams::kBilerp_FilterMode) { - this->initClassID(); - this->addTextureSampler(&fBlurProfileSampler); -} - -GrGLSLFragmentProcessor* GrCircleBlurFragmentProcessor::onCreateGLSLInstance() const { - return new GLSLProcessor; -} - -void GrCircleBlurFragmentProcessor::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - // The code for this processor is always the same so there is nothing to add to the key. - return; -} - -// Computes an unnormalized half kernel (right side). Returns the summation of all the half kernel -// values. -static float make_unnormalized_half_kernel(float* halfKernel, int halfKernelSize, float sigma) { - const float invSigma = 1.f / sigma; - const float b = -0.5f * invSigma * invSigma; - float tot = 0.0f; - // Compute half kernel values at half pixel steps out from the center. - float t = 0.5f; - for (int i = 0; i < halfKernelSize; ++i) { - float value = expf(t * t * b); - tot += value; - halfKernel[i] = value; - t += 1.f; + #include "GrResourceProvider.h" + + + + static float make_unnormalized_half_kernel(float* halfKernel, int halfKernelSize, float sigma) { + const float invSigma = 1.f / sigma; + const float b = -0.5f * invSigma * invSigma; + float tot = 0.0f; + + float t = 0.5f; + for (int i = 0; i < halfKernelSize; ++i) { + float value = expf(t * t * b); + tot += value; + halfKernel[i] = value; + t += 1.f; + } + return tot; } - return tot; -} -// Create a Gaussian half-kernel (right side) and a summed area table given a sigma and number of -// discrete steps. The half kernel is normalized to sum to 0.5. -static void make_half_kernel_and_summed_table(float* halfKernel, float* summedHalfKernel, - int halfKernelSize, float sigma) { - // The half kernel should sum to 0.5 not 1.0. - const float tot = 2.f * make_unnormalized_half_kernel(halfKernel, halfKernelSize, sigma); - float sum = 0.f; - for (int i = 0; i < halfKernelSize; ++i) { - halfKernel[i] /= tot; - sum += halfKernel[i]; - summedHalfKernel[i] = sum; + + + static void make_half_kernel_and_summed_table(float* halfKernel, float* summedHalfKernel, + int halfKernelSize, float sigma) { + + const float tot = 2.f * make_unnormalized_half_kernel(halfKernel, halfKernelSize, sigma); + float sum = 0.f; + for (int i = 0; i < halfKernelSize; ++i) { + halfKernel[i] /= tot; + sum += halfKernel[i]; + summedHalfKernel[i] = sum; + } } -} -// Applies the 1D half kernel vertically at points along the x axis to a circle centered at the -// origin with radius circleR. -void apply_kernel_in_y(float* results, int numSteps, float firstX, float circleR, - int halfKernelSize, const float* summedHalfKernelTable) { - float x = firstX; - for (int i = 0; i < numSteps; ++i, x += 1.f) { - if (x < -circleR || x > circleR) { - results[i] = 0; - continue; - } - float y = sqrtf(circleR * circleR - x * x); - // In the column at x we exit the circle at +y and -y - // The summed table entry j is actually reflects an offset of j + 0.5. - y -= 0.5f; - int yInt = SkScalarFloorToInt(y); - SkASSERT(yInt >= -1); - if (y < 0) { - results[i] = (y + 0.5f) * summedHalfKernelTable[0]; - } else if (yInt >= halfKernelSize - 1) { - results[i] = 0.5f; - } else { - float yFrac = y - yInt; - results[i] = (1.f - yFrac) * summedHalfKernelTable[yInt] + - yFrac * summedHalfKernelTable[yInt + 1]; + + + void apply_kernel_in_y(float* results, int numSteps, float firstX, float circleR, + int halfKernelSize, const float* summedHalfKernelTable) { + float x = firstX; + for (int i = 0; i < numSteps; ++i, x += 1.f) { + if (x < -circleR || x > circleR) { + results[i] = 0; + continue; + } + float y = sqrtf(circleR * circleR - x * x); + + + y -= 0.5f; + int yInt = SkScalarFloorToInt(y); + SkASSERT(yInt >= -1); + if (y < 0) { + results[i] = (y + 0.5f) * summedHalfKernelTable[0]; + } else if (yInt >= halfKernelSize - 1) { + results[i] = 0.5f; + } else { + float yFrac = y - yInt; + results[i] = (1.f - yFrac) * summedHalfKernelTable[yInt] + + yFrac * summedHalfKernelTable[yInt + 1]; + } } } -} - -// Apply a Gaussian at point (evalX, 0) to a circle centered at the origin with radius circleR. -// This relies on having a half kernel computed for the Gaussian and a table of applications of -// the half kernel in y to columns at (evalX - halfKernel, evalX - halfKernel + 1, ..., evalX + -// halfKernel) passed in as yKernelEvaluations. -static uint8_t eval_at(float evalX, float circleR, const float* halfKernel, int halfKernelSize, - const float* yKernelEvaluations) { - float acc = 0; - float x = evalX - halfKernelSize; - for (int i = 0; i < halfKernelSize; ++i, x += 1.f) { - if (x < -circleR || x > circleR) { - continue; + + + + + static uint8_t eval_at(float evalX, float circleR, const float* halfKernel, int halfKernelSize, + const float* yKernelEvaluations) { + float acc = 0; + + float x = evalX - halfKernelSize; + for (int i = 0; i < halfKernelSize; ++i, x += 1.f) { + if (x < -circleR || x > circleR) { + continue; + } + float verticalEval = yKernelEvaluations[i]; + acc += verticalEval * halfKernel[halfKernelSize - i - 1]; } - float verticalEval = yKernelEvaluations[i]; - acc += verticalEval * halfKernel[halfKernelSize - i - 1]; - } - for (int i = 0; i < halfKernelSize; ++i, x += 1.f) { - if (x < -circleR || x > circleR) { - continue; + for (int i = 0; i < halfKernelSize; ++i, x += 1.f) { + if (x < -circleR || x > circleR) { + continue; + } + float verticalEval = yKernelEvaluations[i + halfKernelSize]; + acc += verticalEval * halfKernel[i]; } - float verticalEval = yKernelEvaluations[i + halfKernelSize]; - acc += verticalEval * halfKernel[i]; + + + return SkUnitScalarClampToByte(2.f * acc); } - // Since we applied a half kernel in y we multiply acc by 2 (the circle is symmetric about the - // x axis). - return SkUnitScalarClampToByte(2.f * acc); -} - -// This function creates a profile of a blurred circle. It does this by computing a kernel for -// half the Gaussian and a matching summed area table. The summed area table is used to compute -// an array of vertical applications of the half kernel to the circle along the x axis. The table -// of y evaluations has 2 * k + n entries where k is the size of the half kernel and n is the size -// of the profile being computed. Then for each of the n profile entries we walk out k steps in each -// horizontal direction multiplying the corresponding y evaluation by the half kernel entry and -// sum these values to compute the profile entry. -static uint8_t* create_circle_profile(float sigma, float circleR, int profileTextureWidth) { - const int numSteps = profileTextureWidth; - uint8_t* weights = new uint8_t[numSteps]; - - // The full kernel is 6 sigmas wide. - int halfKernelSize = SkScalarCeilToInt(6.0f*sigma); - // round up to next multiple of 2 and then divide by 2 - halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1; - - // Number of x steps at which to apply kernel in y to cover all the profile samples in x. - int numYSteps = numSteps + 2 * halfKernelSize; - - SkAutoTArray bulkAlloc(halfKernelSize + halfKernelSize + numYSteps); - float* halfKernel = bulkAlloc.get(); - float* summedKernel = bulkAlloc.get() + halfKernelSize; - float* yEvals = bulkAlloc.get() + 2 * halfKernelSize; - make_half_kernel_and_summed_table(halfKernel, summedKernel, halfKernelSize, sigma); - - float firstX = -halfKernelSize + 0.5f; - apply_kernel_in_y(yEvals, numYSteps, firstX, circleR, halfKernelSize, summedKernel); - - for (int i = 0; i < numSteps - 1; ++i) { - float evalX = i + 0.5f; - weights[i] = eval_at(evalX, circleR, halfKernel, halfKernelSize, yEvals + i); - } - // Ensure the tail of the Gaussian goes to zero. - weights[numSteps - 1] = 0; - return weights; -} - -static uint8_t* create_half_plane_profile(int profileWidth) { - SkASSERT(!(profileWidth & 0x1)); - // The full kernel is 6 sigmas wide. - float sigma = profileWidth / 6.f; - int halfKernelSize = profileWidth / 2; - SkAutoTArray halfKernel(halfKernelSize); - uint8_t* profile = new uint8_t[profileWidth]; - - // The half kernel should sum to 0.5. - const float tot = 2.f * make_unnormalized_half_kernel(halfKernel.get(), halfKernelSize, sigma); - float sum = 0.f; - // Populate the profile from the right edge to the middle. - for (int i = 0; i < halfKernelSize; ++i) { - halfKernel[halfKernelSize - i - 1] /= tot; - sum += halfKernel[halfKernelSize - i - 1]; - profile[profileWidth - i - 1] = SkUnitScalarClampToByte(sum); - } - // Populate the profile from the middle to the left edge (by flipping the half kernel and - // continuing the summation). - for (int i = 0; i < halfKernelSize; ++i) { - sum += halfKernel[i]; - profile[halfKernelSize - i - 1] = SkUnitScalarClampToByte(sum); + + + + + + + + static uint8_t* create_circle_profile(float sigma, float circleR, int profileTextureWidth) { + const int numSteps = profileTextureWidth; + uint8_t* weights = new uint8_t[numSteps]; + + + int halfKernelSize = SkScalarCeilToInt(6.0f*sigma); + + halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1; + + + int numYSteps = numSteps + 2 * halfKernelSize; + + SkAutoTArray bulkAlloc(halfKernelSize + halfKernelSize + numYSteps); + float* halfKernel = bulkAlloc.get(); + float* summedKernel = bulkAlloc.get() + halfKernelSize; + float* yEvals = bulkAlloc.get() + 2 * halfKernelSize; + make_half_kernel_and_summed_table(halfKernel, summedKernel, halfKernelSize, sigma); + + float firstX = -halfKernelSize + 0.5f; + apply_kernel_in_y(yEvals, numYSteps, firstX, circleR, halfKernelSize, summedKernel); + + for (int i = 0; i < numSteps - 1; ++i) { + float evalX = i + 0.5f; + weights[i] = eval_at(evalX, circleR, halfKernel, halfKernelSize, yEvals + i); + } + + weights[numSteps - 1] = 0; + return weights; } - // Ensure tail goes to 0. - profile[profileWidth - 1] = 0; - return profile; -} -static sk_sp create_profile_texture(GrResourceProvider* resourceProvider, - const SkRect& circle, - float sigma, - float* solidRadius, float* textureRadius) { - float circleR = circle.width() / 2.0f; - // Profile textures are cached by the ratio of sigma to circle radius and by the size of the - // profile texture (binned by powers of 2). - SkScalar sigmaToCircleRRatio = sigma / circleR; - // When sigma is really small this becomes a equivalent to convolving a Gaussian with a half- - // plane. Similarly, in the extreme high ratio cases circle becomes a point WRT to the Guassian - // and the profile texture is a just a Gaussian evaluation. However, we haven't yet implemented - // this latter optimization. - sigmaToCircleRRatio = SkTMin(sigmaToCircleRRatio, 8.f); - SkFixed sigmaToCircleRRatioFixed; - static const SkScalar kHalfPlaneThreshold = 0.1f; - bool useHalfPlaneApprox = false; - if (sigmaToCircleRRatio <= kHalfPlaneThreshold) { - useHalfPlaneApprox = true; - sigmaToCircleRRatioFixed = 0; - *solidRadius = circleR - 3 * sigma; - *textureRadius = 6 * sigma; - } else { - // Convert to fixed point for the key. - sigmaToCircleRRatioFixed = SkScalarToFixed(sigmaToCircleRRatio); - // We shave off some bits to reduce the number of unique entries. We could probably shave - // off more than we do. - sigmaToCircleRRatioFixed &= ~0xff; - sigmaToCircleRRatio = SkFixedToScalar(sigmaToCircleRRatioFixed); - sigma = circleR * sigmaToCircleRRatio; - *solidRadius = 0; - *textureRadius = circleR + 3 * sigma; + static uint8_t* create_half_plane_profile(int profileWidth) { + SkASSERT(!(profileWidth & 0x1)); + + float sigma = profileWidth / 6.f; + int halfKernelSize = profileWidth / 2; + + SkAutoTArray halfKernel(halfKernelSize); + uint8_t* profile = new uint8_t[profileWidth]; + + + const float tot = 2.f * make_unnormalized_half_kernel(halfKernel.get(), halfKernelSize, + sigma); + float sum = 0.f; + + for (int i = 0; i < halfKernelSize; ++i) { + halfKernel[halfKernelSize - i - 1] /= tot; + sum += halfKernel[halfKernelSize - i - 1]; + profile[profileWidth - i - 1] = SkUnitScalarClampToByte(sum); + } + + + for (int i = 0; i < halfKernelSize; ++i) { + sum += halfKernel[i]; + profile[halfKernelSize - i - 1] = SkUnitScalarClampToByte(sum); + } + + profile[profileWidth - 1] = 0; + return profile; } - static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); - GrUniqueKey key; - GrUniqueKey::Builder builder(&key, kDomain, 1); - builder[0] = sigmaToCircleRRatioFixed; - builder.finish(); - - sk_sp blurProfile = resourceProvider->findProxyByUniqueKey(key); - if (!blurProfile) { - static constexpr int kProfileTextureWidth = 512; - GrSurfaceDesc texDesc; - texDesc.fWidth = kProfileTextureWidth; - texDesc.fHeight = 1; - texDesc.fConfig = kAlpha_8_GrPixelConfig; - - std::unique_ptr profile(nullptr); - if (useHalfPlaneApprox) { - profile.reset(create_half_plane_profile(kProfileTextureWidth)); + static sk_sp create_profile_texture(GrResourceProvider* resourceProvider, + const SkRect& circle, + float sigma, + float* solidRadius, float* textureRadius) { + float circleR = circle.width() / 2.0f; + + + SkScalar sigmaToCircleRRatio = sigma / circleR; + + + + + sigmaToCircleRRatio = SkTMin(sigmaToCircleRRatio, 8.f); + SkFixed sigmaToCircleRRatioFixed; + static const SkScalar kHalfPlaneThreshold = 0.1f; + bool useHalfPlaneApprox = false; + if (sigmaToCircleRRatio <= kHalfPlaneThreshold) { + useHalfPlaneApprox = true; + sigmaToCircleRRatioFixed = 0; + *solidRadius = circleR - 3 * sigma; + *textureRadius = 6 * sigma; } else { - // Rescale params to the size of the texture we're creating. - SkScalar scale = kProfileTextureWidth / *textureRadius; - profile.reset(create_circle_profile(sigma * scale, circleR * scale, - kProfileTextureWidth)); + + sigmaToCircleRRatioFixed = SkScalarToFixed(sigmaToCircleRRatio); + + + sigmaToCircleRRatioFixed &= ~0xff; + sigmaToCircleRRatio = SkFixedToScalar(sigmaToCircleRRatioFixed); + sigma = circleR * sigmaToCircleRRatio; + *solidRadius = 0; + *textureRadius = circleR + 3 * sigma; } - blurProfile = GrSurfaceProxy::MakeDeferred(resourceProvider, - texDesc, SkBudgeted::kYes, profile.get(), 0); + static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); + GrUniqueKey key; + GrUniqueKey::Builder builder(&key, kDomain, 1); + builder[0] = sigmaToCircleRRatioFixed; + builder.finish(); + + sk_sp blurProfile = resourceProvider->findProxyByUniqueKey(key); if (!blurProfile) { - return nullptr; + static constexpr int kProfileTextureWidth = 512; + GrSurfaceDesc texDesc; + texDesc.fWidth = kProfileTextureWidth; + texDesc.fHeight = 1; + texDesc.fConfig = kAlpha_8_GrPixelConfig; + + std::unique_ptr profile(nullptr); + if (useHalfPlaneApprox) { + profile.reset(create_half_plane_profile(kProfileTextureWidth)); + } else { + + SkScalar scale = kProfileTextureWidth / *textureRadius; + profile.reset(create_circle_profile(sigma * scale, circleR * scale, + kProfileTextureWidth)); + } + + blurProfile = GrSurfaceProxy::MakeDeferred(resourceProvider, + texDesc, SkBudgeted::kYes, profile.get(), 0); + if (!blurProfile) { + return nullptr; + } + + resourceProvider->assignUniqueKeyToProxy(key, blurProfile.get()); } - resourceProvider->assignUniqueKeyToProxy(key, blurProfile.get()); + return blurProfile; } - return blurProfile; -} - -////////////////////////////////////////////////////////////////////////////// - -sk_sp GrCircleBlurFragmentProcessor::Make(GrResourceProvider* resourceProvider, - const SkRect& circle, float sigma) { - float solidRadius; - float textureRadius; - sk_sp profile(create_profile_texture(resourceProvider, circle, sigma, - &solidRadius, &textureRadius)); - if (!profile) { - return nullptr; + sk_sp GrCircleBlurFragmentProcessor::Make( + GrResourceProvider* resourceProvider, + const SkRect& circle, + float sigma) { + float solidRadius; + float textureRadius; + sk_sp profile(create_profile_texture(resourceProvider, circle, sigma, + &solidRadius, &textureRadius)); + if (!profile) { + return nullptr; + } + return sk_sp(new GrCircleBlurFragmentProcessor(circle, + textureRadius, + solidRadius, + std::move(profile), + resourceProvider)); + } +#include "glsl/GrGLSLColorSpaceXformHelper.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "glsl/GrGLSLProgramBuilder.h" +#include "SkSLCPP.h" +#include "SkSLUtil.h" +class GrGLSLCircleBlurFragmentProcessor : public GrGLSLFragmentProcessor { +public: + GrGLSLCircleBlurFragmentProcessor() {} + void emitCode(EmitArgs& args) override { + GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; + const GrCircleBlurFragmentProcessor& _outer = args.fFp.cast(); + (void) _outer; + fCircleDataVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kVec4f_GrSLType, kDefault_GrSLPrecision, "circleData"); + fragBuilder->codeAppendf("vec2 vec = vec2((sk_FragCoord.x - %s.x) * %s.w, (sk_FragCoord.y - %s.y) * %s.w);\nfloat dist = length(vec) + (0.5 - %s.z) * %s.w;\n%s = %s * texture(%s, vec2(dist, 0.5));\n", args.fUniformHandler->getUniformCStr(fCircleDataVar), args.fUniformHandler->getUniformCStr(fCircleDataVar), args.fUniformHandler->getUniformCStr(fCircleDataVar), args.fUniformHandler->getUniformCStr(fCircleDataVar), args.fUniformHandler->getUniformCStr(fCircleDataVar), args.fUniformHandler->getUniformCStr(fCircleDataVar), args.fOutputColor, args.fInputColor ? args.fInputColor : "vec4(1)", fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str()); + } +private: + void onSetData(const GrGLSLProgramDataManager& data, const GrFragmentProcessor& _proc) override { + const GrCircleBlurFragmentProcessor& _outer = _proc.cast(); + auto circleRect = _outer.circleRect(); + (void) circleRect; + auto textureRadius = _outer.textureRadius(); + (void) textureRadius; + auto solidRadius = _outer.solidRadius(); + (void) solidRadius; + UniformHandle& blurProfileSampler = fBlurProfileSamplerVar; + (void) blurProfileSampler; + UniformHandle& circleData = fCircleDataVar; + (void) circleData; + + data.set4f(circleData, circleRect.centerX(), circleRect.centerY(), solidRadius, + 1.f / textureRadius); } - return sk_sp(new GrCircleBlurFragmentProcessor(circle, - textureRadius, solidRadius, - std::move(profile))); + UniformHandle fCircleDataVar; + UniformHandle fBlurProfileSamplerVar; +}; +GrGLSLFragmentProcessor* GrCircleBlurFragmentProcessor::onCreateGLSLInstance() const { + return new GrGLSLCircleBlurFragmentProcessor(); +} +void GrCircleBlurFragmentProcessor::onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const { +} +bool GrCircleBlurFragmentProcessor::onIsEqual(const GrFragmentProcessor& other) const { + const GrCircleBlurFragmentProcessor& that = other.cast(); + (void) that; + if (fCircleRect != that.fCircleRect) return false; + if (fTextureRadius != that.fTextureRadius) return false; + if (fSolidRadius != that.fSolidRadius) return false; + if (fBlurProfileSampler != that.fBlurProfileSampler) return false; + return true; } - -////////////////////////////////////////////////////////////////////////////// - GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor); - #if GR_TEST_UTILS -sk_sp GrCircleBlurFragmentProcessor::TestCreate(GrProcessorTestData* d) { - SkScalar wh = d->fRandom->nextRangeScalar(100.f, 1000.f); - SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f); +sk_sp GrCircleBlurFragmentProcessor::TestCreate(GrProcessorTestData* testData) { + + SkScalar wh = testData->fRandom->nextRangeScalar(100.f, 1000.f); + SkScalar sigma = testData->fRandom->nextRangeF(1.f,10.f); SkRect circle = SkRect::MakeWH(wh, wh); - return GrCircleBlurFragmentProcessor::Make(d->resourceProvider(), circle, sigma); + return GrCircleBlurFragmentProcessor::Make(testData->resourceProvider(), circle, sigma); } #endif - #endif diff --git a/src/effects/GrCircleBlurFragmentProcessor.fp b/src/effects/GrCircleBlurFragmentProcessor.fp new file mode 100644 index 0000000000..7682ad186e --- /dev/null +++ b/src/effects/GrCircleBlurFragmentProcessor.fp @@ -0,0 +1,289 @@ +in vec4 circleRect; +in float textureRadius; +in float solidRadius; +in uniform sampler2D blurProfileSampler; + +// The data is formatted as: +// x, y - the center of the circle +// z - inner radius that should map to 0th entry in the texture. +// w - the inverse of the distance over which the texture is stretched. +uniform vec4 circleData; + +@optimizationFlags { + kCompatibleWithCoverageAsAlpha_OptimizationFlag +} + +@constructorParams { + GrResourceProvider* resourceProvider +} + +@make { + static sk_sp Make(GrResourceProvider* resourceProvider, + const SkRect& circle, float sigma); +} + +@setData(data) { + data.set4f(circleData, circleRect.centerX(), circleRect.centerY(), solidRadius, + 1.f / textureRadius); +} + +@cpp { + #include "GrResourceProvider.h" + + // Computes an unnormalized half kernel (right side). Returns the summation of all the half + // kernel values. + static float make_unnormalized_half_kernel(float* halfKernel, int halfKernelSize, float sigma) { + const float invSigma = 1.f / sigma; + const float b = -0.5f * invSigma * invSigma; + float tot = 0.0f; + // Compute half kernel values at half pixel steps out from the center. + float t = 0.5f; + for (int i = 0; i < halfKernelSize; ++i) { + float value = expf(t * t * b); + tot += value; + halfKernel[i] = value; + t += 1.f; + } + return tot; + } + + // Create a Gaussian half-kernel (right side) and a summed area table given a sigma and number + // of discrete steps. The half kernel is normalized to sum to 0.5. + static void make_half_kernel_and_summed_table(float* halfKernel, float* summedHalfKernel, + int halfKernelSize, float sigma) { + // The half kernel should sum to 0.5 not 1.0. + const float tot = 2.f * make_unnormalized_half_kernel(halfKernel, halfKernelSize, sigma); + float sum = 0.f; + for (int i = 0; i < halfKernelSize; ++i) { + halfKernel[i] /= tot; + sum += halfKernel[i]; + summedHalfKernel[i] = sum; + } + } + + // Applies the 1D half kernel vertically at points along the x axis to a circle centered at the + // origin with radius circleR. + void apply_kernel_in_y(float* results, int numSteps, float firstX, float circleR, + int halfKernelSize, const float* summedHalfKernelTable) { + float x = firstX; + for (int i = 0; i < numSteps; ++i, x += 1.f) { + if (x < -circleR || x > circleR) { + results[i] = 0; + continue; + } + float y = sqrtf(circleR * circleR - x * x); + // In the column at x we exit the circle at +y and -y + // The summed table entry j is actually reflects an offset of j + 0.5. + y -= 0.5f; + int yInt = SkScalarFloorToInt(y); + SkASSERT(yInt >= -1); + if (y < 0) { + results[i] = (y + 0.5f) * summedHalfKernelTable[0]; + } else if (yInt >= halfKernelSize - 1) { + results[i] = 0.5f; + } else { + float yFrac = y - yInt; + results[i] = (1.f - yFrac) * summedHalfKernelTable[yInt] + + yFrac * summedHalfKernelTable[yInt + 1]; + } + } + } + + // Apply a Gaussian at point (evalX, 0) to a circle centered at the origin with radius circleR. + // This relies on having a half kernel computed for the Gaussian and a table of applications of + // the half kernel in y to columns at (evalX - halfKernel, evalX - halfKernel + 1, ..., evalX + + // halfKernel) passed in as yKernelEvaluations. + static uint8_t eval_at(float evalX, float circleR, const float* halfKernel, int halfKernelSize, + const float* yKernelEvaluations) { + float acc = 0; + + float x = evalX - halfKernelSize; + for (int i = 0; i < halfKernelSize; ++i, x += 1.f) { + if (x < -circleR || x > circleR) { + continue; + } + float verticalEval = yKernelEvaluations[i]; + acc += verticalEval * halfKernel[halfKernelSize - i - 1]; + } + for (int i = 0; i < halfKernelSize; ++i, x += 1.f) { + if (x < -circleR || x > circleR) { + continue; + } + float verticalEval = yKernelEvaluations[i + halfKernelSize]; + acc += verticalEval * halfKernel[i]; + } + // Since we applied a half kernel in y we multiply acc by 2 (the circle is symmetric about + // the x axis). + return SkUnitScalarClampToByte(2.f * acc); + } + + // This function creates a profile of a blurred circle. It does this by computing a kernel for + // half the Gaussian and a matching summed area table. The summed area table is used to compute + // an array of vertical applications of the half kernel to the circle along the x axis. The + // table of y evaluations has 2 * k + n entries where k is the size of the half kernel and n is + // the size of the profile being computed. Then for each of the n profile entries we walk out k + // steps in each horizontal direction multiplying the corresponding y evaluation by the half + // kernel entry and sum these values to compute the profile entry. + static uint8_t* create_circle_profile(float sigma, float circleR, int profileTextureWidth) { + const int numSteps = profileTextureWidth; + uint8_t* weights = new uint8_t[numSteps]; + + // The full kernel is 6 sigmas wide. + int halfKernelSize = SkScalarCeilToInt(6.0f*sigma); + // round up to next multiple of 2 and then divide by 2 + halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1; + + // Number of x steps at which to apply kernel in y to cover all the profile samples in x. + int numYSteps = numSteps + 2 * halfKernelSize; + + SkAutoTArray bulkAlloc(halfKernelSize + halfKernelSize + numYSteps); + float* halfKernel = bulkAlloc.get(); + float* summedKernel = bulkAlloc.get() + halfKernelSize; + float* yEvals = bulkAlloc.get() + 2 * halfKernelSize; + make_half_kernel_and_summed_table(halfKernel, summedKernel, halfKernelSize, sigma); + + float firstX = -halfKernelSize + 0.5f; + apply_kernel_in_y(yEvals, numYSteps, firstX, circleR, halfKernelSize, summedKernel); + + for (int i = 0; i < numSteps - 1; ++i) { + float evalX = i + 0.5f; + weights[i] = eval_at(evalX, circleR, halfKernel, halfKernelSize, yEvals + i); + } + // Ensure the tail of the Gaussian goes to zero. + weights[numSteps - 1] = 0; + return weights; + } + + static uint8_t* create_half_plane_profile(int profileWidth) { + SkASSERT(!(profileWidth & 0x1)); + // The full kernel is 6 sigmas wide. + float sigma = profileWidth / 6.f; + int halfKernelSize = profileWidth / 2; + + SkAutoTArray halfKernel(halfKernelSize); + uint8_t* profile = new uint8_t[profileWidth]; + + // The half kernel should sum to 0.5. + const float tot = 2.f * make_unnormalized_half_kernel(halfKernel.get(), halfKernelSize, + sigma); + float sum = 0.f; + // Populate the profile from the right edge to the middle. + for (int i = 0; i < halfKernelSize; ++i) { + halfKernel[halfKernelSize - i - 1] /= tot; + sum += halfKernel[halfKernelSize - i - 1]; + profile[profileWidth - i - 1] = SkUnitScalarClampToByte(sum); + } + // Populate the profile from the middle to the left edge (by flipping the half kernel and + // continuing the summation). + for (int i = 0; i < halfKernelSize; ++i) { + sum += halfKernel[i]; + profile[halfKernelSize - i - 1] = SkUnitScalarClampToByte(sum); + } + // Ensure tail goes to 0. + profile[profileWidth - 1] = 0; + return profile; + } + + static sk_sp create_profile_texture(GrResourceProvider* resourceProvider, + const SkRect& circle, + float sigma, + float* solidRadius, float* textureRadius) { + float circleR = circle.width() / 2.0f; + // Profile textures are cached by the ratio of sigma to circle radius and by the size of the + // profile texture (binned by powers of 2). + SkScalar sigmaToCircleRRatio = sigma / circleR; + // When sigma is really small this becomes a equivalent to convolving a Gaussian with a + // half-plane. Similarly, in the extreme high ratio cases circle becomes a point WRT to the + // Guassian and the profile texture is a just a Gaussian evaluation. However, we haven't yet + // implemented this latter optimization. + sigmaToCircleRRatio = SkTMin(sigmaToCircleRRatio, 8.f); + SkFixed sigmaToCircleRRatioFixed; + static const SkScalar kHalfPlaneThreshold = 0.1f; + bool useHalfPlaneApprox = false; + if (sigmaToCircleRRatio <= kHalfPlaneThreshold) { + useHalfPlaneApprox = true; + sigmaToCircleRRatioFixed = 0; + *solidRadius = circleR - 3 * sigma; + *textureRadius = 6 * sigma; + } else { + // Convert to fixed point for the key. + sigmaToCircleRRatioFixed = SkScalarToFixed(sigmaToCircleRRatio); + // We shave off some bits to reduce the number of unique entries. We could probably + // shave off more than we do. + sigmaToCircleRRatioFixed &= ~0xff; + sigmaToCircleRRatio = SkFixedToScalar(sigmaToCircleRRatioFixed); + sigma = circleR * sigmaToCircleRRatio; + *solidRadius = 0; + *textureRadius = circleR + 3 * sigma; + } + + static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); + GrUniqueKey key; + GrUniqueKey::Builder builder(&key, kDomain, 1); + builder[0] = sigmaToCircleRRatioFixed; + builder.finish(); + + sk_sp blurProfile = resourceProvider->findProxyByUniqueKey(key); + if (!blurProfile) { + static constexpr int kProfileTextureWidth = 512; + GrSurfaceDesc texDesc; + texDesc.fWidth = kProfileTextureWidth; + texDesc.fHeight = 1; + texDesc.fConfig = kAlpha_8_GrPixelConfig; + + std::unique_ptr profile(nullptr); + if (useHalfPlaneApprox) { + profile.reset(create_half_plane_profile(kProfileTextureWidth)); + } else { + // Rescale params to the size of the texture we're creating. + SkScalar scale = kProfileTextureWidth / *textureRadius; + profile.reset(create_circle_profile(sigma * scale, circleR * scale, + kProfileTextureWidth)); + } + + blurProfile = GrSurfaceProxy::MakeDeferred(resourceProvider, + texDesc, SkBudgeted::kYes, profile.get(), 0); + if (!blurProfile) { + return nullptr; + } + + resourceProvider->assignUniqueKeyToProxy(key, blurProfile.get()); + } + + return blurProfile; + } + + sk_sp GrCircleBlurFragmentProcessor::Make( + GrResourceProvider* resourceProvider, + const SkRect& circle, + float sigma) { + float solidRadius; + float textureRadius; + sk_sp profile(create_profile_texture(resourceProvider, circle, sigma, + &solidRadius, &textureRadius)); + if (!profile) { + return nullptr; + } + return sk_sp(new GrCircleBlurFragmentProcessor(circle, + textureRadius, + solidRadius, + std::move(profile), + resourceProvider)); + } +} + +void main() { + // We just want to compute "(length(vec) - circleData.z + 0.5) * circleData.w" but need to + // rearrange for precision. + vec2 vec = vec2((sk_FragCoord.x - circleData.x) * circleData.w, + (sk_FragCoord.y - circleData.y) * circleData.w); + float dist = length(vec) + (0.5 - circleData.z) * circleData.w; + sk_OutColor = sk_InColor * texture(blurProfileSampler, vec2(dist, 0.5)); +} + +@test(testData) { + SkScalar wh = testData->fRandom->nextRangeScalar(100.f, 1000.f); + SkScalar sigma = testData->fRandom->nextRangeF(1.f,10.f); + SkRect circle = SkRect::MakeWH(wh, wh); + return GrCircleBlurFragmentProcessor::Make(testData->resourceProvider(), circle, sigma); +} \ No newline at end of file diff --git a/src/effects/GrCircleBlurFragmentProcessor.h b/src/effects/GrCircleBlurFragmentProcessor.h index 3ed4cfc094..c01ea9b11d 100644 --- a/src/effects/GrCircleBlurFragmentProcessor.h +++ b/src/effects/GrCircleBlurFragmentProcessor.h @@ -1,73 +1,52 @@ /* - * Copyright 2015 Google Inc. + * Copyright 2017 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ +/* + * This file was autogenerated from GrCircleBlurFragmentProcessor.fp; do not modify. + */ #ifndef GrCircleBlurFragmentProcessor_DEFINED #define GrCircleBlurFragmentProcessor_DEFINED - -#include "SkString.h" #include "SkTypes.h" - #if SK_SUPPORT_GPU - #include "GrFragmentProcessor.h" -#include "GrProcessorUnitTest.h" - -class GrResourceProvider; - -// This FP handles the special case of a blurred circle. It uses a 1D -// profile that is just rotated about the origin of the circle. +#include "GrCoordTransform.h" +#include "effects/GrProxyMove.h" class GrCircleBlurFragmentProcessor : public GrFragmentProcessor { public: - static sk_sp Make(GrResourceProvider*, const SkRect& circle, float sigma); - - ~GrCircleBlurFragmentProcessor() override {} - - const char* name() const override { return "CircleBlur"; } - - SkString dumpInfo() const override { - SkString str; - str.appendf("Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], solidR: %.2f, textureR: %.2f", - fCircle.fLeft, fCircle.fTop, fCircle.fRight, fCircle.fBottom, - fSolidRadius, fTextureRadius); - return str; - } + SkRect circleRect() const { return fCircleRect; } + float textureRadius() const { return fTextureRadius; } + float solidRadius() const { return fSolidRadius; } + static sk_sp Make(GrResourceProvider* resourceProvider, + const SkRect& circle, float sigma); + const char* name() const override { return "CircleBlurFragmentProcessor"; } private: - // This nested GLSL processor implementation is defined in the cpp file. - class GLSLProcessor; - - /** - * Creates a profile texture for the circle and sigma. The texture will have a height of 1. - * The x texture coord should map from 0 to 1 across the radius range of solidRadius to - * solidRadius + textureRadius. - */ - GrCircleBlurFragmentProcessor(const SkRect& circle, - float textureRadius, float innerRadius, - sk_sp blurProfile); - - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - - void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override; - - bool onIsEqual(const GrFragmentProcessor& other) const override { - const GrCircleBlurFragmentProcessor& cbfp = other.cast(); - return fCircle == cbfp.fCircle && fSolidRadius == cbfp.fSolidRadius && - fTextureRadius == cbfp.fTextureRadius; + GrCircleBlurFragmentProcessor(SkRect circleRect, float textureRadius, float solidRadius, sk_sp blurProfileSampler, + GrResourceProvider* resourceProvider +) + : INHERITED((OptimizationFlags) + kCompatibleWithCoverageAsAlpha_OptimizationFlag +) + , fCircleRect(circleRect) + , fTextureRadius(textureRadius) + , fSolidRadius(solidRadius) + , fBlurProfileSampler(std::move(blurProfileSampler)) { + this->addTextureSampler(&fBlurProfileSampler); + this->initClassID(); } - - SkRect fCircle; - SkScalar fSolidRadius; - float fTextureRadius; - TextureSampler fBlurProfileSampler; - + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; + void onGetGLSLProcessorKey(const GrShaderCaps&,GrProcessorKeyBuilder*) const override; + bool onIsEqual(const GrFragmentProcessor&) const override; GR_DECLARE_FRAGMENT_PROCESSOR_TEST - + SkRect fCircleRect; + float fTextureRadius; + float fSolidRadius; + TextureSampler fBlurProfileSampler; typedef GrFragmentProcessor INHERITED; }; - #endif #endif diff --git a/src/gpu/effects/GrDitherEffect.cpp b/src/gpu/effects/GrDitherEffect.cpp index 8c24d78843..370e0eedd1 100644 --- a/src/gpu/effects/GrDitherEffect.cpp +++ b/src/gpu/effects/GrDitherEffect.cpp @@ -9,6 +9,7 @@ * This file was autogenerated from GrDitherEffect.fp; do not modify. */ #include "GrDitherEffect.h" +#if SK_SUPPORT_GPU #include "glsl/GrGLSLColorSpaceXformHelper.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" @@ -45,3 +46,4 @@ sk_sp GrDitherEffect::TestCreate(GrProcessorTestData* testD return GrDitherEffect::Make(); } #endif +#endif diff --git a/src/gpu/effects/GrDitherEffect.h b/src/gpu/effects/GrDitherEffect.h index cbb626424c..d0bf9a9f9c 100644 --- a/src/gpu/effects/GrDitherEffect.h +++ b/src/gpu/effects/GrDitherEffect.h @@ -10,6 +10,8 @@ */ #ifndef GrDitherEffect_DEFINED #define GrDitherEffect_DEFINED +#include "SkTypes.h" +#if SK_SUPPORT_GPU #include "GrFragmentProcessor.h" #include "GrCoordTransform.h" #include "effects/GrProxyMove.h" @@ -31,3 +33,4 @@ private: typedef GrFragmentProcessor INHERITED; }; #endif +#endif diff --git a/src/sksl/SkSLCPPCodeGenerator.cpp b/src/sksl/SkSLCPPCodeGenerator.cpp index 09622f3e4c..9210dde7a3 100644 --- a/src/sksl/SkSLCPPCodeGenerator.cpp +++ b/src/sksl/SkSLCPPCodeGenerator.cpp @@ -542,7 +542,8 @@ bool CPPCodeGenerator::generateCode() { const char* baseName = fName.c_str(); const char* fullName = fFullName.c_str(); this->writef(kFragmentProcessorHeader, fullName); - this->writef("#include \"%s.h\"\n", fullName); + this->writef("#include \"%s.h\"\n" + "#if SK_SUPPORT_GPU\n", fullName); this->writeSection(CPP_SECTION); this->writef("#include \"glsl/GrGLSLColorSpaceXformHelper.h\"\n" "#include \"glsl/GrGLSLFragmentProcessor.h\"\n" @@ -593,6 +594,7 @@ bool CPPCodeGenerator::generateCode() { "}\n"); this->writeTest(); this->writeSection(CPP_END_SECTION); + this->write("#endif\n"); result &= 0 == fErrors.errorCount(); return result; } diff --git a/src/sksl/SkSLHCodeGenerator.cpp b/src/sksl/SkSLHCodeGenerator.cpp index 405fb0e6ce..cd3f7f2ec1 100644 --- a/src/sksl/SkSLHCodeGenerator.cpp +++ b/src/sksl/SkSLHCodeGenerator.cpp @@ -201,6 +201,8 @@ bool HCodeGenerator::generateCode() { "#define %s_DEFINED\n", fFullName.c_str(), fFullName.c_str()); + this->writef("#include \"SkTypes.h\"\n" + "#if SK_SUPPORT_GPU\n"); this->writeSection(HEADER_SECTION); this->writef("#include \"GrFragmentProcessor.h\"\n" "#include \"GrCoordTransform.h\"\n" @@ -231,7 +233,8 @@ bool HCodeGenerator::generateCode() { this->writef(" typedef GrFragmentProcessor INHERITED;\n" "};\n"); this->writeSection(HEADER_END_SECTION); - this->writef("#endif\n"); + this->writef("#endif\n" + "#endif\n"); return 0 == fErrors.errorCount(); } -- cgit v1.2.3