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