1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2012 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "src/gpu/effects/GrGaussianConvolutionFragmentProcessor.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "src/core/SkGpuBlurUtils.h" 11cb93a386Sopenharmony_ci#include "src/gpu/GrTexture.h" 12cb93a386Sopenharmony_ci#include "src/gpu/GrTextureProxy.h" 13cb93a386Sopenharmony_ci#include "src/gpu/effects/GrTextureEffect.h" 14cb93a386Sopenharmony_ci#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" 15cb93a386Sopenharmony_ci#include "src/gpu/glsl/GrGLSLProgramDataManager.h" 16cb93a386Sopenharmony_ci#include "src/gpu/glsl/GrGLSLUniformHandler.h" 17cb93a386Sopenharmony_ci#include "src/sksl/dsl/priv/DSLFPs.h" 18cb93a386Sopenharmony_ci 19cb93a386Sopenharmony_ci// For brevity 20cb93a386Sopenharmony_ciusing UniformHandle = GrGLSLProgramDataManager::UniformHandle; 21cb93a386Sopenharmony_ciusing Direction = GrGaussianConvolutionFragmentProcessor::Direction; 22cb93a386Sopenharmony_ci 23cb93a386Sopenharmony_ciclass GrGaussianConvolutionFragmentProcessor::Impl : public ProgramImpl { 24cb93a386Sopenharmony_cipublic: 25cb93a386Sopenharmony_ci void emitCode(EmitArgs&) override; 26cb93a386Sopenharmony_ci 27cb93a386Sopenharmony_ciprivate: 28cb93a386Sopenharmony_ci void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; 29cb93a386Sopenharmony_ci 30cb93a386Sopenharmony_ci UniformHandle fKernelUni; 31cb93a386Sopenharmony_ci UniformHandle fOffsetsUni; 32cb93a386Sopenharmony_ci UniformHandle fKernelWidthUni; 33cb93a386Sopenharmony_ci UniformHandle fIncrementUni; 34cb93a386Sopenharmony_ci}; 35cb93a386Sopenharmony_ci 36cb93a386Sopenharmony_cienum class LoopType { 37cb93a386Sopenharmony_ci kUnrolled, 38cb93a386Sopenharmony_ci kFixedLength, 39cb93a386Sopenharmony_ci kVariableLength, 40cb93a386Sopenharmony_ci}; 41cb93a386Sopenharmony_ci 42cb93a386Sopenharmony_cistatic LoopType loop_type(const GrShaderCaps& caps) { 43cb93a386Sopenharmony_ci // This checks that bitwise integer operations and array indexing by non-consts are allowed. 44cb93a386Sopenharmony_ci if (caps.generation() < k130_GrGLSLGeneration) { 45cb93a386Sopenharmony_ci return LoopType::kUnrolled; 46cb93a386Sopenharmony_ci } 47cb93a386Sopenharmony_ci // If we're in reduced shader mode and we can have a loop then use a uniform to limit the 48cb93a386Sopenharmony_ci // number of iterations so we don't need a code variation for each width. 49cb93a386Sopenharmony_ci return caps.reducedShaderMode() ? LoopType::kVariableLength : LoopType::kFixedLength; 50cb93a386Sopenharmony_ci} 51cb93a386Sopenharmony_ci 52cb93a386Sopenharmony_civoid GrGaussianConvolutionFragmentProcessor::Impl::emitCode(EmitArgs& args) { 53cb93a386Sopenharmony_ci const GrGaussianConvolutionFragmentProcessor& ce = 54cb93a386Sopenharmony_ci args.fFp.cast<GrGaussianConvolutionFragmentProcessor>(); 55cb93a386Sopenharmony_ci 56cb93a386Sopenharmony_ci using namespace SkSL::dsl; 57cb93a386Sopenharmony_ci StartFragmentProcessor(this, &args); 58cb93a386Sopenharmony_ci GlobalVar increment(kUniform_Modifier, kHalf2_Type, "Increment"); 59cb93a386Sopenharmony_ci Declare(increment); 60cb93a386Sopenharmony_ci fIncrementUni = VarUniformHandle(increment); 61cb93a386Sopenharmony_ci 62cb93a386Sopenharmony_ci int width = SkGpuBlurUtils::LinearKernelWidth(ce.fRadius); 63cb93a386Sopenharmony_ci 64cb93a386Sopenharmony_ci LoopType loopType = loop_type(*args.fShaderCaps); 65cb93a386Sopenharmony_ci 66cb93a386Sopenharmony_ci int arrayCount; 67cb93a386Sopenharmony_ci if (loopType == LoopType::kVariableLength) { 68cb93a386Sopenharmony_ci // Size the kernel uniform for the maximum width. 69cb93a386Sopenharmony_ci arrayCount = (SkGpuBlurUtils::LinearKernelWidth(kMaxKernelRadius) + 3) / 4; 70cb93a386Sopenharmony_ci } else { 71cb93a386Sopenharmony_ci arrayCount = (width + 3) / 4; 72cb93a386Sopenharmony_ci SkASSERT(4 * arrayCount >= width); 73cb93a386Sopenharmony_ci } 74cb93a386Sopenharmony_ci 75cb93a386Sopenharmony_ci GlobalVar kernel(kUniform_Modifier, Array(kHalf4_Type, arrayCount), "Kernel"); 76cb93a386Sopenharmony_ci Declare(kernel); 77cb93a386Sopenharmony_ci fKernelUni = VarUniformHandle(kernel); 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_ci 80cb93a386Sopenharmony_ci GlobalVar offsets(kUniform_Modifier, Array(kHalf4_Type, arrayCount), "Offsets"); 81cb93a386Sopenharmony_ci Declare(offsets); 82cb93a386Sopenharmony_ci fOffsetsUni = VarUniformHandle(offsets); 83cb93a386Sopenharmony_ci 84cb93a386Sopenharmony_ci Var color(kHalf4_Type, "color", Half4(0)); 85cb93a386Sopenharmony_ci Declare(color); 86cb93a386Sopenharmony_ci 87cb93a386Sopenharmony_ci Var coord(kFloat2_Type, "coord", sk_SampleCoord()); 88cb93a386Sopenharmony_ci Declare(coord); 89cb93a386Sopenharmony_ci 90cb93a386Sopenharmony_ci switch (loopType) { 91cb93a386Sopenharmony_ci case LoopType::kUnrolled: 92cb93a386Sopenharmony_ci for (int i = 0; i < width; i++) { 93cb93a386Sopenharmony_ci color += SampleChild(/*index=*/0, coord + offsets[i / 4][i & 3] * increment) * 94cb93a386Sopenharmony_ci kernel[i / 4][i & 0x3]; 95cb93a386Sopenharmony_ci } 96cb93a386Sopenharmony_ci break; 97cb93a386Sopenharmony_ci case LoopType::kFixedLength: { 98cb93a386Sopenharmony_ci Var i(kInt_Type, "i", 0); 99cb93a386Sopenharmony_ci For(Declare(i), i < width, i++, 100cb93a386Sopenharmony_ci color += SampleChild(/*index=*/0, coord + offsets[i / 4][i & 3] * increment) * 101cb93a386Sopenharmony_ci kernel[i / 4][i & 0x3]); 102cb93a386Sopenharmony_ci break; 103cb93a386Sopenharmony_ci } 104cb93a386Sopenharmony_ci case LoopType::kVariableLength: { 105cb93a386Sopenharmony_ci GlobalVar kernelWidth(kUniform_Modifier, kInt_Type, "kernelWidth"); 106cb93a386Sopenharmony_ci Declare(kernelWidth); 107cb93a386Sopenharmony_ci fKernelWidthUni = VarUniformHandle(kernelWidth); 108cb93a386Sopenharmony_ci Var i(kInt_Type, "i", 0); 109cb93a386Sopenharmony_ci For(Declare(i), i < kernelWidth, i++, 110cb93a386Sopenharmony_ci color += SampleChild(/*index=*/0, coord + offsets[i / 4][i & 3] * increment) * 111cb93a386Sopenharmony_ci kernel[i / 4][i & 0x3]); 112cb93a386Sopenharmony_ci break; 113cb93a386Sopenharmony_ci } 114cb93a386Sopenharmony_ci } 115cb93a386Sopenharmony_ci 116cb93a386Sopenharmony_ci Return(color); 117cb93a386Sopenharmony_ci EndFragmentProcessor(); 118cb93a386Sopenharmony_ci} 119cb93a386Sopenharmony_ci 120cb93a386Sopenharmony_civoid GrGaussianConvolutionFragmentProcessor::Impl::onSetData(const GrGLSLProgramDataManager& pdman, 121cb93a386Sopenharmony_ci const GrFragmentProcessor& processor) { 122cb93a386Sopenharmony_ci const auto& conv = processor.cast<GrGaussianConvolutionFragmentProcessor>(); 123cb93a386Sopenharmony_ci 124cb93a386Sopenharmony_ci float increment[2] = {}; 125cb93a386Sopenharmony_ci increment[static_cast<int>(conv.fDirection)] = 1; 126cb93a386Sopenharmony_ci pdman.set2fv(fIncrementUni, 1, increment); 127cb93a386Sopenharmony_ci 128cb93a386Sopenharmony_ci int width = SkGpuBlurUtils::LinearKernelWidth(conv.fRadius); 129cb93a386Sopenharmony_ci int arrayCount = (width + 3)/4; 130cb93a386Sopenharmony_ci SkDEBUGCODE(size_t arraySize = 4*arrayCount;) 131cb93a386Sopenharmony_ci SkASSERT(arraySize >= static_cast<size_t>(width)); 132cb93a386Sopenharmony_ci SkASSERT(arraySize <= SK_ARRAY_COUNT(GrGaussianConvolutionFragmentProcessor::fKernel)); 133cb93a386Sopenharmony_ci pdman.set4fv(fKernelUni, arrayCount, conv.fKernel); 134cb93a386Sopenharmony_ci pdman.set4fv(fOffsetsUni, arrayCount, conv.fOffsets); 135cb93a386Sopenharmony_ci if (fKernelWidthUni.isValid()) { 136cb93a386Sopenharmony_ci pdman.set1i(fKernelWidthUni, width); 137cb93a386Sopenharmony_ci } 138cb93a386Sopenharmony_ci} 139cb93a386Sopenharmony_ci 140cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 141cb93a386Sopenharmony_ci 142cb93a386Sopenharmony_cistd::unique_ptr<GrFragmentProcessor> GrGaussianConvolutionFragmentProcessor::Make( 143cb93a386Sopenharmony_ci GrSurfaceProxyView view, 144cb93a386Sopenharmony_ci SkAlphaType alphaType, 145cb93a386Sopenharmony_ci Direction dir, 146cb93a386Sopenharmony_ci int halfWidth, 147cb93a386Sopenharmony_ci float gaussianSigma, 148cb93a386Sopenharmony_ci GrSamplerState::WrapMode wm, 149cb93a386Sopenharmony_ci const SkIRect& subset, 150cb93a386Sopenharmony_ci const SkIRect* pixelDomain, 151cb93a386Sopenharmony_ci const GrCaps& caps) { 152cb93a386Sopenharmony_ci std::unique_ptr<GrFragmentProcessor> child; 153cb93a386Sopenharmony_ci bool is_zero_sigma = SkGpuBlurUtils::IsEffectivelyZeroSigma(gaussianSigma); 154cb93a386Sopenharmony_ci // We should sample as nearest if there will be no shader to preserve existing behaviour, but 155cb93a386Sopenharmony_ci // the linear blur requires a linear sample. 156cb93a386Sopenharmony_ci GrSamplerState::Filter filter = is_zero_sigma ? 157cb93a386Sopenharmony_ci GrSamplerState::Filter::kNearest : GrSamplerState::Filter::kLinear; 158cb93a386Sopenharmony_ci GrSamplerState sampler(wm, filter); 159cb93a386Sopenharmony_ci if (is_zero_sigma) { 160cb93a386Sopenharmony_ci halfWidth = 0; 161cb93a386Sopenharmony_ci } 162cb93a386Sopenharmony_ci // It's pretty common to blur a subset of an input texture. In reduced shader mode we always 163cb93a386Sopenharmony_ci // apply the wrap mode in the shader. 164cb93a386Sopenharmony_ci bool alwaysUseShaderTileMode = caps.reducedShaderMode(); 165cb93a386Sopenharmony_ci if (pixelDomain && !alwaysUseShaderTileMode) { 166cb93a386Sopenharmony_ci // Inset because we expect to be invoked at pixel centers. 167cb93a386Sopenharmony_ci SkRect domain = SkRect::Make(*pixelDomain).makeInset(0.5, 0.5f); 168cb93a386Sopenharmony_ci switch (dir) { 169cb93a386Sopenharmony_ci case Direction::kX: domain.outset(halfWidth, 0); break; 170cb93a386Sopenharmony_ci case Direction::kY: domain.outset(0, halfWidth); break; 171cb93a386Sopenharmony_ci } 172cb93a386Sopenharmony_ci child = GrTextureEffect::MakeSubset(std::move(view), 173cb93a386Sopenharmony_ci alphaType, 174cb93a386Sopenharmony_ci SkMatrix::I(), 175cb93a386Sopenharmony_ci sampler, 176cb93a386Sopenharmony_ci SkRect::Make(subset), 177cb93a386Sopenharmony_ci domain, 178cb93a386Sopenharmony_ci caps, 179cb93a386Sopenharmony_ci GrTextureEffect::kDefaultBorder); 180cb93a386Sopenharmony_ci } else { 181cb93a386Sopenharmony_ci child = GrTextureEffect::MakeSubset(std::move(view), 182cb93a386Sopenharmony_ci alphaType, 183cb93a386Sopenharmony_ci SkMatrix::I(), 184cb93a386Sopenharmony_ci sampler, 185cb93a386Sopenharmony_ci SkRect::Make(subset), 186cb93a386Sopenharmony_ci caps, 187cb93a386Sopenharmony_ci GrTextureEffect::kDefaultBorder, 188cb93a386Sopenharmony_ci alwaysUseShaderTileMode); 189cb93a386Sopenharmony_ci } 190cb93a386Sopenharmony_ci 191cb93a386Sopenharmony_ci if (is_zero_sigma) { 192cb93a386Sopenharmony_ci return child; 193cb93a386Sopenharmony_ci } 194cb93a386Sopenharmony_ci return std::unique_ptr<GrFragmentProcessor>(new GrGaussianConvolutionFragmentProcessor( 195cb93a386Sopenharmony_ci std::move(child), dir, halfWidth, gaussianSigma)); 196cb93a386Sopenharmony_ci} 197cb93a386Sopenharmony_ci 198cb93a386Sopenharmony_ciGrGaussianConvolutionFragmentProcessor::GrGaussianConvolutionFragmentProcessor( 199cb93a386Sopenharmony_ci std::unique_ptr<GrFragmentProcessor> child, 200cb93a386Sopenharmony_ci Direction direction, 201cb93a386Sopenharmony_ci int radius, 202cb93a386Sopenharmony_ci float gaussianSigma) 203cb93a386Sopenharmony_ci : INHERITED(kGrGaussianConvolutionFragmentProcessor_ClassID, 204cb93a386Sopenharmony_ci ProcessorOptimizationFlags(child.get())) 205cb93a386Sopenharmony_ci , fRadius(radius) 206cb93a386Sopenharmony_ci , fDirection(direction) { 207cb93a386Sopenharmony_ci this->registerChild(std::move(child), SkSL::SampleUsage::Explicit()); 208cb93a386Sopenharmony_ci SkASSERT(radius <= kMaxKernelRadius); 209cb93a386Sopenharmony_ci SkGpuBlurUtils::Compute1DLinearGaussianKernel(fKernel, fOffsets, gaussianSigma, fRadius); 210cb93a386Sopenharmony_ci this->setUsesSampleCoordsDirectly(); 211cb93a386Sopenharmony_ci} 212cb93a386Sopenharmony_ci 213cb93a386Sopenharmony_ciGrGaussianConvolutionFragmentProcessor::GrGaussianConvolutionFragmentProcessor( 214cb93a386Sopenharmony_ci const GrGaussianConvolutionFragmentProcessor& that) 215cb93a386Sopenharmony_ci : INHERITED(that) 216cb93a386Sopenharmony_ci , fRadius(that.fRadius) 217cb93a386Sopenharmony_ci , fDirection(that.fDirection) { 218cb93a386Sopenharmony_ci memcpy(fKernel, that.fKernel, SkGpuBlurUtils::LinearKernelWidth(fRadius) * sizeof(float)); 219cb93a386Sopenharmony_ci memcpy(fOffsets, that.fOffsets, SkGpuBlurUtils::LinearKernelWidth(fRadius) * sizeof(float)); 220cb93a386Sopenharmony_ci} 221cb93a386Sopenharmony_ci 222cb93a386Sopenharmony_ciSkString GrGaussianConvolutionFragmentProcessor::getShaderDfxInfo() const 223cb93a386Sopenharmony_ci{ 224cb93a386Sopenharmony_ci SkString format; 225cb93a386Sopenharmony_ci format.printf("ShaderDfx_GrGaussianConvolution_%d", fRadius); 226cb93a386Sopenharmony_ci return format; 227cb93a386Sopenharmony_ci} 228cb93a386Sopenharmony_ci 229cb93a386Sopenharmony_civoid GrGaussianConvolutionFragmentProcessor::onAddToKey(const GrShaderCaps& shaderCaps, 230cb93a386Sopenharmony_ci GrProcessorKeyBuilder* b) const { 231cb93a386Sopenharmony_ci if (loop_type(shaderCaps) != LoopType::kVariableLength) { 232cb93a386Sopenharmony_ci b->add32(fRadius); 233cb93a386Sopenharmony_ci } 234cb93a386Sopenharmony_ci} 235cb93a386Sopenharmony_ci 236cb93a386Sopenharmony_cistd::unique_ptr<GrFragmentProcessor::ProgramImpl> 237cb93a386Sopenharmony_ciGrGaussianConvolutionFragmentProcessor::onMakeProgramImpl() const { 238cb93a386Sopenharmony_ci return std::make_unique<Impl>(); 239cb93a386Sopenharmony_ci} 240cb93a386Sopenharmony_ci 241cb93a386Sopenharmony_cibool GrGaussianConvolutionFragmentProcessor::onIsEqual(const GrFragmentProcessor& sBase) const { 242cb93a386Sopenharmony_ci const auto& that = sBase.cast<GrGaussianConvolutionFragmentProcessor>(); 243cb93a386Sopenharmony_ci return fRadius == that.fRadius && fDirection == that.fDirection && 244cb93a386Sopenharmony_ci std::equal(fKernel, fKernel + SkGpuBlurUtils::LinearKernelWidth(fRadius), that.fKernel) && 245cb93a386Sopenharmony_ci std::equal(fOffsets, fOffsets + SkGpuBlurUtils::LinearKernelWidth(fRadius), that.fOffsets); 246cb93a386Sopenharmony_ci} 247cb93a386Sopenharmony_ci 248cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 249cb93a386Sopenharmony_ci 250cb93a386Sopenharmony_ciGR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrGaussianConvolutionFragmentProcessor); 251cb93a386Sopenharmony_ci 252cb93a386Sopenharmony_ci#if GR_TEST_UTILS 253cb93a386Sopenharmony_cistd::unique_ptr<GrFragmentProcessor> GrGaussianConvolutionFragmentProcessor::TestCreate( 254cb93a386Sopenharmony_ci GrProcessorTestData* d) { 255cb93a386Sopenharmony_ci auto [view, ct, at] = d->randomView(); 256cb93a386Sopenharmony_ci 257cb93a386Sopenharmony_ci Direction dir = d->fRandom->nextBool() ? Direction::kY : Direction::kX; 258cb93a386Sopenharmony_ci SkIRect subset{ 259cb93a386Sopenharmony_ci static_cast<int>(d->fRandom->nextRangeU(0, view.width() - 1)), 260cb93a386Sopenharmony_ci static_cast<int>(d->fRandom->nextRangeU(0, view.height() - 1)), 261cb93a386Sopenharmony_ci static_cast<int>(d->fRandom->nextRangeU(0, view.width() - 1)), 262cb93a386Sopenharmony_ci static_cast<int>(d->fRandom->nextRangeU(0, view.height() - 1)), 263cb93a386Sopenharmony_ci }; 264cb93a386Sopenharmony_ci subset.sort(); 265cb93a386Sopenharmony_ci 266cb93a386Sopenharmony_ci auto wm = static_cast<GrSamplerState::WrapMode>( 267cb93a386Sopenharmony_ci d->fRandom->nextULessThan(GrSamplerState::kWrapModeCount)); 268cb93a386Sopenharmony_ci int radius = d->fRandom->nextRangeU(1, kMaxKernelRadius); 269cb93a386Sopenharmony_ci float sigma = radius / 3.f; 270cb93a386Sopenharmony_ci SkIRect temp; 271cb93a386Sopenharmony_ci SkIRect* domain = nullptr; 272cb93a386Sopenharmony_ci if (d->fRandom->nextBool()) { 273cb93a386Sopenharmony_ci temp = { 274cb93a386Sopenharmony_ci static_cast<int>(d->fRandom->nextRangeU(0, view.width() - 1)), 275cb93a386Sopenharmony_ci static_cast<int>(d->fRandom->nextRangeU(0, view.height() - 1)), 276cb93a386Sopenharmony_ci static_cast<int>(d->fRandom->nextRangeU(0, view.width() - 1)), 277cb93a386Sopenharmony_ci static_cast<int>(d->fRandom->nextRangeU(0, view.height() - 1)), 278cb93a386Sopenharmony_ci }; 279cb93a386Sopenharmony_ci temp.sort(); 280cb93a386Sopenharmony_ci domain = &temp; 281cb93a386Sopenharmony_ci } 282cb93a386Sopenharmony_ci 283cb93a386Sopenharmony_ci return GrGaussianConvolutionFragmentProcessor::Make(std::move(view), at, dir, radius, sigma, wm, 284cb93a386Sopenharmony_ci subset, domain, *d->caps()); 285cb93a386Sopenharmony_ci} 286cb93a386Sopenharmony_ci#endif 287