1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2017 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/effects/GrTextureEffect.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/GpuTypes.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrTypes.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAssert.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkFloatingPoint.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkMath.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/gpu/ganesh/GrTypesPriv.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkRandom.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkSLTypeShared.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/KeyBuilder.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrSurfaceProxy.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrTestUtils.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrTexture.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/effects/GrMatrixEffect.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/glsl/GrGLSLProgramDataManager.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/glsl/GrGLSLUniformHandler.h"
28*c8dee2aaSAndroid Build Coastguard Worker
29*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
30*c8dee2aaSAndroid Build Coastguard Worker #include <cmath>
31*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
32*c8dee2aaSAndroid Build Coastguard Worker
33*c8dee2aaSAndroid Build Coastguard Worker enum SkAlphaType : int;
34*c8dee2aaSAndroid Build Coastguard Worker struct GrShaderCaps;
35*c8dee2aaSAndroid Build Coastguard Worker
36*c8dee2aaSAndroid Build Coastguard Worker using Wrap = GrSamplerState::WrapMode;
37*c8dee2aaSAndroid Build Coastguard Worker using Filter = GrSamplerState::Filter;
38*c8dee2aaSAndroid Build Coastguard Worker using MipmapMode = GrSamplerState::MipmapMode;
39*c8dee2aaSAndroid Build Coastguard Worker
40*c8dee2aaSAndroid Build Coastguard Worker struct GrTextureEffect::Sampling {
41*c8dee2aaSAndroid Build Coastguard Worker GrSamplerState fHWSampler;
42*c8dee2aaSAndroid Build Coastguard Worker ShaderMode fShaderModes[2] = {ShaderMode::kNone, ShaderMode::kNone};
43*c8dee2aaSAndroid Build Coastguard Worker SkRect fShaderSubset = {0, 0, 0, 0};
44*c8dee2aaSAndroid Build Coastguard Worker SkRect fShaderClamp = {0, 0, 0, 0};
45*c8dee2aaSAndroid Build Coastguard Worker float fBorder[4] = {0, 0, 0, 0};
SamplingGrTextureEffect::Sampling46*c8dee2aaSAndroid Build Coastguard Worker Sampling(Filter filter, MipmapMode mm) : fHWSampler(filter, mm) {}
47*c8dee2aaSAndroid Build Coastguard Worker Sampling(const GrSurfaceProxy& proxy,
48*c8dee2aaSAndroid Build Coastguard Worker GrSamplerState wrap,
49*c8dee2aaSAndroid Build Coastguard Worker const SkRect&,
50*c8dee2aaSAndroid Build Coastguard Worker const SkRect*,
51*c8dee2aaSAndroid Build Coastguard Worker const float border[4],
52*c8dee2aaSAndroid Build Coastguard Worker bool alwaysUseShaderTileMode,
53*c8dee2aaSAndroid Build Coastguard Worker const GrCaps&,
54*c8dee2aaSAndroid Build Coastguard Worker SkVector linearFilterInset = {0.5f, 0.5f});
55*c8dee2aaSAndroid Build Coastguard Worker inline bool hasBorderAlpha() const;
56*c8dee2aaSAndroid Build Coastguard Worker };
57*c8dee2aaSAndroid Build Coastguard Worker
Sampling(const GrSurfaceProxy & proxy,GrSamplerState sampler,const SkRect & subset,const SkRect * domain,const float border[4],bool alwaysUseShaderTileMode,const GrCaps & caps,SkVector linearFilterInset)58*c8dee2aaSAndroid Build Coastguard Worker GrTextureEffect::Sampling::Sampling(const GrSurfaceProxy& proxy,
59*c8dee2aaSAndroid Build Coastguard Worker GrSamplerState sampler,
60*c8dee2aaSAndroid Build Coastguard Worker const SkRect& subset,
61*c8dee2aaSAndroid Build Coastguard Worker const SkRect* domain,
62*c8dee2aaSAndroid Build Coastguard Worker const float border[4],
63*c8dee2aaSAndroid Build Coastguard Worker bool alwaysUseShaderTileMode,
64*c8dee2aaSAndroid Build Coastguard Worker const GrCaps& caps,
65*c8dee2aaSAndroid Build Coastguard Worker SkVector linearFilterInset) {
66*c8dee2aaSAndroid Build Coastguard Worker struct Span {
67*c8dee2aaSAndroid Build Coastguard Worker float fA = 0.f, fB = 0.f;
68*c8dee2aaSAndroid Build Coastguard Worker
69*c8dee2aaSAndroid Build Coastguard Worker Span makeInset(float o) const {
70*c8dee2aaSAndroid Build Coastguard Worker Span r = {fA + o, fB - o};
71*c8dee2aaSAndroid Build Coastguard Worker if (r.fA > r.fB) {
72*c8dee2aaSAndroid Build Coastguard Worker r.fA = r.fB = (r.fA + r.fB) / 2;
73*c8dee2aaSAndroid Build Coastguard Worker }
74*c8dee2aaSAndroid Build Coastguard Worker return r;
75*c8dee2aaSAndroid Build Coastguard Worker }
76*c8dee2aaSAndroid Build Coastguard Worker
77*c8dee2aaSAndroid Build Coastguard Worker bool contains(Span r) const { return fA <= r.fA && fB >= r.fB; }
78*c8dee2aaSAndroid Build Coastguard Worker };
79*c8dee2aaSAndroid Build Coastguard Worker struct Result1D {
80*c8dee2aaSAndroid Build Coastguard Worker ShaderMode fShaderMode = ShaderMode::kNone;
81*c8dee2aaSAndroid Build Coastguard Worker Span fShaderSubset = {};
82*c8dee2aaSAndroid Build Coastguard Worker Span fShaderClamp = {};
83*c8dee2aaSAndroid Build Coastguard Worker Wrap fHWWrap = Wrap::kClamp;
84*c8dee2aaSAndroid Build Coastguard Worker };
85*c8dee2aaSAndroid Build Coastguard Worker
86*c8dee2aaSAndroid Build Coastguard Worker auto type = proxy.asTextureProxy()->textureType();
87*c8dee2aaSAndroid Build Coastguard Worker auto filter = sampler.filter();
88*c8dee2aaSAndroid Build Coastguard Worker auto mm = sampler.mipmapMode();
89*c8dee2aaSAndroid Build Coastguard Worker
90*c8dee2aaSAndroid Build Coastguard Worker auto canDoWrapInHW = [&](int size, Wrap wrap) {
91*c8dee2aaSAndroid Build Coastguard Worker if (alwaysUseShaderTileMode) {
92*c8dee2aaSAndroid Build Coastguard Worker return false;
93*c8dee2aaSAndroid Build Coastguard Worker }
94*c8dee2aaSAndroid Build Coastguard Worker // TODO: Use HW border color when available.
95*c8dee2aaSAndroid Build Coastguard Worker if (wrap == Wrap::kClampToBorder &&
96*c8dee2aaSAndroid Build Coastguard Worker (!caps.clampToBorderSupport() || border[0] || border[1] || border[2] || border[3])) {
97*c8dee2aaSAndroid Build Coastguard Worker return false;
98*c8dee2aaSAndroid Build Coastguard Worker }
99*c8dee2aaSAndroid Build Coastguard Worker if (wrap != Wrap::kClamp && !caps.npotTextureTileSupport() && !SkIsPow2(size)) {
100*c8dee2aaSAndroid Build Coastguard Worker return false;
101*c8dee2aaSAndroid Build Coastguard Worker }
102*c8dee2aaSAndroid Build Coastguard Worker if (type != GrTextureType::k2D &&
103*c8dee2aaSAndroid Build Coastguard Worker !(wrap == Wrap::kClamp || wrap == Wrap::kClampToBorder)) {
104*c8dee2aaSAndroid Build Coastguard Worker return false;
105*c8dee2aaSAndroid Build Coastguard Worker }
106*c8dee2aaSAndroid Build Coastguard Worker return true;
107*c8dee2aaSAndroid Build Coastguard Worker };
108*c8dee2aaSAndroid Build Coastguard Worker
109*c8dee2aaSAndroid Build Coastguard Worker SkISize dim = proxy.isFullyLazy() ? SkISize{-1, -1} : proxy.backingStoreDimensions();
110*c8dee2aaSAndroid Build Coastguard Worker
111*c8dee2aaSAndroid Build Coastguard Worker // TODO: Right now if we use shader based subsetting for any reason we just completely drop
112*c8dee2aaSAndroid Build Coastguard Worker // aniso. Longer term allow shader subsetting, reusing the special repeat mode LOD selection
113*c8dee2aaSAndroid Build Coastguard Worker // logic for mip maps, and simply don't attempt to restrict ansiso's computed samples to the
114*c8dee2aaSAndroid Build Coastguard Worker // subset. That is use "subsetting" but not "clamping"/insetting in terms of the shader gen
115*c8dee2aaSAndroid Build Coastguard Worker // logic.
116*c8dee2aaSAndroid Build Coastguard Worker bool aniso = sampler.isAniso();
117*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!aniso || caps.anisoSupport());
118*c8dee2aaSAndroid Build Coastguard Worker if (aniso) {
119*c8dee2aaSAndroid Build Coastguard Worker bool anisoSubset = !subset.contains(proxy.backingStoreBoundsRect()) &&
120*c8dee2aaSAndroid Build Coastguard Worker (!domain || !subset.contains(*domain));
121*c8dee2aaSAndroid Build Coastguard Worker bool needsShaderWrap = !canDoWrapInHW(dim.width(), sampler.wrapModeX()) ||
122*c8dee2aaSAndroid Build Coastguard Worker !canDoWrapInHW(dim.height(), sampler.wrapModeY());
123*c8dee2aaSAndroid Build Coastguard Worker if (needsShaderWrap || anisoSubset) {
124*c8dee2aaSAndroid Build Coastguard Worker MipmapMode newMM = proxy.asTextureProxy()->mipmapped() == skgpu::Mipmapped::kYes
125*c8dee2aaSAndroid Build Coastguard Worker ? MipmapMode::kLinear
126*c8dee2aaSAndroid Build Coastguard Worker : MipmapMode::kNone;
127*c8dee2aaSAndroid Build Coastguard Worker sampler = GrSamplerState(sampler.wrapModeX(),
128*c8dee2aaSAndroid Build Coastguard Worker sampler.wrapModeY(),
129*c8dee2aaSAndroid Build Coastguard Worker SkFilterMode::kLinear,
130*c8dee2aaSAndroid Build Coastguard Worker newMM);
131*c8dee2aaSAndroid Build Coastguard Worker aniso = false;
132*c8dee2aaSAndroid Build Coastguard Worker }
133*c8dee2aaSAndroid Build Coastguard Worker }
134*c8dee2aaSAndroid Build Coastguard Worker
135*c8dee2aaSAndroid Build Coastguard Worker auto resolve = [&](int size, Wrap wrap, Span subset, Span domain, float linearFilterInset) {
136*c8dee2aaSAndroid Build Coastguard Worker Result1D r;
137*c8dee2aaSAndroid Build Coastguard Worker bool canDoModeInHW = canDoWrapInHW(size, wrap);
138*c8dee2aaSAndroid Build Coastguard Worker if (canDoModeInHW && size > 0 && subset.fA <= 0 && subset.fB >= size) {
139*c8dee2aaSAndroid Build Coastguard Worker r.fShaderMode = ShaderMode::kNone;
140*c8dee2aaSAndroid Build Coastguard Worker r.fHWWrap = wrap;
141*c8dee2aaSAndroid Build Coastguard Worker r.fShaderSubset = r.fShaderClamp = {0, 0};
142*c8dee2aaSAndroid Build Coastguard Worker return r;
143*c8dee2aaSAndroid Build Coastguard Worker }
144*c8dee2aaSAndroid Build Coastguard Worker
145*c8dee2aaSAndroid Build Coastguard Worker r.fShaderSubset = subset;
146*c8dee2aaSAndroid Build Coastguard Worker bool domainIsSafe = false;
147*c8dee2aaSAndroid Build Coastguard Worker if (filter == Filter::kNearest) {
148*c8dee2aaSAndroid Build Coastguard Worker Span isubset{std::floor(subset.fA), std::ceil(subset.fB)};
149*c8dee2aaSAndroid Build Coastguard Worker if (domain.fA > isubset.fA && domain.fB < isubset.fB) {
150*c8dee2aaSAndroid Build Coastguard Worker domainIsSafe = true;
151*c8dee2aaSAndroid Build Coastguard Worker }
152*c8dee2aaSAndroid Build Coastguard Worker // This inset prevents sampling neighboring texels that could occur when
153*c8dee2aaSAndroid Build Coastguard Worker // texture coords fall exactly at texel boundaries (depending on precision
154*c8dee2aaSAndroid Build Coastguard Worker // and GPU-specific snapping at the boundary).
155*c8dee2aaSAndroid Build Coastguard Worker r.fShaderClamp = isubset.makeInset(0.5f + kInsetEpsilon);
156*c8dee2aaSAndroid Build Coastguard Worker } else {
157*c8dee2aaSAndroid Build Coastguard Worker r.fShaderClamp = subset.makeInset(linearFilterInset + kInsetEpsilon);
158*c8dee2aaSAndroid Build Coastguard Worker if (r.fShaderClamp.contains(domain)) {
159*c8dee2aaSAndroid Build Coastguard Worker domainIsSafe = true;
160*c8dee2aaSAndroid Build Coastguard Worker }
161*c8dee2aaSAndroid Build Coastguard Worker }
162*c8dee2aaSAndroid Build Coastguard Worker if (!alwaysUseShaderTileMode && domainIsSafe) {
163*c8dee2aaSAndroid Build Coastguard Worker // The domain of coords that will be used won't access texels outside of the subset.
164*c8dee2aaSAndroid Build Coastguard Worker // So the wrap mode effectively doesn't matter. We use kClamp since it is always
165*c8dee2aaSAndroid Build Coastguard Worker // supported.
166*c8dee2aaSAndroid Build Coastguard Worker r.fShaderMode = ShaderMode::kNone;
167*c8dee2aaSAndroid Build Coastguard Worker r.fHWWrap = Wrap::kClamp;
168*c8dee2aaSAndroid Build Coastguard Worker r.fShaderSubset = r.fShaderClamp = {0, 0};
169*c8dee2aaSAndroid Build Coastguard Worker return r;
170*c8dee2aaSAndroid Build Coastguard Worker }
171*c8dee2aaSAndroid Build Coastguard Worker r.fShaderMode = GetShaderMode(wrap, filter, mm);
172*c8dee2aaSAndroid Build Coastguard Worker r.fHWWrap = Wrap::kClamp;
173*c8dee2aaSAndroid Build Coastguard Worker return r;
174*c8dee2aaSAndroid Build Coastguard Worker };
175*c8dee2aaSAndroid Build Coastguard Worker
176*c8dee2aaSAndroid Build Coastguard Worker Result1D x, y;
177*c8dee2aaSAndroid Build Coastguard Worker if (!aniso) {
178*c8dee2aaSAndroid Build Coastguard Worker Span subsetX{subset.fLeft, subset.fRight};
179*c8dee2aaSAndroid Build Coastguard Worker auto domainX = domain ? Span{domain->fLeft, domain->fRight}
180*c8dee2aaSAndroid Build Coastguard Worker : Span{SK_FloatNegativeInfinity, SK_FloatInfinity};
181*c8dee2aaSAndroid Build Coastguard Worker x = resolve(dim.width(), sampler.wrapModeX(), subsetX, domainX, linearFilterInset.fX);
182*c8dee2aaSAndroid Build Coastguard Worker
183*c8dee2aaSAndroid Build Coastguard Worker Span subsetY{subset.fTop, subset.fBottom};
184*c8dee2aaSAndroid Build Coastguard Worker auto domainY = domain ? Span{domain->fTop, domain->fBottom}
185*c8dee2aaSAndroid Build Coastguard Worker : Span{SK_FloatNegativeInfinity, SK_FloatInfinity};
186*c8dee2aaSAndroid Build Coastguard Worker y = resolve(dim.height(), sampler.wrapModeY(), subsetY, domainY, linearFilterInset.fY);
187*c8dee2aaSAndroid Build Coastguard Worker } else {
188*c8dee2aaSAndroid Build Coastguard Worker x.fHWWrap = sampler.wrapModeX();
189*c8dee2aaSAndroid Build Coastguard Worker y.fHWWrap = sampler.wrapModeY();
190*c8dee2aaSAndroid Build Coastguard Worker }
191*c8dee2aaSAndroid Build Coastguard Worker
192*c8dee2aaSAndroid Build Coastguard Worker fHWSampler = aniso ? GrSamplerState::Aniso(x.fHWWrap,
193*c8dee2aaSAndroid Build Coastguard Worker y.fHWWrap,
194*c8dee2aaSAndroid Build Coastguard Worker sampler.maxAniso(),
195*c8dee2aaSAndroid Build Coastguard Worker proxy.asTextureProxy()->mipmapped())
196*c8dee2aaSAndroid Build Coastguard Worker : GrSamplerState{x.fHWWrap, y.fHWWrap, filter, mm};
197*c8dee2aaSAndroid Build Coastguard Worker fShaderModes[0] = x.fShaderMode;
198*c8dee2aaSAndroid Build Coastguard Worker fShaderModes[1] = y.fShaderMode;
199*c8dee2aaSAndroid Build Coastguard Worker fShaderSubset = {x.fShaderSubset.fA, y.fShaderSubset.fA,
200*c8dee2aaSAndroid Build Coastguard Worker x.fShaderSubset.fB, y.fShaderSubset.fB};
201*c8dee2aaSAndroid Build Coastguard Worker fShaderClamp = {x.fShaderClamp.fA, y.fShaderClamp.fA,
202*c8dee2aaSAndroid Build Coastguard Worker x.fShaderClamp.fB, y.fShaderClamp.fB};
203*c8dee2aaSAndroid Build Coastguard Worker std::copy_n(border, 4, fBorder);
204*c8dee2aaSAndroid Build Coastguard Worker }
205*c8dee2aaSAndroid Build Coastguard Worker
hasBorderAlpha() const206*c8dee2aaSAndroid Build Coastguard Worker bool GrTextureEffect::Sampling::hasBorderAlpha() const {
207*c8dee2aaSAndroid Build Coastguard Worker if (fHWSampler.wrapModeX() == Wrap::kClampToBorder ||
208*c8dee2aaSAndroid Build Coastguard Worker fHWSampler.wrapModeY() == Wrap::kClampToBorder) {
209*c8dee2aaSAndroid Build Coastguard Worker return true;
210*c8dee2aaSAndroid Build Coastguard Worker }
211*c8dee2aaSAndroid Build Coastguard Worker if (ShaderModeIsClampToBorder(fShaderModes[0]) || ShaderModeIsClampToBorder(fShaderModes[1])) {
212*c8dee2aaSAndroid Build Coastguard Worker return fBorder[3] < 1.f;
213*c8dee2aaSAndroid Build Coastguard Worker }
214*c8dee2aaSAndroid Build Coastguard Worker return false;
215*c8dee2aaSAndroid Build Coastguard Worker }
216*c8dee2aaSAndroid Build Coastguard Worker
Make(GrSurfaceProxyView view,SkAlphaType alphaType,const SkMatrix & matrix,Filter filter,MipmapMode mm)217*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(GrSurfaceProxyView view,
218*c8dee2aaSAndroid Build Coastguard Worker SkAlphaType alphaType,
219*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& matrix,
220*c8dee2aaSAndroid Build Coastguard Worker Filter filter,
221*c8dee2aaSAndroid Build Coastguard Worker MipmapMode mm) {
222*c8dee2aaSAndroid Build Coastguard Worker Sampling sampling = Sampling(filter, mm);
223*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> te(new GrTextureEffect(std::move(view),
224*c8dee2aaSAndroid Build Coastguard Worker alphaType,
225*c8dee2aaSAndroid Build Coastguard Worker sampling));
226*c8dee2aaSAndroid Build Coastguard Worker return GrMatrixEffect::Make(matrix, std::move(te));
227*c8dee2aaSAndroid Build Coastguard Worker }
228*c8dee2aaSAndroid Build Coastguard Worker
Make(GrSurfaceProxyView view,SkAlphaType alphaType,const SkMatrix & matrix,GrSamplerState sampler,const GrCaps & caps,const float border[4])229*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(GrSurfaceProxyView view,
230*c8dee2aaSAndroid Build Coastguard Worker SkAlphaType alphaType,
231*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& matrix,
232*c8dee2aaSAndroid Build Coastguard Worker GrSamplerState sampler,
233*c8dee2aaSAndroid Build Coastguard Worker const GrCaps& caps,
234*c8dee2aaSAndroid Build Coastguard Worker const float border[4]) {
235*c8dee2aaSAndroid Build Coastguard Worker Sampling sampling(*view.proxy(),
236*c8dee2aaSAndroid Build Coastguard Worker sampler,
237*c8dee2aaSAndroid Build Coastguard Worker SkRect::Make(view.proxy()->dimensions()),
238*c8dee2aaSAndroid Build Coastguard Worker nullptr,
239*c8dee2aaSAndroid Build Coastguard Worker border,
240*c8dee2aaSAndroid Build Coastguard Worker false,
241*c8dee2aaSAndroid Build Coastguard Worker caps);
242*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> te(new GrTextureEffect(std::move(view),
243*c8dee2aaSAndroid Build Coastguard Worker alphaType,
244*c8dee2aaSAndroid Build Coastguard Worker sampling));
245*c8dee2aaSAndroid Build Coastguard Worker return GrMatrixEffect::Make(matrix, std::move(te));
246*c8dee2aaSAndroid Build Coastguard Worker }
247*c8dee2aaSAndroid Build Coastguard Worker
MakeSubset(GrSurfaceProxyView view,SkAlphaType alphaType,const SkMatrix & matrix,GrSamplerState sampler,const SkRect & subset,const GrCaps & caps,const float border[4],bool alwaysUseShaderTileMode)248*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyView view,
249*c8dee2aaSAndroid Build Coastguard Worker SkAlphaType alphaType,
250*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& matrix,
251*c8dee2aaSAndroid Build Coastguard Worker GrSamplerState sampler,
252*c8dee2aaSAndroid Build Coastguard Worker const SkRect& subset,
253*c8dee2aaSAndroid Build Coastguard Worker const GrCaps& caps,
254*c8dee2aaSAndroid Build Coastguard Worker const float border[4],
255*c8dee2aaSAndroid Build Coastguard Worker bool alwaysUseShaderTileMode) {
256*c8dee2aaSAndroid Build Coastguard Worker Sampling sampling(*view.proxy(),
257*c8dee2aaSAndroid Build Coastguard Worker sampler,
258*c8dee2aaSAndroid Build Coastguard Worker subset,
259*c8dee2aaSAndroid Build Coastguard Worker nullptr,
260*c8dee2aaSAndroid Build Coastguard Worker border,
261*c8dee2aaSAndroid Build Coastguard Worker alwaysUseShaderTileMode,
262*c8dee2aaSAndroid Build Coastguard Worker caps);
263*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> te(new GrTextureEffect(std::move(view),
264*c8dee2aaSAndroid Build Coastguard Worker alphaType,
265*c8dee2aaSAndroid Build Coastguard Worker sampling));
266*c8dee2aaSAndroid Build Coastguard Worker return GrMatrixEffect::Make(matrix, std::move(te));
267*c8dee2aaSAndroid Build Coastguard Worker }
268*c8dee2aaSAndroid Build Coastguard Worker
MakeSubset(GrSurfaceProxyView view,SkAlphaType alphaType,const SkMatrix & matrix,GrSamplerState sampler,const SkRect & subset,const SkRect & domain,const GrCaps & caps,const float border[4])269*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyView view,
270*c8dee2aaSAndroid Build Coastguard Worker SkAlphaType alphaType,
271*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& matrix,
272*c8dee2aaSAndroid Build Coastguard Worker GrSamplerState sampler,
273*c8dee2aaSAndroid Build Coastguard Worker const SkRect& subset,
274*c8dee2aaSAndroid Build Coastguard Worker const SkRect& domain,
275*c8dee2aaSAndroid Build Coastguard Worker const GrCaps& caps,
276*c8dee2aaSAndroid Build Coastguard Worker const float border[4]) {
277*c8dee2aaSAndroid Build Coastguard Worker Sampling sampling(*view.proxy(), sampler, subset, &domain, border, false, caps);
278*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> te(new GrTextureEffect(std::move(view),
279*c8dee2aaSAndroid Build Coastguard Worker alphaType,
280*c8dee2aaSAndroid Build Coastguard Worker sampling));
281*c8dee2aaSAndroid Build Coastguard Worker return GrMatrixEffect::Make(matrix, std::move(te));
282*c8dee2aaSAndroid Build Coastguard Worker }
283*c8dee2aaSAndroid Build Coastguard Worker
MakeCustomLinearFilterInset(GrSurfaceProxyView view,SkAlphaType alphaType,const SkMatrix & matrix,Wrap wx,Wrap wy,const SkRect & subset,const SkRect * domain,SkVector inset,const GrCaps & caps,const float border[4])284*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeCustomLinearFilterInset(
285*c8dee2aaSAndroid Build Coastguard Worker GrSurfaceProxyView view,
286*c8dee2aaSAndroid Build Coastguard Worker SkAlphaType alphaType,
287*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& matrix,
288*c8dee2aaSAndroid Build Coastguard Worker Wrap wx,
289*c8dee2aaSAndroid Build Coastguard Worker Wrap wy,
290*c8dee2aaSAndroid Build Coastguard Worker const SkRect& subset,
291*c8dee2aaSAndroid Build Coastguard Worker const SkRect* domain,
292*c8dee2aaSAndroid Build Coastguard Worker SkVector inset,
293*c8dee2aaSAndroid Build Coastguard Worker const GrCaps& caps,
294*c8dee2aaSAndroid Build Coastguard Worker const float border[4]) {
295*c8dee2aaSAndroid Build Coastguard Worker GrSamplerState sampler(wx, wy, Filter::kLinear);
296*c8dee2aaSAndroid Build Coastguard Worker Sampling sampling(*view.proxy(), sampler, subset, domain, border, false, caps, inset);
297*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> te(new GrTextureEffect(std::move(view),
298*c8dee2aaSAndroid Build Coastguard Worker alphaType,
299*c8dee2aaSAndroid Build Coastguard Worker sampling));
300*c8dee2aaSAndroid Build Coastguard Worker return GrMatrixEffect::Make(matrix, std::move(te));
301*c8dee2aaSAndroid Build Coastguard Worker }
302*c8dee2aaSAndroid Build Coastguard Worker
coordAdjustmentMatrix() const303*c8dee2aaSAndroid Build Coastguard Worker SkMatrix GrTextureEffect::coordAdjustmentMatrix() const {
304*c8dee2aaSAndroid Build Coastguard Worker SkMatrix m;
305*c8dee2aaSAndroid Build Coastguard Worker GrTexture* texture = this->texture();
306*c8dee2aaSAndroid Build Coastguard Worker SkISize d = texture->dimensions();
307*c8dee2aaSAndroid Build Coastguard Worker if (this->matrixEffectShouldNormalize()) {
308*c8dee2aaSAndroid Build Coastguard Worker if (fView.origin() == kBottomLeft_GrSurfaceOrigin) {
309*c8dee2aaSAndroid Build Coastguard Worker m.setScaleTranslate(1.f / d.width(), -1.f / d.height(), 0, 1);
310*c8dee2aaSAndroid Build Coastguard Worker } else {
311*c8dee2aaSAndroid Build Coastguard Worker m.setScale(1.f / d.width(), 1.f / d.height());
312*c8dee2aaSAndroid Build Coastguard Worker }
313*c8dee2aaSAndroid Build Coastguard Worker } else {
314*c8dee2aaSAndroid Build Coastguard Worker if (fView.origin() == kBottomLeft_GrSurfaceOrigin) {
315*c8dee2aaSAndroid Build Coastguard Worker m.setScaleTranslate(1.f, -1.f, 0, d.height());
316*c8dee2aaSAndroid Build Coastguard Worker }
317*c8dee2aaSAndroid Build Coastguard Worker }
318*c8dee2aaSAndroid Build Coastguard Worker return m;
319*c8dee2aaSAndroid Build Coastguard Worker }
320*c8dee2aaSAndroid Build Coastguard Worker
GetShaderMode(Wrap wrap,Filter filter,MipmapMode mm)321*c8dee2aaSAndroid Build Coastguard Worker GrTextureEffect::ShaderMode GrTextureEffect::GetShaderMode(Wrap wrap,
322*c8dee2aaSAndroid Build Coastguard Worker Filter filter,
323*c8dee2aaSAndroid Build Coastguard Worker MipmapMode mm) {
324*c8dee2aaSAndroid Build Coastguard Worker switch (wrap) {
325*c8dee2aaSAndroid Build Coastguard Worker case Wrap::kMirrorRepeat:
326*c8dee2aaSAndroid Build Coastguard Worker return ShaderMode::kMirrorRepeat;
327*c8dee2aaSAndroid Build Coastguard Worker case Wrap::kClamp:
328*c8dee2aaSAndroid Build Coastguard Worker return ShaderMode::kClamp;
329*c8dee2aaSAndroid Build Coastguard Worker case Wrap::kRepeat:
330*c8dee2aaSAndroid Build Coastguard Worker switch (mm) {
331*c8dee2aaSAndroid Build Coastguard Worker case MipmapMode::kNone:
332*c8dee2aaSAndroid Build Coastguard Worker switch (filter) {
333*c8dee2aaSAndroid Build Coastguard Worker case Filter::kNearest: return ShaderMode::kRepeat_Nearest_None;
334*c8dee2aaSAndroid Build Coastguard Worker case Filter::kLinear: return ShaderMode::kRepeat_Linear_None;
335*c8dee2aaSAndroid Build Coastguard Worker }
336*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
337*c8dee2aaSAndroid Build Coastguard Worker case MipmapMode::kNearest:
338*c8dee2aaSAndroid Build Coastguard Worker case MipmapMode::kLinear:
339*c8dee2aaSAndroid Build Coastguard Worker switch (filter) {
340*c8dee2aaSAndroid Build Coastguard Worker case Filter::kNearest: return ShaderMode::kRepeat_Nearest_Mipmap;
341*c8dee2aaSAndroid Build Coastguard Worker case Filter::kLinear: return ShaderMode::kRepeat_Linear_Mipmap;
342*c8dee2aaSAndroid Build Coastguard Worker }
343*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
344*c8dee2aaSAndroid Build Coastguard Worker }
345*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
346*c8dee2aaSAndroid Build Coastguard Worker case Wrap::kClampToBorder:
347*c8dee2aaSAndroid Build Coastguard Worker return filter == Filter::kNearest ? ShaderMode::kClampToBorder_Nearest
348*c8dee2aaSAndroid Build Coastguard Worker : ShaderMode::kClampToBorder_Filter;
349*c8dee2aaSAndroid Build Coastguard Worker }
350*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
351*c8dee2aaSAndroid Build Coastguard Worker }
352*c8dee2aaSAndroid Build Coastguard Worker
ShaderModeIsClampToBorder(ShaderMode m)353*c8dee2aaSAndroid Build Coastguard Worker inline bool GrTextureEffect::ShaderModeIsClampToBorder(ShaderMode m) {
354*c8dee2aaSAndroid Build Coastguard Worker return m == ShaderMode::kClampToBorder_Nearest || m == ShaderMode::kClampToBorder_Filter;
355*c8dee2aaSAndroid Build Coastguard Worker }
356*c8dee2aaSAndroid Build Coastguard Worker
ShaderModeRequiresUnormCoord(ShaderMode m)357*c8dee2aaSAndroid Build Coastguard Worker bool GrTextureEffect::ShaderModeRequiresUnormCoord(ShaderMode m) {
358*c8dee2aaSAndroid Build Coastguard Worker switch (m) {
359*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kNone: return false;
360*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kClamp: return false;
361*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kRepeat_Nearest_None: return false;
362*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kRepeat_Linear_None: return true;
363*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kRepeat_Nearest_Mipmap: return true;
364*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kRepeat_Linear_Mipmap: return true;
365*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kMirrorRepeat: return false;
366*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kClampToBorder_Nearest: return true;
367*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kClampToBorder_Filter: return true;
368*c8dee2aaSAndroid Build Coastguard Worker }
369*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
370*c8dee2aaSAndroid Build Coastguard Worker }
371*c8dee2aaSAndroid Build Coastguard Worker
emitCode(EmitArgs & args)372*c8dee2aaSAndroid Build Coastguard Worker void GrTextureEffect::Impl::emitCode(EmitArgs& args) {
373*c8dee2aaSAndroid Build Coastguard Worker using ShaderMode = GrTextureEffect::ShaderMode;
374*c8dee2aaSAndroid Build Coastguard Worker
375*c8dee2aaSAndroid Build Coastguard Worker auto& te = args.fFp.cast<GrTextureEffect>();
376*c8dee2aaSAndroid Build Coastguard Worker auto* fb = args.fFragBuilder;
377*c8dee2aaSAndroid Build Coastguard Worker
378*c8dee2aaSAndroid Build Coastguard Worker if (te.fShaderModes[0] == ShaderMode::kNone &&
379*c8dee2aaSAndroid Build Coastguard Worker te.fShaderModes[1] == ShaderMode::kNone) {
380*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("return ");
381*c8dee2aaSAndroid Build Coastguard Worker fb->appendTextureLookup(fSamplerHandle, args.fSampleCoord);
382*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf(";");
383*c8dee2aaSAndroid Build Coastguard Worker } else {
384*c8dee2aaSAndroid Build Coastguard Worker // Here is the basic flow of the various ShaderModes are implemented in a series of
385*c8dee2aaSAndroid Build Coastguard Worker // steps. Not all the steps apply to all the modes. We try to emit only the steps
386*c8dee2aaSAndroid Build Coastguard Worker // that are necessary for the given x/y shader modes.
387*c8dee2aaSAndroid Build Coastguard Worker //
388*c8dee2aaSAndroid Build Coastguard Worker // 0) Start with interpolated coordinates (unnormalize if doing anything
389*c8dee2aaSAndroid Build Coastguard Worker // complicated).
390*c8dee2aaSAndroid Build Coastguard Worker // 1) Map the coordinates into the subset range [Repeat and MirrorRepeat], or pass
391*c8dee2aaSAndroid Build Coastguard Worker // through output of 0).
392*c8dee2aaSAndroid Build Coastguard Worker // 2) Clamp the coordinates to a 0.5 inset of the subset rect [Clamp, Repeat, and
393*c8dee2aaSAndroid Build Coastguard Worker // MirrorRepeat always or ClampToBorder only when filtering] or pass through
394*c8dee2aaSAndroid Build Coastguard Worker // output of 1). The clamp rect collapses to a line or point it if the subset
395*c8dee2aaSAndroid Build Coastguard Worker // rect is less than one pixel wide/tall.
396*c8dee2aaSAndroid Build Coastguard Worker // 3) Look up texture with output of 2) [All]
397*c8dee2aaSAndroid Build Coastguard Worker // 3) Use the difference between 1) and 2) to apply filtering at edge [Repeat or
398*c8dee2aaSAndroid Build Coastguard Worker // ClampToBorder]. In the Repeat case this requires extra texture lookups on the
399*c8dee2aaSAndroid Build Coastguard Worker // other side of the subset (up to 3 more reads). Or if ClampToBorder and not
400*c8dee2aaSAndroid Build Coastguard Worker // filtering do a hard less than/greater than test with the subset rect.
401*c8dee2aaSAndroid Build Coastguard Worker
402*c8dee2aaSAndroid Build Coastguard Worker // Convert possible projective texture coordinates into non-homogeneous half2.
403*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("float2 inCoord = %s;", args.fSampleCoord);
404*c8dee2aaSAndroid Build Coastguard Worker
405*c8dee2aaSAndroid Build Coastguard Worker const auto& m = te.fShaderModes;
406*c8dee2aaSAndroid Build Coastguard Worker
407*c8dee2aaSAndroid Build Coastguard Worker const char* borderName = nullptr;
408*c8dee2aaSAndroid Build Coastguard Worker if (te.hasClampToBorderShaderMode()) {
409*c8dee2aaSAndroid Build Coastguard Worker fBorderUni = args.fUniformHandler->addUniform(
410*c8dee2aaSAndroid Build Coastguard Worker &te, kFragment_GrShaderFlag, SkSLType::kHalf4, "border", &borderName);
411*c8dee2aaSAndroid Build Coastguard Worker }
412*c8dee2aaSAndroid Build Coastguard Worker auto modeUsesSubset = [](ShaderMode m) {
413*c8dee2aaSAndroid Build Coastguard Worker switch (m) {
414*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kNone: return false;
415*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kClamp: return false;
416*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kRepeat_Nearest_None: return true;
417*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kRepeat_Linear_None: return true;
418*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kRepeat_Nearest_Mipmap: return true;
419*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kRepeat_Linear_Mipmap: return true;
420*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kMirrorRepeat: return true;
421*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kClampToBorder_Nearest: return true;
422*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kClampToBorder_Filter: return true;
423*c8dee2aaSAndroid Build Coastguard Worker }
424*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
425*c8dee2aaSAndroid Build Coastguard Worker };
426*c8dee2aaSAndroid Build Coastguard Worker
427*c8dee2aaSAndroid Build Coastguard Worker auto modeUsesClamp = [](ShaderMode m) {
428*c8dee2aaSAndroid Build Coastguard Worker switch (m) {
429*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kNone: return false;
430*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kClamp: return true;
431*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kRepeat_Nearest_None: return true;
432*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kRepeat_Linear_None: return true;
433*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kRepeat_Nearest_Mipmap: return true;
434*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kRepeat_Linear_Mipmap: return true;
435*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kMirrorRepeat: return true;
436*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kClampToBorder_Nearest: return false;
437*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kClampToBorder_Filter: return true;
438*c8dee2aaSAndroid Build Coastguard Worker }
439*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
440*c8dee2aaSAndroid Build Coastguard Worker };
441*c8dee2aaSAndroid Build Coastguard Worker
442*c8dee2aaSAndroid Build Coastguard Worker bool useSubset[2] = {modeUsesSubset(m[0]), modeUsesSubset(m[1])};
443*c8dee2aaSAndroid Build Coastguard Worker bool useClamp [2] = {modeUsesClamp (m[0]), modeUsesClamp (m[1])};
444*c8dee2aaSAndroid Build Coastguard Worker
445*c8dee2aaSAndroid Build Coastguard Worker const char* subsetName = nullptr;
446*c8dee2aaSAndroid Build Coastguard Worker if (useSubset[0] || useSubset[1]) {
447*c8dee2aaSAndroid Build Coastguard Worker fSubsetUni = args.fUniformHandler->addUniform(
448*c8dee2aaSAndroid Build Coastguard Worker &te, kFragment_GrShaderFlag, SkSLType::kFloat4, "subset", &subsetName);
449*c8dee2aaSAndroid Build Coastguard Worker }
450*c8dee2aaSAndroid Build Coastguard Worker
451*c8dee2aaSAndroid Build Coastguard Worker const char* clampName = nullptr;
452*c8dee2aaSAndroid Build Coastguard Worker if (useClamp[0] || useClamp[1]) {
453*c8dee2aaSAndroid Build Coastguard Worker fClampUni = args.fUniformHandler->addUniform(
454*c8dee2aaSAndroid Build Coastguard Worker &te, kFragment_GrShaderFlag, SkSLType::kFloat4, "clamp", &clampName);
455*c8dee2aaSAndroid Build Coastguard Worker }
456*c8dee2aaSAndroid Build Coastguard Worker
457*c8dee2aaSAndroid Build Coastguard Worker bool unormCoordsRequiredForShaderMode = ShaderModeRequiresUnormCoord(m[0]) ||
458*c8dee2aaSAndroid Build Coastguard Worker ShaderModeRequiresUnormCoord(m[1]);
459*c8dee2aaSAndroid Build Coastguard Worker // We should not pre-normalize the input coords with GrMatrixEffect if we're going to
460*c8dee2aaSAndroid Build Coastguard Worker // operate on unnormalized coords and then normalize after the shader mode.
461*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!(unormCoordsRequiredForShaderMode && te.matrixEffectShouldNormalize()));
462*c8dee2aaSAndroid Build Coastguard Worker bool sampleCoordsMustBeNormalized =
463*c8dee2aaSAndroid Build Coastguard Worker te.fView.asTextureProxy()->textureType() != GrTextureType::kRectangle;
464*c8dee2aaSAndroid Build Coastguard Worker
465*c8dee2aaSAndroid Build Coastguard Worker const char* idims = nullptr;
466*c8dee2aaSAndroid Build Coastguard Worker if (unormCoordsRequiredForShaderMode && sampleCoordsMustBeNormalized) {
467*c8dee2aaSAndroid Build Coastguard Worker // TODO: Detect support for textureSize() or polyfill textureSize() in SkSL and
468*c8dee2aaSAndroid Build Coastguard Worker // always use?
469*c8dee2aaSAndroid Build Coastguard Worker fIDimsUni = args.fUniformHandler->addUniform(&te, kFragment_GrShaderFlag,
470*c8dee2aaSAndroid Build Coastguard Worker SkSLType::kFloat2, "idims", &idims);
471*c8dee2aaSAndroid Build Coastguard Worker }
472*c8dee2aaSAndroid Build Coastguard Worker
473*c8dee2aaSAndroid Build Coastguard Worker // Generates a string to read at a coordinate, normalizing coords if necessary.
474*c8dee2aaSAndroid Build Coastguard Worker auto read = [&](const char* coord) {
475*c8dee2aaSAndroid Build Coastguard Worker SkString result;
476*c8dee2aaSAndroid Build Coastguard Worker SkString normCoord;
477*c8dee2aaSAndroid Build Coastguard Worker if (idims) {
478*c8dee2aaSAndroid Build Coastguard Worker normCoord.printf("(%s) * %s", coord, idims);
479*c8dee2aaSAndroid Build Coastguard Worker } else {
480*c8dee2aaSAndroid Build Coastguard Worker normCoord = coord;
481*c8dee2aaSAndroid Build Coastguard Worker }
482*c8dee2aaSAndroid Build Coastguard Worker fb->appendTextureLookup(&result, fSamplerHandle, normCoord.c_str());
483*c8dee2aaSAndroid Build Coastguard Worker return result;
484*c8dee2aaSAndroid Build Coastguard Worker };
485*c8dee2aaSAndroid Build Coastguard Worker
486*c8dee2aaSAndroid Build Coastguard Worker // Implements coord wrapping for kRepeat and kMirrorRepeat
487*c8dee2aaSAndroid Build Coastguard Worker auto subsetCoord = [&](ShaderMode mode,
488*c8dee2aaSAndroid Build Coastguard Worker const char* coordSwizzle,
489*c8dee2aaSAndroid Build Coastguard Worker const char* subsetStartSwizzle,
490*c8dee2aaSAndroid Build Coastguard Worker const char* subsetStopSwizzle,
491*c8dee2aaSAndroid Build Coastguard Worker const char* extraCoord,
492*c8dee2aaSAndroid Build Coastguard Worker const char* coordWeight) {
493*c8dee2aaSAndroid Build Coastguard Worker switch (mode) {
494*c8dee2aaSAndroid Build Coastguard Worker // These modes either don't use the subset rect or don't need to map the
495*c8dee2aaSAndroid Build Coastguard Worker // coords to be within the subset.
496*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kNone:
497*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kClampToBorder_Nearest:
498*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kClampToBorder_Filter:
499*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kClamp:
500*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("subsetCoord.%s = inCoord.%s;", coordSwizzle, coordSwizzle);
501*c8dee2aaSAndroid Build Coastguard Worker break;
502*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kRepeat_Nearest_None:
503*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kRepeat_Linear_None:
504*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf(
505*c8dee2aaSAndroid Build Coastguard Worker "subsetCoord.%s = mod(inCoord.%s - %s.%s, %s.%s - %s.%s) + %s.%s;",
506*c8dee2aaSAndroid Build Coastguard Worker coordSwizzle, coordSwizzle, subsetName, subsetStartSwizzle, subsetName,
507*c8dee2aaSAndroid Build Coastguard Worker subsetStopSwizzle, subsetName, subsetStartSwizzle, subsetName,
508*c8dee2aaSAndroid Build Coastguard Worker subsetStartSwizzle);
509*c8dee2aaSAndroid Build Coastguard Worker break;
510*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kRepeat_Nearest_Mipmap:
511*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kRepeat_Linear_Mipmap:
512*c8dee2aaSAndroid Build Coastguard Worker // The approach here is to generate two sets of texture coords that
513*c8dee2aaSAndroid Build Coastguard Worker // are both "moving" at the same speed (if not direction) as
514*c8dee2aaSAndroid Build Coastguard Worker // inCoords. We accomplish that by using two out of phase mirror
515*c8dee2aaSAndroid Build Coastguard Worker // repeat coords. We will always sample using both coords but the
516*c8dee2aaSAndroid Build Coastguard Worker // read from the upward sloping one is selected using a weight
517*c8dee2aaSAndroid Build Coastguard Worker // that transitions from one set to the other near the reflection
518*c8dee2aaSAndroid Build Coastguard Worker // point. Like the coords, the weight is a saw-tooth function,
519*c8dee2aaSAndroid Build Coastguard Worker // phase-shifted, vertically translated, and then clamped to 0..1.
520*c8dee2aaSAndroid Build Coastguard Worker // TODO: Skip this and use textureGrad() when available.
521*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(extraCoord);
522*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(coordWeight);
523*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppend("{");
524*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("float w = %s.%s - %s.%s;", subsetName, subsetStopSwizzle,
525*c8dee2aaSAndroid Build Coastguard Worker subsetName, subsetStartSwizzle);
526*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("float w2 = 2 * w;");
527*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("float d = inCoord.%s - %s.%s;", coordSwizzle, subsetName,
528*c8dee2aaSAndroid Build Coastguard Worker subsetStartSwizzle);
529*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppend("float m = mod(d, w2);");
530*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppend("float o = mix(m, w2 - m, step(w, m));");
531*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("subsetCoord.%s = o + %s.%s;", coordSwizzle, subsetName,
532*c8dee2aaSAndroid Build Coastguard Worker subsetStartSwizzle);
533*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("%s = w - o + %s.%s;", extraCoord, subsetName,
534*c8dee2aaSAndroid Build Coastguard Worker subsetStartSwizzle);
535*c8dee2aaSAndroid Build Coastguard Worker // coordWeight is used as the third param of mix() to blend between a
536*c8dee2aaSAndroid Build Coastguard Worker // sample taken using subsetCoord and a sample at extraCoord.
537*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppend("float hw = w/2;");
538*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppend("float n = mod(d - hw, w2);");
539*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("%s = saturate(half(mix(n, w2 - n, step(w, n)) - hw + 0.5));",
540*c8dee2aaSAndroid Build Coastguard Worker coordWeight);
541*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppend("}");
542*c8dee2aaSAndroid Build Coastguard Worker break;
543*c8dee2aaSAndroid Build Coastguard Worker case ShaderMode::kMirrorRepeat:
544*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppend("{");
545*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("float w = %s.%s - %s.%s;", subsetName, subsetStopSwizzle,
546*c8dee2aaSAndroid Build Coastguard Worker subsetName, subsetStartSwizzle);
547*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("float w2 = 2 * w;");
548*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("float m = mod(inCoord.%s - %s.%s, w2);", coordSwizzle,
549*c8dee2aaSAndroid Build Coastguard Worker subsetName, subsetStartSwizzle);
550*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("subsetCoord.%s = mix(m, w2 - m, step(w, m)) + %s.%s;",
551*c8dee2aaSAndroid Build Coastguard Worker coordSwizzle, subsetName, subsetStartSwizzle);
552*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppend("}");
553*c8dee2aaSAndroid Build Coastguard Worker break;
554*c8dee2aaSAndroid Build Coastguard Worker }
555*c8dee2aaSAndroid Build Coastguard Worker };
556*c8dee2aaSAndroid Build Coastguard Worker
557*c8dee2aaSAndroid Build Coastguard Worker auto clampCoord = [&](bool clamp,
558*c8dee2aaSAndroid Build Coastguard Worker const char* coordSwizzle,
559*c8dee2aaSAndroid Build Coastguard Worker const char* clampStartSwizzle,
560*c8dee2aaSAndroid Build Coastguard Worker const char* clampStopSwizzle) {
561*c8dee2aaSAndroid Build Coastguard Worker if (clamp) {
562*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("clampedCoord%s = clamp(subsetCoord%s, %s%s, %s%s);",
563*c8dee2aaSAndroid Build Coastguard Worker coordSwizzle, coordSwizzle,
564*c8dee2aaSAndroid Build Coastguard Worker clampName, clampStartSwizzle,
565*c8dee2aaSAndroid Build Coastguard Worker clampName, clampStopSwizzle);
566*c8dee2aaSAndroid Build Coastguard Worker } else {
567*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("clampedCoord%s = subsetCoord%s;", coordSwizzle, coordSwizzle);
568*c8dee2aaSAndroid Build Coastguard Worker }
569*c8dee2aaSAndroid Build Coastguard Worker };
570*c8dee2aaSAndroid Build Coastguard Worker
571*c8dee2aaSAndroid Build Coastguard Worker // Insert vars for extra coords and blending weights for repeat + mip map.
572*c8dee2aaSAndroid Build Coastguard Worker const char* extraRepeatCoordX = nullptr;
573*c8dee2aaSAndroid Build Coastguard Worker const char* repeatCoordWeightX = nullptr;
574*c8dee2aaSAndroid Build Coastguard Worker const char* extraRepeatCoordY = nullptr;
575*c8dee2aaSAndroid Build Coastguard Worker const char* repeatCoordWeightY = nullptr;
576*c8dee2aaSAndroid Build Coastguard Worker
577*c8dee2aaSAndroid Build Coastguard Worker bool mipmapRepeatX = m[0] == ShaderMode::kRepeat_Nearest_Mipmap ||
578*c8dee2aaSAndroid Build Coastguard Worker m[0] == ShaderMode::kRepeat_Linear_Mipmap;
579*c8dee2aaSAndroid Build Coastguard Worker bool mipmapRepeatY = m[1] == ShaderMode::kRepeat_Nearest_Mipmap ||
580*c8dee2aaSAndroid Build Coastguard Worker m[1] == ShaderMode::kRepeat_Linear_Mipmap;
581*c8dee2aaSAndroid Build Coastguard Worker
582*c8dee2aaSAndroid Build Coastguard Worker if (mipmapRepeatX || mipmapRepeatY) {
583*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppend("float2 extraRepeatCoord;");
584*c8dee2aaSAndroid Build Coastguard Worker }
585*c8dee2aaSAndroid Build Coastguard Worker if (mipmapRepeatX) {
586*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppend("half repeatCoordWeightX;");
587*c8dee2aaSAndroid Build Coastguard Worker extraRepeatCoordX = "extraRepeatCoord.x";
588*c8dee2aaSAndroid Build Coastguard Worker repeatCoordWeightX = "repeatCoordWeightX";
589*c8dee2aaSAndroid Build Coastguard Worker }
590*c8dee2aaSAndroid Build Coastguard Worker if (mipmapRepeatY) {
591*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppend("half repeatCoordWeightY;");
592*c8dee2aaSAndroid Build Coastguard Worker extraRepeatCoordY = "extraRepeatCoord.y";
593*c8dee2aaSAndroid Build Coastguard Worker repeatCoordWeightY = "repeatCoordWeightY";
594*c8dee2aaSAndroid Build Coastguard Worker }
595*c8dee2aaSAndroid Build Coastguard Worker
596*c8dee2aaSAndroid Build Coastguard Worker // Apply subset rect and clamp rect to coords.
597*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppend("float2 subsetCoord;");
598*c8dee2aaSAndroid Build Coastguard Worker subsetCoord(te.fShaderModes[0], "x", "x", "z", extraRepeatCoordX, repeatCoordWeightX);
599*c8dee2aaSAndroid Build Coastguard Worker subsetCoord(te.fShaderModes[1], "y", "y", "w", extraRepeatCoordY, repeatCoordWeightY);
600*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppend("float2 clampedCoord;");
601*c8dee2aaSAndroid Build Coastguard Worker if (useClamp[0] == useClamp[1]) {
602*c8dee2aaSAndroid Build Coastguard Worker clampCoord(useClamp[0], "", ".xy", ".zw");
603*c8dee2aaSAndroid Build Coastguard Worker } else {
604*c8dee2aaSAndroid Build Coastguard Worker clampCoord(useClamp[0], ".x", ".x", ".z");
605*c8dee2aaSAndroid Build Coastguard Worker clampCoord(useClamp[1], ".y", ".y", ".w");
606*c8dee2aaSAndroid Build Coastguard Worker }
607*c8dee2aaSAndroid Build Coastguard Worker // Additional clamping for the extra coords for kRepeat with mip maps.
608*c8dee2aaSAndroid Build Coastguard Worker if (mipmapRepeatX && mipmapRepeatY) {
609*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("extraRepeatCoord = clamp(extraRepeatCoord, %s.xy, %s.zw);",
610*c8dee2aaSAndroid Build Coastguard Worker clampName, clampName);
611*c8dee2aaSAndroid Build Coastguard Worker } else if (mipmapRepeatX) {
612*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("extraRepeatCoord.x = clamp(extraRepeatCoord.x, %s.x, %s.z);",
613*c8dee2aaSAndroid Build Coastguard Worker clampName, clampName);
614*c8dee2aaSAndroid Build Coastguard Worker } else if (mipmapRepeatY) {
615*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("extraRepeatCoord.y = clamp(extraRepeatCoord.y, %s.y, %s.w);",
616*c8dee2aaSAndroid Build Coastguard Worker clampName, clampName);
617*c8dee2aaSAndroid Build Coastguard Worker }
618*c8dee2aaSAndroid Build Coastguard Worker
619*c8dee2aaSAndroid Build Coastguard Worker // Do the 2 or 4 texture reads for kRepeatMipMap and then apply the weight(s)
620*c8dee2aaSAndroid Build Coastguard Worker // to blend between them. If neither direction is repeat or not using mip maps do a single
621*c8dee2aaSAndroid Build Coastguard Worker // read at clampedCoord.
622*c8dee2aaSAndroid Build Coastguard Worker if (mipmapRepeatX && mipmapRepeatY) {
623*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf(
624*c8dee2aaSAndroid Build Coastguard Worker "half4 textureColor ="
625*c8dee2aaSAndroid Build Coastguard Worker " mix(mix(%s, %s, repeatCoordWeightX),"
626*c8dee2aaSAndroid Build Coastguard Worker " mix(%s, %s, repeatCoordWeightX),"
627*c8dee2aaSAndroid Build Coastguard Worker " repeatCoordWeightY);",
628*c8dee2aaSAndroid Build Coastguard Worker read("clampedCoord").c_str(),
629*c8dee2aaSAndroid Build Coastguard Worker read("float2(extraRepeatCoord.x, clampedCoord.y)").c_str(),
630*c8dee2aaSAndroid Build Coastguard Worker read("float2(clampedCoord.x, extraRepeatCoord.y)").c_str(),
631*c8dee2aaSAndroid Build Coastguard Worker read("float2(extraRepeatCoord.x, extraRepeatCoord.y)").c_str());
632*c8dee2aaSAndroid Build Coastguard Worker
633*c8dee2aaSAndroid Build Coastguard Worker } else if (mipmapRepeatX) {
634*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("half4 textureColor = mix(%s, %s, repeatCoordWeightX);",
635*c8dee2aaSAndroid Build Coastguard Worker read("clampedCoord").c_str(),
636*c8dee2aaSAndroid Build Coastguard Worker read("float2(extraRepeatCoord.x, clampedCoord.y)").c_str());
637*c8dee2aaSAndroid Build Coastguard Worker } else if (mipmapRepeatY) {
638*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("half4 textureColor = mix(%s, %s, repeatCoordWeightY);",
639*c8dee2aaSAndroid Build Coastguard Worker read("clampedCoord").c_str(),
640*c8dee2aaSAndroid Build Coastguard Worker read("float2(clampedCoord.x, extraRepeatCoord.y)").c_str());
641*c8dee2aaSAndroid Build Coastguard Worker } else {
642*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("half4 textureColor = %s;", read("clampedCoord").c_str());
643*c8dee2aaSAndroid Build Coastguard Worker }
644*c8dee2aaSAndroid Build Coastguard Worker
645*c8dee2aaSAndroid Build Coastguard Worker // Strings for extra texture reads used only in kRepeatLinear
646*c8dee2aaSAndroid Build Coastguard Worker SkString repeatLinearReadX;
647*c8dee2aaSAndroid Build Coastguard Worker SkString repeatLinearReadY;
648*c8dee2aaSAndroid Build Coastguard Worker
649*c8dee2aaSAndroid Build Coastguard Worker // Calculate the amount the coord moved for clamping. This will be used
650*c8dee2aaSAndroid Build Coastguard Worker // to implement shader-based filtering for kClampToBorder and kRepeat.
651*c8dee2aaSAndroid Build Coastguard Worker bool repeatLinearFilterX = m[0] == ShaderMode::kRepeat_Linear_None ||
652*c8dee2aaSAndroid Build Coastguard Worker m[0] == ShaderMode::kRepeat_Linear_Mipmap;
653*c8dee2aaSAndroid Build Coastguard Worker bool repeatLinearFilterY = m[1] == ShaderMode::kRepeat_Linear_None ||
654*c8dee2aaSAndroid Build Coastguard Worker m[1] == ShaderMode::kRepeat_Linear_Mipmap;
655*c8dee2aaSAndroid Build Coastguard Worker if (repeatLinearFilterX || m[0] == ShaderMode::kClampToBorder_Filter) {
656*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppend("half errX = half(subsetCoord.x - clampedCoord.x);");
657*c8dee2aaSAndroid Build Coastguard Worker if (repeatLinearFilterX) {
658*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("float repeatCoordX = errX > 0 ? %s.x : %s.z;",
659*c8dee2aaSAndroid Build Coastguard Worker clampName, clampName);
660*c8dee2aaSAndroid Build Coastguard Worker repeatLinearReadX = read("float2(repeatCoordX, clampedCoord.y)");
661*c8dee2aaSAndroid Build Coastguard Worker }
662*c8dee2aaSAndroid Build Coastguard Worker }
663*c8dee2aaSAndroid Build Coastguard Worker if (repeatLinearFilterY || m[1] == ShaderMode::kClampToBorder_Filter) {
664*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppend("half errY = half(subsetCoord.y - clampedCoord.y);");
665*c8dee2aaSAndroid Build Coastguard Worker if (repeatLinearFilterY) {
666*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("float repeatCoordY = errY > 0 ? %s.y : %s.w;",
667*c8dee2aaSAndroid Build Coastguard Worker clampName, clampName);
668*c8dee2aaSAndroid Build Coastguard Worker repeatLinearReadY = read("float2(clampedCoord.x, repeatCoordY)");
669*c8dee2aaSAndroid Build Coastguard Worker }
670*c8dee2aaSAndroid Build Coastguard Worker }
671*c8dee2aaSAndroid Build Coastguard Worker
672*c8dee2aaSAndroid Build Coastguard Worker // Add logic for kRepeat + linear filter. Do 1 or 3 more texture reads depending
673*c8dee2aaSAndroid Build Coastguard Worker // on whether both modes are kRepeat and whether we're near a single subset edge
674*c8dee2aaSAndroid Build Coastguard Worker // or a corner. Then blend the multiple reads using the err values calculated
675*c8dee2aaSAndroid Build Coastguard Worker // above.
676*c8dee2aaSAndroid Build Coastguard Worker const char* ifStr = "if";
677*c8dee2aaSAndroid Build Coastguard Worker if (repeatLinearFilterX && repeatLinearFilterY) {
678*c8dee2aaSAndroid Build Coastguard Worker auto repeatLinearReadXY = read("float2(repeatCoordX, repeatCoordY)");
679*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf(
680*c8dee2aaSAndroid Build Coastguard Worker "if (errX != 0 && errY != 0) {"
681*c8dee2aaSAndroid Build Coastguard Worker " errX = abs(errX);"
682*c8dee2aaSAndroid Build Coastguard Worker " textureColor = mix(mix(textureColor, %s, errX),"
683*c8dee2aaSAndroid Build Coastguard Worker " mix(%s, %s, errX),"
684*c8dee2aaSAndroid Build Coastguard Worker " abs(errY));"
685*c8dee2aaSAndroid Build Coastguard Worker "}",
686*c8dee2aaSAndroid Build Coastguard Worker repeatLinearReadX.c_str(), repeatLinearReadY.c_str(),
687*c8dee2aaSAndroid Build Coastguard Worker repeatLinearReadXY.c_str());
688*c8dee2aaSAndroid Build Coastguard Worker ifStr = "else if";
689*c8dee2aaSAndroid Build Coastguard Worker }
690*c8dee2aaSAndroid Build Coastguard Worker if (repeatLinearFilterX) {
691*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf(
692*c8dee2aaSAndroid Build Coastguard Worker "%s (errX != 0) {"
693*c8dee2aaSAndroid Build Coastguard Worker " textureColor = mix(textureColor, %s, abs(errX));"
694*c8dee2aaSAndroid Build Coastguard Worker "}",
695*c8dee2aaSAndroid Build Coastguard Worker ifStr, repeatLinearReadX.c_str());
696*c8dee2aaSAndroid Build Coastguard Worker }
697*c8dee2aaSAndroid Build Coastguard Worker if (repeatLinearFilterY) {
698*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf(
699*c8dee2aaSAndroid Build Coastguard Worker "%s (errY != 0) {"
700*c8dee2aaSAndroid Build Coastguard Worker " textureColor = mix(textureColor, %s, abs(errY));"
701*c8dee2aaSAndroid Build Coastguard Worker "}",
702*c8dee2aaSAndroid Build Coastguard Worker ifStr, repeatLinearReadY.c_str());
703*c8dee2aaSAndroid Build Coastguard Worker }
704*c8dee2aaSAndroid Build Coastguard Worker
705*c8dee2aaSAndroid Build Coastguard Worker // Do soft edge shader filtering against border color for kClampToBorderFilter using
706*c8dee2aaSAndroid Build Coastguard Worker // the err values calculated above.
707*c8dee2aaSAndroid Build Coastguard Worker if (m[0] == ShaderMode::kClampToBorder_Filter) {
708*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("textureColor = mix(textureColor, %s, min(abs(errX), 1));", borderName);
709*c8dee2aaSAndroid Build Coastguard Worker }
710*c8dee2aaSAndroid Build Coastguard Worker if (m[1] == ShaderMode::kClampToBorder_Filter) {
711*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("textureColor = mix(textureColor, %s, min(abs(errY), 1));", borderName);
712*c8dee2aaSAndroid Build Coastguard Worker }
713*c8dee2aaSAndroid Build Coastguard Worker
714*c8dee2aaSAndroid Build Coastguard Worker // Do hard-edge shader transition to border color for kClampToBorderNearest at the
715*c8dee2aaSAndroid Build Coastguard Worker // subset boundaries. Snap the input coordinates to nearest neighbor (with an
716*c8dee2aaSAndroid Build Coastguard Worker // epsilon) before comparing to the subset rect to avoid GPU interpolation errors
717*c8dee2aaSAndroid Build Coastguard Worker if (m[0] == ShaderMode::kClampToBorder_Nearest) {
718*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf(
719*c8dee2aaSAndroid Build Coastguard Worker "float snappedX = floor(inCoord.x + 0.001) + 0.5;"
720*c8dee2aaSAndroid Build Coastguard Worker "if (snappedX < %s.x || snappedX > %s.z) {"
721*c8dee2aaSAndroid Build Coastguard Worker " textureColor = %s;"
722*c8dee2aaSAndroid Build Coastguard Worker "}",
723*c8dee2aaSAndroid Build Coastguard Worker subsetName, subsetName, borderName);
724*c8dee2aaSAndroid Build Coastguard Worker }
725*c8dee2aaSAndroid Build Coastguard Worker if (m[1] == ShaderMode::kClampToBorder_Nearest) {
726*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf(
727*c8dee2aaSAndroid Build Coastguard Worker "float snappedY = floor(inCoord.y + 0.001) + 0.5;"
728*c8dee2aaSAndroid Build Coastguard Worker "if (snappedY < %s.y || snappedY > %s.w) {"
729*c8dee2aaSAndroid Build Coastguard Worker " textureColor = %s;"
730*c8dee2aaSAndroid Build Coastguard Worker "}",
731*c8dee2aaSAndroid Build Coastguard Worker subsetName, subsetName, borderName);
732*c8dee2aaSAndroid Build Coastguard Worker }
733*c8dee2aaSAndroid Build Coastguard Worker fb->codeAppendf("return textureColor;");
734*c8dee2aaSAndroid Build Coastguard Worker }
735*c8dee2aaSAndroid Build Coastguard Worker }
736*c8dee2aaSAndroid Build Coastguard Worker
onSetData(const GrGLSLProgramDataManager & pdm,const GrFragmentProcessor & fp)737*c8dee2aaSAndroid Build Coastguard Worker void GrTextureEffect::Impl::onSetData(const GrGLSLProgramDataManager& pdm,
738*c8dee2aaSAndroid Build Coastguard Worker const GrFragmentProcessor& fp) {
739*c8dee2aaSAndroid Build Coastguard Worker const auto& te = fp.cast<GrTextureEffect>();
740*c8dee2aaSAndroid Build Coastguard Worker
741*c8dee2aaSAndroid Build Coastguard Worker const float w = te.texture()->width();
742*c8dee2aaSAndroid Build Coastguard Worker const float h = te.texture()->height();
743*c8dee2aaSAndroid Build Coastguard Worker const auto& s = te.fSubset;
744*c8dee2aaSAndroid Build Coastguard Worker const auto& c = te.fClamp;
745*c8dee2aaSAndroid Build Coastguard Worker
746*c8dee2aaSAndroid Build Coastguard Worker auto type = te.texture()->textureType();
747*c8dee2aaSAndroid Build Coastguard Worker
748*c8dee2aaSAndroid Build Coastguard Worker float idims[2] = {1.f/w, 1.f/h};
749*c8dee2aaSAndroid Build Coastguard Worker
750*c8dee2aaSAndroid Build Coastguard Worker if (fIDimsUni.isValid()) {
751*c8dee2aaSAndroid Build Coastguard Worker pdm.set2fv(fIDimsUni, 1, idims);
752*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(type != GrTextureType::kRectangle);
753*c8dee2aaSAndroid Build Coastguard Worker }
754*c8dee2aaSAndroid Build Coastguard Worker
755*c8dee2aaSAndroid Build Coastguard Worker auto pushRect = [&](float rect[4], UniformHandle uni) {
756*c8dee2aaSAndroid Build Coastguard Worker if (te.view().origin() == kBottomLeft_GrSurfaceOrigin) {
757*c8dee2aaSAndroid Build Coastguard Worker rect[1] = h - rect[1];
758*c8dee2aaSAndroid Build Coastguard Worker rect[3] = h - rect[3];
759*c8dee2aaSAndroid Build Coastguard Worker std::swap(rect[1], rect[3]);
760*c8dee2aaSAndroid Build Coastguard Worker }
761*c8dee2aaSAndroid Build Coastguard Worker if (!fIDimsUni.isValid() && type != GrTextureType::kRectangle) {
762*c8dee2aaSAndroid Build Coastguard Worker rect[0] *= idims[0];
763*c8dee2aaSAndroid Build Coastguard Worker rect[2] *= idims[0];
764*c8dee2aaSAndroid Build Coastguard Worker rect[1] *= idims[1];
765*c8dee2aaSAndroid Build Coastguard Worker rect[3] *= idims[1];
766*c8dee2aaSAndroid Build Coastguard Worker }
767*c8dee2aaSAndroid Build Coastguard Worker pdm.set4fv(uni, 1, rect);
768*c8dee2aaSAndroid Build Coastguard Worker };
769*c8dee2aaSAndroid Build Coastguard Worker
770*c8dee2aaSAndroid Build Coastguard Worker if (fSubsetUni.isValid()) {
771*c8dee2aaSAndroid Build Coastguard Worker float subset[] = {s.fLeft, s.fTop, s.fRight, s.fBottom};
772*c8dee2aaSAndroid Build Coastguard Worker pushRect(subset, fSubsetUni);
773*c8dee2aaSAndroid Build Coastguard Worker }
774*c8dee2aaSAndroid Build Coastguard Worker if (fClampUni.isValid()) {
775*c8dee2aaSAndroid Build Coastguard Worker float subset[] = {c.fLeft, c.fTop, c.fRight, c.fBottom};
776*c8dee2aaSAndroid Build Coastguard Worker pushRect(subset, fClampUni);
777*c8dee2aaSAndroid Build Coastguard Worker }
778*c8dee2aaSAndroid Build Coastguard Worker if (fBorderUni.isValid()) {
779*c8dee2aaSAndroid Build Coastguard Worker pdm.set4fv(fBorderUni, 1, te.fBorder);
780*c8dee2aaSAndroid Build Coastguard Worker }
781*c8dee2aaSAndroid Build Coastguard Worker }
782*c8dee2aaSAndroid Build Coastguard Worker
onMakeProgramImpl() const783*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor::ProgramImpl> GrTextureEffect::onMakeProgramImpl() const {
784*c8dee2aaSAndroid Build Coastguard Worker return std::make_unique<Impl>();
785*c8dee2aaSAndroid Build Coastguard Worker }
786*c8dee2aaSAndroid Build Coastguard Worker
onAddToKey(const GrShaderCaps &,skgpu::KeyBuilder * b) const787*c8dee2aaSAndroid Build Coastguard Worker void GrTextureEffect::onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder* b) const {
788*c8dee2aaSAndroid Build Coastguard Worker auto m0 = static_cast<uint32_t>(fShaderModes[0]);
789*c8dee2aaSAndroid Build Coastguard Worker b->addBits(8, m0, "shaderMode0");
790*c8dee2aaSAndroid Build Coastguard Worker
791*c8dee2aaSAndroid Build Coastguard Worker auto m1 = static_cast<uint32_t>(fShaderModes[1]);
792*c8dee2aaSAndroid Build Coastguard Worker b->addBits(8, m1, "shaderMode1");
793*c8dee2aaSAndroid Build Coastguard Worker }
794*c8dee2aaSAndroid Build Coastguard Worker
onIsEqual(const GrFragmentProcessor & other) const795*c8dee2aaSAndroid Build Coastguard Worker bool GrTextureEffect::onIsEqual(const GrFragmentProcessor& other) const {
796*c8dee2aaSAndroid Build Coastguard Worker auto& that = other.cast<GrTextureEffect>();
797*c8dee2aaSAndroid Build Coastguard Worker if (fView != that.fView) {
798*c8dee2aaSAndroid Build Coastguard Worker return false;
799*c8dee2aaSAndroid Build Coastguard Worker }
800*c8dee2aaSAndroid Build Coastguard Worker if (fSamplerState != that.fSamplerState) {
801*c8dee2aaSAndroid Build Coastguard Worker return false;
802*c8dee2aaSAndroid Build Coastguard Worker }
803*c8dee2aaSAndroid Build Coastguard Worker if (fShaderModes[0] != that.fShaderModes[0] || fShaderModes[1] != that.fShaderModes[1]) {
804*c8dee2aaSAndroid Build Coastguard Worker return false;
805*c8dee2aaSAndroid Build Coastguard Worker }
806*c8dee2aaSAndroid Build Coastguard Worker if (fSubset != that.fSubset) {
807*c8dee2aaSAndroid Build Coastguard Worker return false;
808*c8dee2aaSAndroid Build Coastguard Worker }
809*c8dee2aaSAndroid Build Coastguard Worker if (this->hasClampToBorderShaderMode() && !std::equal(fBorder, fBorder + 4, that.fBorder)) {
810*c8dee2aaSAndroid Build Coastguard Worker return false;
811*c8dee2aaSAndroid Build Coastguard Worker }
812*c8dee2aaSAndroid Build Coastguard Worker return true;
813*c8dee2aaSAndroid Build Coastguard Worker }
814*c8dee2aaSAndroid Build Coastguard Worker
matrixEffectShouldNormalize() const815*c8dee2aaSAndroid Build Coastguard Worker bool GrTextureEffect::matrixEffectShouldNormalize() const {
816*c8dee2aaSAndroid Build Coastguard Worker return fView.asTextureProxy()->textureType() != GrTextureType::kRectangle &&
817*c8dee2aaSAndroid Build Coastguard Worker !ShaderModeRequiresUnormCoord(fShaderModes[0]) &&
818*c8dee2aaSAndroid Build Coastguard Worker !ShaderModeRequiresUnormCoord(fShaderModes[1]);
819*c8dee2aaSAndroid Build Coastguard Worker }
820*c8dee2aaSAndroid Build Coastguard Worker
GrTextureEffect(GrSurfaceProxyView view,SkAlphaType alphaType,const Sampling & sampling)821*c8dee2aaSAndroid Build Coastguard Worker GrTextureEffect::GrTextureEffect(GrSurfaceProxyView view,
822*c8dee2aaSAndroid Build Coastguard Worker SkAlphaType alphaType,
823*c8dee2aaSAndroid Build Coastguard Worker const Sampling& sampling)
824*c8dee2aaSAndroid Build Coastguard Worker : GrFragmentProcessor(kGrTextureEffect_ClassID,
825*c8dee2aaSAndroid Build Coastguard Worker ModulateForSamplerOptFlags(alphaType, sampling.hasBorderAlpha()))
826*c8dee2aaSAndroid Build Coastguard Worker , fView(std::move(view))
827*c8dee2aaSAndroid Build Coastguard Worker , fSamplerState(sampling.fHWSampler)
828*c8dee2aaSAndroid Build Coastguard Worker , fSubset(sampling.fShaderSubset)
829*c8dee2aaSAndroid Build Coastguard Worker , fClamp(sampling.fShaderClamp)
830*c8dee2aaSAndroid Build Coastguard Worker , fShaderModes{sampling.fShaderModes[0], sampling.fShaderModes[1]} {
831*c8dee2aaSAndroid Build Coastguard Worker // We always compare the range even when it isn't used so assert we have canonical don't care
832*c8dee2aaSAndroid Build Coastguard Worker // values.
833*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fShaderModes[0] != ShaderMode::kNone || (fSubset.fLeft == 0 && fSubset.fRight == 0));
834*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fShaderModes[1] != ShaderMode::kNone || (fSubset.fTop == 0 && fSubset.fBottom == 0));
835*c8dee2aaSAndroid Build Coastguard Worker this->setUsesSampleCoordsDirectly();
836*c8dee2aaSAndroid Build Coastguard Worker std::copy_n(sampling.fBorder, 4, fBorder);
837*c8dee2aaSAndroid Build Coastguard Worker }
838*c8dee2aaSAndroid Build Coastguard Worker
GrTextureEffect(const GrTextureEffect & src)839*c8dee2aaSAndroid Build Coastguard Worker GrTextureEffect::GrTextureEffect(const GrTextureEffect& src)
840*c8dee2aaSAndroid Build Coastguard Worker : INHERITED(kGrTextureEffect_ClassID, src.optimizationFlags())
841*c8dee2aaSAndroid Build Coastguard Worker , fView(src.fView)
842*c8dee2aaSAndroid Build Coastguard Worker , fSamplerState(src.fSamplerState)
843*c8dee2aaSAndroid Build Coastguard Worker , fSubset(src.fSubset)
844*c8dee2aaSAndroid Build Coastguard Worker , fClamp(src.fClamp)
845*c8dee2aaSAndroid Build Coastguard Worker , fShaderModes{src.fShaderModes[0], src.fShaderModes[1]} {
846*c8dee2aaSAndroid Build Coastguard Worker std::copy_n(src.fBorder, 4, fBorder);
847*c8dee2aaSAndroid Build Coastguard Worker this->setUsesSampleCoordsDirectly();
848*c8dee2aaSAndroid Build Coastguard Worker }
849*c8dee2aaSAndroid Build Coastguard Worker
clone() const850*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> GrTextureEffect::clone() const {
851*c8dee2aaSAndroid Build Coastguard Worker return std::unique_ptr<GrFragmentProcessor>(new GrTextureEffect(*this));
852*c8dee2aaSAndroid Build Coastguard Worker }
853*c8dee2aaSAndroid Build Coastguard Worker
GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrTextureEffect)854*c8dee2aaSAndroid Build Coastguard Worker GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrTextureEffect)
855*c8dee2aaSAndroid Build Coastguard Worker #if defined(GPU_TEST_UTILS)
856*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> GrTextureEffect::TestCreate(GrProcessorTestData* testData) {
857*c8dee2aaSAndroid Build Coastguard Worker auto [view, ct, at] = testData->randomView();
858*c8dee2aaSAndroid Build Coastguard Worker Wrap wrapModes[2];
859*c8dee2aaSAndroid Build Coastguard Worker GrTest::TestWrapModes(testData->fRandom, wrapModes);
860*c8dee2aaSAndroid Build Coastguard Worker
861*c8dee2aaSAndroid Build Coastguard Worker Filter filter = testData->fRandom->nextBool() ? Filter::kLinear : Filter::kNearest;
862*c8dee2aaSAndroid Build Coastguard Worker MipmapMode mm = MipmapMode::kNone;
863*c8dee2aaSAndroid Build Coastguard Worker if (view.asTextureProxy()->mipmapped() == skgpu::Mipmapped::kYes) {
864*c8dee2aaSAndroid Build Coastguard Worker mm = testData->fRandom->nextBool() ? MipmapMode::kLinear : MipmapMode::kNone;
865*c8dee2aaSAndroid Build Coastguard Worker }
866*c8dee2aaSAndroid Build Coastguard Worker GrSamplerState params(wrapModes, filter, mm);
867*c8dee2aaSAndroid Build Coastguard Worker
868*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& matrix = GrTest::TestMatrix(testData->fRandom);
869*c8dee2aaSAndroid Build Coastguard Worker return GrTextureEffect::Make(std::move(view), at, matrix, params, *testData->caps());
870*c8dee2aaSAndroid Build Coastguard Worker }
871*c8dee2aaSAndroid Build Coastguard Worker #endif
872