blob: b2ed99eea719384b763931547b3ba7b0f23adb20 [file] [log] [blame]
Brian Salomonb8f098d2020-01-07 11:15:44 -05001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "src/gpu/effects/GrTextureEffect.h"
9
Greg Daniel456f9b52020-03-05 19:14:18 +000010#include "src/gpu/GrTexture.h"
Brian Salomonca6b2f42020-01-24 11:31:21 -050011#include "src/gpu/GrTexturePriv.h"
Ethan Nicholas4ab84ed2020-04-17 13:07:02 -040012#include "src/gpu/effects/generated/GrMatrixEffect.h"
Brian Salomonb8f098d2020-01-07 11:15:44 -050013#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
14#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
15#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
16#include "src/sksl/SkSLCPP.h"
17#include "src/sksl/SkSLUtil.h"
18
Brian Salomon53c909e2020-02-13 13:54:24 -050019using Mode = GrSamplerState::WrapMode;
20using Filter = GrSamplerState::Filter;
Brian Salomonca6b2f42020-01-24 11:31:21 -050021
Brian Salomond71548a2020-02-29 19:43:30 -050022struct GrTextureEffect::Sampling {
23 GrSamplerState fHWSampler;
24 ShaderMode fShaderModes[2] = {ShaderMode::kNone, ShaderMode::kNone};
25 SkRect fShaderSubset = {0, 0, 0, 0};
26 SkRect fShaderClamp = {0, 0, 0, 0};
27 float fBorder[4] = {0, 0, 0, 0};
28 Sampling(GrSamplerState::Filter filter) : fHWSampler(filter) {}
29 Sampling(const GrSurfaceProxy& proxy,
30 GrSamplerState sampler,
31 const SkRect&,
32 const SkRect*,
33 const float border[4],
34 const GrCaps&);
35 inline bool hasBorderAlpha() const;
36};
37
Brian Salomonca6b2f42020-01-24 11:31:21 -050038GrTextureEffect::Sampling::Sampling(const GrSurfaceProxy& proxy,
39 GrSamplerState sampler,
40 const SkRect& subset,
Brian Salomonca6b2f42020-01-24 11:31:21 -050041 const SkRect* domain,
Brian Salomond71548a2020-02-29 19:43:30 -050042 const float border[4],
Brian Salomonca6b2f42020-01-24 11:31:21 -050043 const GrCaps& caps) {
Brian Salomon53c909e2020-02-13 13:54:24 -050044 struct Span {
45 float fA = 0.f, fB = 0.f;
Brian Salomonca6b2f42020-01-24 11:31:21 -050046
Brian Salomon53c909e2020-02-13 13:54:24 -050047 Span makeInset(float o) const {
48 Span r = {fA + o, fB - o};
49 if (r.fA > r.fB) {
50 r.fA = r.fB = (r.fA + r.fB) / 2;
51 }
Brian Salomonca6b2f42020-01-24 11:31:21 -050052 return r;
53 }
54
Brian Salomon53c909e2020-02-13 13:54:24 -050055 bool contains(Span r) const { return fA <= r.fA && fB >= r.fB; }
56 };
57 struct Result1D {
58 ShaderMode fShaderMode;
59 Span fShaderSubset;
60 Span fShaderClamp;
61 Mode fHWMode;
62 };
63
64 auto type = proxy.asTextureProxy()->textureType();
65 auto filter = sampler.filter();
66
Brian Salomond71548a2020-02-29 19:43:30 -050067 auto resolve = [type, &caps, filter, &border](int size, Mode mode, Span subset, Span domain) {
Brian Salomon53c909e2020-02-13 13:54:24 -050068 Result1D r;
Brian Salomond71548a2020-02-29 19:43:30 -050069 bool canDoModeInHW = true;
70 // TODO: Use HW border color when available.
71 if (mode == Mode::kClampToBorder &&
72 (!caps.clampToBorderSupport() || border[0] || border[1] || border[2] || border[3])) {
73 canDoModeInHW = false;
74 } else if (mode != Mode::kClamp && !caps.npotTextureTileSupport() && !SkIsPow2(size)) {
75 canDoModeInHW = false;
76 } else if (type != GrTextureType::k2D &&
77 !(mode == Mode::kClamp || mode == Mode::kClampToBorder)) {
78 canDoModeInHW = false;
79 }
80 if (canDoModeInHW && size > 0 && subset.fA <= 0 && subset.fB >= size) {
Brian Salomon53c909e2020-02-13 13:54:24 -050081 r.fShaderMode = ShaderMode::kNone;
82 r.fHWMode = mode;
83 r.fShaderSubset = r.fShaderClamp = {0, 0};
84 return r;
85 }
86
87 r.fShaderSubset = subset;
Brian Salomoned729f92020-02-05 12:15:18 -050088 bool domainIsSafe = false;
Brian Salomoned729f92020-02-05 12:15:18 -050089 if (filter == Filter::kNearest) {
90 Span isubset{sk_float_floor(subset.fA), sk_float_ceil(subset.fB)};
91 if (domain.fA > isubset.fA && domain.fB < isubset.fB) {
92 domainIsSafe = true;
93 }
Brian Salomon53c909e2020-02-13 13:54:24 -050094 // This inset prevents sampling neighboring texels that could occur when
95 // texture coords fall exactly at texel boundaries (depending on precision
96 // and GPU-specific snapping at the boundary).
97 r.fShaderClamp = isubset.makeInset(0.5f);
98 } else {
99 r.fShaderClamp = subset.makeInset(0.5f);
100 if (r.fShaderClamp.contains(domain)) {
101 domainIsSafe = true;
Brian Salomoned729f92020-02-05 12:15:18 -0500102 }
Brian Salomoned729f92020-02-05 12:15:18 -0500103 }
Brian Salomon53c909e2020-02-13 13:54:24 -0500104 if (domainIsSafe) {
105 // The domain of coords that will be used won't access texels outside of the subset.
106 // So the wrap mode effectively doesn't matter. We use kClamp since it is always
107 // supported.
Brian Salomonca6b2f42020-01-24 11:31:21 -0500108 r.fShaderMode = ShaderMode::kNone;
Brian Salomon53c909e2020-02-13 13:54:24 -0500109 r.fHWMode = Mode::kClamp;
110 r.fShaderSubset = r.fShaderClamp = {0, 0};
Brian Salomonca6b2f42020-01-24 11:31:21 -0500111 return r;
112 }
Brian Salomonca6b2f42020-01-24 11:31:21 -0500113 r.fShaderMode = static_cast<ShaderMode>(mode);
114 r.fHWMode = Mode::kClamp;
115 return r;
116 };
117
118 SkISize dim = proxy.isFullyLazy() ? SkISize{-1, -1} : proxy.backingStoreDimensions();
119
120 Span subsetX{subset.fLeft, subset.fRight};
121 auto domainX = domain ? Span{domain->fLeft, domain->fRight}
122 : Span{SK_FloatNegativeInfinity, SK_FloatInfinity};
123 auto x = resolve(dim.width(), sampler.wrapModeX(), subsetX, domainX);
124
125 Span subsetY{subset.fTop, subset.fBottom};
126 auto domainY = domain ? Span{domain->fTop, domain->fBottom}
127 : Span{SK_FloatNegativeInfinity, SK_FloatInfinity};
128 auto y = resolve(dim.height(), sampler.wrapModeY(), subsetY, domainY);
129
Brian Salomon53c909e2020-02-13 13:54:24 -0500130 fHWSampler = {x.fHWMode, y.fHWMode, filter};
Brian Salomonca6b2f42020-01-24 11:31:21 -0500131 fShaderModes[0] = x.fShaderMode;
132 fShaderModes[1] = y.fShaderMode;
133 fShaderSubset = {x.fShaderSubset.fA, y.fShaderSubset.fA,
134 x.fShaderSubset.fB, y.fShaderSubset.fB};
Brian Salomon53c909e2020-02-13 13:54:24 -0500135 fShaderClamp = {x.fShaderClamp.fA, y.fShaderClamp.fA,
136 x.fShaderClamp.fB, y.fShaderClamp.fB};
Brian Salomond71548a2020-02-29 19:43:30 -0500137 std::copy_n(border, 4, fBorder);
Brian Salomonca6b2f42020-01-24 11:31:21 -0500138}
139
Brian Salomond71548a2020-02-29 19:43:30 -0500140bool GrTextureEffect::Sampling::hasBorderAlpha() const {
141 if (fHWSampler.wrapModeX() == GrSamplerState::WrapMode::kClampToBorder ||
142 fHWSampler.wrapModeY() == GrSamplerState::WrapMode::kClampToBorder) {
143 return true;
144 }
145 if (fShaderModes[0] == ShaderMode::kClampToBorder ||
146 fShaderModes[1] == ShaderMode::kClampToBorder) {
147 return fBorder[3] < 1.f;
148 }
149 return false;
Brian Salomonca6b2f42020-01-24 11:31:21 -0500150}
151
Ethan Nicholas4ab84ed2020-04-17 13:07:02 -0400152static void get_matrix(const SkMatrix& preMatrix, const GrSurfaceProxyView& view,
153 SkMatrix* outMatrix, bool* outLazyProxyNormalization) {
154 SkMatrix combined = preMatrix;
155 bool normalize = view.proxy()->backendFormat().textureType() != GrTextureType::kRectangle;
156 if (normalize) {
157 if (view.proxy()->isFullyLazy()) {
158 *outLazyProxyNormalization = true;
159 } else {
160 SkMatrixPriv::PostIDiv(&combined, view.proxy()->backingStoreDimensions().fWidth,
161 view.proxy()->backingStoreDimensions().fHeight);
162 *outLazyProxyNormalization = false;
163 }
164 } else {
165 *outLazyProxyNormalization = false;
166 }
167 if (view.origin() == kBottomLeft_GrSurfaceOrigin) {
168 if (normalize) {
169 // combined.postScale(1,-1);
170 // combined.postTranslate(0,1);
171 combined.set(SkMatrix::kMSkewY,
172 combined[SkMatrix::kMPersp0] - combined[SkMatrix::kMSkewY]);
173 combined.set(SkMatrix::kMScaleY,
174 combined[SkMatrix::kMPersp1] - combined[SkMatrix::kMScaleY]);
175 combined.set(SkMatrix::kMTransY,
176 combined[SkMatrix::kMPersp2] - combined[SkMatrix::kMTransY]);
177 } else {
178 // combined.postScale(1, -1);
179 // combined.postTranslate(0,1);
180 SkScalar h = view.proxy()->backingStoreDimensions().fHeight;
181 combined.set(SkMatrix::kMSkewY,
182 h * combined[SkMatrix::kMPersp0] - combined[SkMatrix::kMSkewY]);
183 combined.set(SkMatrix::kMScaleY,
184 h * combined[SkMatrix::kMPersp1] - combined[SkMatrix::kMScaleY]);
185 combined.set(SkMatrix::kMTransY,
186 h * combined[SkMatrix::kMPersp2] - combined[SkMatrix::kMTransY]);
187 }
188 }
189 *outMatrix = combined;
190}
191
Greg Danield2ccbb52020-02-05 10:45:39 -0500192std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(GrSurfaceProxyView view,
Brian Salomonb8f098d2020-01-07 11:15:44 -0500193 SkAlphaType alphaType,
194 const SkMatrix& matrix,
Brian Salomon53c909e2020-02-13 13:54:24 -0500195 Filter filter) {
Ethan Nicholas4ab84ed2020-04-17 13:07:02 -0400196 SkMatrix final;
197 bool lazyProxyNormalization;
198 get_matrix(matrix, view, &final, &lazyProxyNormalization);
199 return GrMatrixEffect::Apply(final, std::unique_ptr<GrFragmentProcessor>(
200 new GrTextureEffect(std::move(view),
201 alphaType,
202 Sampling(filter),
203 lazyProxyNormalization)));
Brian Salomonca6b2f42020-01-24 11:31:21 -0500204}
205
Greg Danield2ccbb52020-02-05 10:45:39 -0500206std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(GrSurfaceProxyView view,
Brian Salomonca6b2f42020-01-24 11:31:21 -0500207 SkAlphaType alphaType,
208 const SkMatrix& matrix,
209 GrSamplerState sampler,
Brian Salomond71548a2020-02-29 19:43:30 -0500210 const GrCaps& caps,
211 const float border[4]) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500212 Sampling sampling(*view.proxy(), sampler, SkRect::Make(view.proxy()->dimensions()), nullptr,
Brian Salomond71548a2020-02-29 19:43:30 -0500213 border, caps);
Ethan Nicholas4ab84ed2020-04-17 13:07:02 -0400214 SkMatrix final;
215 bool lazyProxyNormalization;
216 get_matrix(matrix, view, &final, &lazyProxyNormalization);
217 return GrMatrixEffect::Apply(final, std::unique_ptr<GrFragmentProcessor>(
218 new GrTextureEffect(std::move(view),
219 alphaType,
220 sampling,
221 lazyProxyNormalization)));
Brian Salomonca6b2f42020-01-24 11:31:21 -0500222}
223
Greg Danield2ccbb52020-02-05 10:45:39 -0500224std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyView view,
Brian Salomonca6b2f42020-01-24 11:31:21 -0500225 SkAlphaType alphaType,
226 const SkMatrix& matrix,
227 GrSamplerState sampler,
228 const SkRect& subset,
Brian Salomond71548a2020-02-29 19:43:30 -0500229 const GrCaps& caps,
230 const float border[4]) {
231 Sampling sampling(*view.proxy(), sampler, subset, nullptr, border, caps);
Ethan Nicholas4ab84ed2020-04-17 13:07:02 -0400232 SkMatrix final;
233 bool lazyProxyNormalization;
234 get_matrix(matrix, view, &final, &lazyProxyNormalization);
235 return GrMatrixEffect::Apply(final, std::unique_ptr<GrFragmentProcessor>(
236 new GrTextureEffect(std::move(view),
237 alphaType,
238 sampling,
239 lazyProxyNormalization)));
Brian Salomonca6b2f42020-01-24 11:31:21 -0500240}
241
Greg Danield2ccbb52020-02-05 10:45:39 -0500242std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyView view,
Brian Salomonca6b2f42020-01-24 11:31:21 -0500243 SkAlphaType alphaType,
244 const SkMatrix& matrix,
245 GrSamplerState sampler,
246 const SkRect& subset,
247 const SkRect& domain,
Brian Salomond71548a2020-02-29 19:43:30 -0500248 const GrCaps& caps,
249 const float border[4]) {
250 Sampling sampling(*view.proxy(), sampler, subset, &domain, border, caps);
Ethan Nicholas4ab84ed2020-04-17 13:07:02 -0400251 SkMatrix final;
252 bool lazyProxyNormalization;
253 get_matrix(matrix, view, &final, &lazyProxyNormalization);
254 return GrMatrixEffect::Apply(final, std::unique_ptr<GrFragmentProcessor>(
255 new GrTextureEffect(std::move(view),
256 alphaType,
257 sampling,
258 lazyProxyNormalization)));
Brian Salomonb8f098d2020-01-07 11:15:44 -0500259}
260
Brian Salomon53c909e2020-02-13 13:54:24 -0500261GrTextureEffect::FilterLogic GrTextureEffect::GetFilterLogic(ShaderMode mode,
262 GrSamplerState::Filter filter) {
263 switch (mode) {
264 case ShaderMode::kMirrorRepeat:
265 case ShaderMode::kNone:
266 case ShaderMode::kClamp:
267 return FilterLogic::kNone;
268 case ShaderMode::kRepeat:
269 switch (filter) {
270 case GrSamplerState::Filter::kNearest:
271 return FilterLogic::kNone;
272 case GrSamplerState::Filter::kBilerp:
273 return FilterLogic::kRepeatBilerp;
274 case GrSamplerState::Filter::kMipMap:
Brian Salomonf46f19b2020-02-13 14:11:28 -0500275 return FilterLogic::kRepeatMipMap;
Brian Salomon53c909e2020-02-13 13:54:24 -0500276 }
277 SkUNREACHABLE;
Brian Salomond71548a2020-02-29 19:43:30 -0500278 case ShaderMode::kClampToBorder:
279 return filter > GrSamplerState::Filter::kNearest ? FilterLogic::kClampToBorderFilter
280 : FilterLogic::kClampToBorderNearest;
Brian Salomon53c909e2020-02-13 13:54:24 -0500281 }
282 SkUNREACHABLE;
283}
284
Brian Salomonb8f098d2020-01-07 11:15:44 -0500285GrGLSLFragmentProcessor* GrTextureEffect::onCreateGLSLInstance() const {
286 class Impl : public GrGLSLFragmentProcessor {
Brian Salomonca6b2f42020-01-24 11:31:21 -0500287 UniformHandle fSubsetUni;
Brian Salomon53c909e2020-02-13 13:54:24 -0500288 UniformHandle fClampUni;
289 UniformHandle fNormUni;
Brian Salomond71548a2020-02-29 19:43:30 -0500290 UniformHandle fBorderUni;
Brian Salomonca6b2f42020-01-24 11:31:21 -0500291
Brian Salomonb8f098d2020-01-07 11:15:44 -0500292 public:
293 void emitCode(EmitArgs& args) override {
Ethan Nicholas94996eb2020-04-01 13:46:10 -0400294 auto& te = args.fFp.cast<GrTextureEffect>();
Ethan Nicholas4ab84ed2020-04-17 13:07:02 -0400295 SkString coords;
296 if (args.fFp.isSampledWithExplicitCoords()) {
297 coords = "_coords";
298 } else {
299 coords = args.fTransformedCoords[0].fVaryingPoint.c_str();
300 }
Brian Salomonb8f098d2020-01-07 11:15:44 -0500301 auto* fb = args.fFragBuilder;
Ethan Nicholas4ab84ed2020-04-17 13:07:02 -0400302 if (te.sampleMatrix().fKind == SkSL::SampleMatrix::Kind::kMixed) {
303 args.fUniformHandler->writeUniformMappings(te.sampleMatrix().fOwner, fb);
304 coords = SkStringPrintf("(%s * _matrix * float3(%s, 1)).xy",
305 te.sampleMatrix().fExpression.c_str(),
306 coords.c_str());
307 }
Brian Salomonca6b2f42020-01-24 11:31:21 -0500308 if (te.fShaderModes[0] == ShaderMode::kNone &&
309 te.fShaderModes[1] == ShaderMode::kNone) {
310 fb->codeAppendf("%s = ", args.fOutputColor);
Ethan Nicholas4ab84ed2020-04-17 13:07:02 -0400311 if (te.fLazyProxyNormalization) {
312 const char* norm = nullptr;
313 fNormUni = args.fUniformHandler->addUniform(&te, kFragment_GrShaderFlag,
314 kFloat4_GrSLType, "norm", &norm);
315 fb->appendTextureLookupAndBlend(args.fInputColor, SkBlendMode::kModulate,
316 args.fTexSamplers[0],
317 SkStringPrintf("%s * %s.zw", coords.c_str(),
318 norm).c_str());
319 } else {
320 fb->appendTextureLookupAndBlend(args.fInputColor, SkBlendMode::kModulate,
321 args.fTexSamplers[0], coords.c_str());
322 }
Brian Salomonca6b2f42020-01-24 11:31:21 -0500323 fb->codeAppendf(";");
324 } else {
Ethan Nicholas4ab84ed2020-04-17 13:07:02 -0400325 // Tripping this assert means we have a normalized fully lazy proxy with a
326 // non-default ShaderMode. There's nothing fundamentally wrong with doing that, but
327 // it hasn't been tested and this code path probably won't handle normalization
328 // properly in that case.
329 SkASSERT(!te.fLazyProxyNormalization);
Brian Salomon53c909e2020-02-13 13:54:24 -0500330 // Here is the basic flow of the various ShaderModes are implemented in a series of
331 // steps. Not all the steps apply to all the modes. We try to emit only the steps
332 // that are necessary for the given x/y shader modes.
333 //
334 // 0) Start with interpolated coordinates (unnormalize if doing anything
335 // complicated).
336 // 1) Map the coordinates into the subset range [Repeat and MirrorRepeat], or pass
337 // through output of 0).
338 // 2) Clamp the coordinates to a 0.5 inset of the subset rect [Clamp, Repeat, and
Brian Salomond71548a2020-02-29 19:43:30 -0500339 // MirrorRepeat always or ClampToBorder only when filtering] or pass through
340 // output of 1). The clamp rect collapses to a line or point it if the subset
341 // rect is less than one pixel wide/tall.
Brian Salomon53c909e2020-02-13 13:54:24 -0500342 // 3) Look up texture with output of 2) [All]
343 // 3) Use the difference between 1) and 2) to apply filtering at edge [Repeat or
Brian Salomond71548a2020-02-29 19:43:30 -0500344 // ClampToBorder]. In the Repeat case this requires extra texture lookups on the
345 // other side of the subset (up to 3 more reads). Or if ClampToBorder and not
346 // filtering do a hard less than/greater than test with the subset rect.
Brian Salomonca6b2f42020-01-24 11:31:21 -0500347
Brian Salomon53c909e2020-02-13 13:54:24 -0500348 // Convert possible projective texture coordinates into non-homogeneous half2.
Brian Salomon66356c22020-02-11 15:22:18 -0500349 fb->codeAppendf(
350 "float2 inCoord = %s;",
Ethan Nicholas58430122020-04-14 09:54:02 -0400351 fb->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint,
352 te.sampleMatrix()).c_str());
Brian Salomon66356c22020-02-11 15:22:18 -0500353
Brian Salomon53c909e2020-02-13 13:54:24 -0500354 const auto& m = te.fShaderModes;
Robert Phillips6eb5cb92020-03-05 12:52:45 -0500355 GrTextureType textureType = te.fSampler.proxy()->backendFormat().textureType();
356 bool normCoords = textureType != GrTextureType::kRectangle;
Brian Salomon53c909e2020-02-13 13:54:24 -0500357 auto filter = te.fSampler.samplerState().filter();
358 FilterLogic filterLogic[2] = {GetFilterLogic(m[0], filter),
359 GetFilterLogic(m[1], filter)};
360
Brian Salomond71548a2020-02-29 19:43:30 -0500361 const char* borderName = nullptr;
362 if (te.fShaderModes[0] == ShaderMode::kClampToBorder ||
363 te.fShaderModes[1] == ShaderMode::kClampToBorder) {
364 fBorderUni = args.fUniformHandler->addUniform(
Ethan Nicholas16464c32020-04-06 13:53:05 -0400365 &te, kFragment_GrShaderFlag, kHalf4_GrSLType, "border", &borderName);
Brian Salomond71548a2020-02-29 19:43:30 -0500366 }
Brian Salomon53c909e2020-02-13 13:54:24 -0500367 auto modeUsesSubset = [](ShaderMode m) {
368 return m == ShaderMode::kRepeat || m == ShaderMode::kMirrorRepeat ||
Brian Salomond71548a2020-02-29 19:43:30 -0500369 m == ShaderMode::kClampToBorder;
Brian Salomon53c909e2020-02-13 13:54:24 -0500370 };
371
372 auto modeUsesClamp = [filter](ShaderMode m) {
373 return m != ShaderMode::kNone &&
Brian Salomond71548a2020-02-29 19:43:30 -0500374 (m != ShaderMode::kClampToBorder || filter > Filter::kNearest);
Brian Salomon53c909e2020-02-13 13:54:24 -0500375 };
376
377 bool useSubset[2] = {modeUsesSubset(m[0]), modeUsesSubset(m[1])};
378 bool useClamp [2] = {modeUsesClamp (m[0]), modeUsesClamp (m[1])};
379
380 const char* subsetName = nullptr;
381 if (useSubset[0] || useSubset[1]) {
382 fSubsetUni = args.fUniformHandler->addUniform(
Ethan Nicholas16464c32020-04-06 13:53:05 -0400383 &te, kFragment_GrShaderFlag, kFloat4_GrSLType, "subset", &subsetName);
Brian Salomon53c909e2020-02-13 13:54:24 -0500384 }
385
386 const char* clampName = nullptr;
387 if (useClamp[0] || useClamp[1]) {
388 fClampUni = args.fUniformHandler->addUniform(
Ethan Nicholas16464c32020-04-06 13:53:05 -0400389 &te, kFragment_GrShaderFlag, kFloat4_GrSLType, "clamp", &clampName);
Brian Salomon53c909e2020-02-13 13:54:24 -0500390 }
391
392 // To keep things a little simpler, when we have filtering logic in the shader we
393 // operate on unnormalized texture coordinates. We add a uniform that stores
394 // {w, h, 1/w, 1/h} in a float4.
395 const char* norm = nullptr;
396 if (normCoords && (filterLogic[0] != FilterLogic::kNone ||
397 filterLogic[1] != FilterLogic::kNone)) {
398 // TODO: Detect support for textureSize() or polyfill textureSize() in SkSL and
399 // always use?
Ethan Nicholas16464c32020-04-06 13:53:05 -0400400 fNormUni = args.fUniformHandler->addUniform(&te, kFragment_GrShaderFlag,
Brian Salomon53c909e2020-02-13 13:54:24 -0500401 kFloat4_GrSLType, "norm", &norm);
402 // TODO: Remove the normalization from the CoordTransform to skip unnormalizing
403 // step here.
404 fb->codeAppendf("inCoord *= %s.xy;", norm);
405 }
406
407 // Generates a string to read at a coordinate, normalizing coords if necessary.
Brian Salomonf46f19b2020-02-13 14:11:28 -0500408 auto read = [&](const char* coord) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500409 SkString result;
410 SkString normCoord;
411 if (norm) {
412 normCoord.printf("(%s) * %s.zw", coord, norm);
413 } else {
414 normCoord = coord;
415 }
Brian Salomonf46f19b2020-02-13 14:11:28 -0500416 fb->appendTextureLookup(&result, args.fTexSamplers[0], normCoord.c_str());
Brian Salomon53c909e2020-02-13 13:54:24 -0500417 return result;
418 };
419
420 // Implements coord wrapping for kRepeat and kMirrorRepeat
Brian Salomonf46f19b2020-02-13 14:11:28 -0500421 auto subsetCoord = [&](ShaderMode mode,
422 const char* coordSwizzle,
423 const char* subsetStartSwizzle,
424 const char* subsetStopSwizzle,
425 const char* extraCoord,
426 const char* coordWeight) {
Brian Salomon66356c22020-02-11 15:22:18 -0500427 switch (mode) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500428 // These modes either don't use the subset rect or don't need to map the
429 // coords to be within the subset.
Brian Salomon66356c22020-02-11 15:22:18 -0500430 case ShaderMode::kNone:
Brian Salomond71548a2020-02-29 19:43:30 -0500431 case ShaderMode::kClampToBorder:
Brian Salomon66356c22020-02-11 15:22:18 -0500432 case ShaderMode::kClamp:
Brian Salomon53c909e2020-02-13 13:54:24 -0500433 fb->codeAppendf("subsetCoord.%s = inCoord.%s;", coordSwizzle,
434 coordSwizzle);
Brian Salomon66356c22020-02-11 15:22:18 -0500435 break;
436 case ShaderMode::kRepeat:
Brian Salomonf46f19b2020-02-13 14:11:28 -0500437 if (filter == Filter::kMipMap) {
438 // The approach here is to generate two sets of texture coords that
439 // are both "moving" at the same speed (if not direction) as
440 // inCoords. We accomplish that by using two out of phase mirror
441 // repeat coords. We will always sample using both coords but the
442 // read from the upward sloping one is selected using a weight
443 // that transitions from one set to the other near the reflection
444 // point. Like the coords, the weight is a saw-tooth function,
445 // phase-shifted, vertically translated, and then clamped to 0..1.
446 // TODO: Skip this and use textureGrad() when available.
447 SkASSERT(extraCoord);
448 SkASSERT(coordWeight);
449 fb->codeAppend("{");
450 fb->codeAppendf("float w = %s.%s - %s.%s;", subsetName,
451 subsetStopSwizzle, subsetName, subsetStartSwizzle);
452 fb->codeAppendf("float w2 = 2 * w;");
453 fb->codeAppendf("float d = inCoord.%s - %s.%s;", coordSwizzle,
454 subsetName, subsetStartSwizzle);
455 fb->codeAppend("float m = mod(d, w2);");
456 fb->codeAppend("float o = mix(m, w2 - m, step(w, m));");
457 fb->codeAppendf("subsetCoord.%s = o + %s.%s;", coordSwizzle,
458 subsetName, subsetStartSwizzle);
459 fb->codeAppendf("%s = w - o + %s.%s;", extraCoord, subsetName,
460 subsetStartSwizzle);
461 // coordWeight is used as the third param of mix() to blend between a
462 // sample taken using subsetCoord and a sample at extraCoord.
463 fb->codeAppend("float hw = w/2;");
464 fb->codeAppend("float n = mod(d - hw, w2);");
465 fb->codeAppendf(
466 "%s = saturate(half(mix(n, w2 - n, step(w, n)) - hw + "
467 "0.5));",
468 coordWeight);
469 fb->codeAppend("}");
470 } else {
471 fb->codeAppendf(
472 "subsetCoord.%s = mod(inCoord.%s - %s.%s, %s.%s - %s.%s) + "
473 "%s.%s;",
474 coordSwizzle, coordSwizzle, subsetName, subsetStartSwizzle,
475 subsetName, subsetStopSwizzle, subsetName,
476 subsetStartSwizzle, subsetName, subsetStartSwizzle);
477 }
Brian Salomon66356c22020-02-11 15:22:18 -0500478 break;
479 case ShaderMode::kMirrorRepeat: {
480 fb->codeAppend("{");
481 fb->codeAppendf("float w = %s.%s - %s.%s;", subsetName,
482 subsetStopSwizzle, subsetName, subsetStartSwizzle);
483 fb->codeAppendf("float w2 = 2 * w;");
484 fb->codeAppendf("float m = mod(inCoord.%s - %s.%s, w2);", coordSwizzle,
485 subsetName, subsetStartSwizzle);
Brian Salomon53c909e2020-02-13 13:54:24 -0500486 fb->codeAppendf("subsetCoord.%s = mix(m, w2 - m, step(w, m)) + %s.%s;",
Brian Salomon66356c22020-02-11 15:22:18 -0500487 coordSwizzle, subsetName, subsetStartSwizzle);
488 fb->codeAppend("}");
489 break;
490 }
491 }
492 };
493
Brian Salomonf46f19b2020-02-13 14:11:28 -0500494 auto clampCoord = [&](bool clamp,
495 const char* coordSwizzle,
496 const char* clampStartSwizzle,
497 const char* clampStopSwizzle) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500498 if (clamp) {
499 fb->codeAppendf("clampedCoord.%s = clamp(subsetCoord.%s, %s.%s, %s.%s);",
500 coordSwizzle, coordSwizzle, clampName, clampStartSwizzle,
501 clampName, clampStopSwizzle);
502 } else {
503 fb->codeAppendf("clampedCoord.%s = subsetCoord.%s;", coordSwizzle,
504 coordSwizzle);
505 }
506 };
507
Brian Salomonf46f19b2020-02-13 14:11:28 -0500508 // Insert vars for extra coords and blending weights for kRepeatMipMap.
509 const char* extraRepeatCoordX = nullptr;
510 const char* repeatCoordWeightX = nullptr;
511 const char* extraRepeatCoordY = nullptr;
512 const char* repeatCoordWeightY = nullptr;
513 if (filterLogic[0] == FilterLogic::kRepeatMipMap) {
514 fb->codeAppend("float extraRepeatCoordX; half repeatCoordWeightX;");
515 extraRepeatCoordX = "extraRepeatCoordX";
516 repeatCoordWeightX = "repeatCoordWeightX";
517 }
518 if (filterLogic[1] == FilterLogic::kRepeatMipMap) {
519 fb->codeAppend("float extraRepeatCoordY; half repeatCoordWeightY;");
520 extraRepeatCoordY = "extraRepeatCoordY";
521 repeatCoordWeightY = "repeatCoordWeightY";
522 }
523
524 // Apply subset rect and clamp rect to coords.
Brian Salomon53c909e2020-02-13 13:54:24 -0500525 fb->codeAppend("float2 subsetCoord;");
Brian Salomonf46f19b2020-02-13 14:11:28 -0500526 subsetCoord(te.fShaderModes[0], "x", "x", "z", extraRepeatCoordX,
527 repeatCoordWeightX);
528 subsetCoord(te.fShaderModes[1], "y", "y", "w", extraRepeatCoordY,
529 repeatCoordWeightY);
Brian Salomon53c909e2020-02-13 13:54:24 -0500530 fb->codeAppend("float2 clampedCoord;");
531 clampCoord(useClamp[0], "x", "x", "z");
532 clampCoord(useClamp[1], "y", "y", "w");
Brian Salomonca6b2f42020-01-24 11:31:21 -0500533
Brian Salomonf46f19b2020-02-13 14:11:28 -0500534 // Additional clamping for the extra coords for kRepeatMipMap.
535 if (filterLogic[0] == FilterLogic::kRepeatMipMap) {
536 fb->codeAppendf("extraRepeatCoordX = clamp(extraRepeatCoordX, %s.x, %s.z);",
537 clampName, clampName);
538 }
539 if (filterLogic[1] == FilterLogic::kRepeatMipMap) {
540 fb->codeAppendf("extraRepeatCoordY = clamp(extraRepeatCoordY, %s.y, %s.w);",
541 clampName, clampName);
542 }
543
544 // Do the 2 or 4 texture reads for kRepeatMipMap and then apply the weight(s)
545 // to blend between them. If neither direction is kRepeatMipMap do a single
546 // read at clampedCoord.
547 if (filterLogic[0] == FilterLogic::kRepeatMipMap &&
548 filterLogic[1] == FilterLogic::kRepeatMipMap) {
549 fb->codeAppendf(
550 "half4 textureColor ="
551 " mix(mix(%s, %s, repeatCoordWeightX),"
552 " mix(%s, %s, repeatCoordWeightX),"
553 " repeatCoordWeightY);",
554 read("clampedCoord").c_str(),
555 read("float2(extraRepeatCoordX, clampedCoord.y)").c_str(),
556 read("float2(clampedCoord.x, extraRepeatCoordY)").c_str(),
557 read("float2(extraRepeatCoordX, extraRepeatCoordY)").c_str());
558
559 } else if (filterLogic[0] == FilterLogic::kRepeatMipMap) {
560 fb->codeAppendf("half4 textureColor = mix(%s, %s, repeatCoordWeightX);",
561 read("clampedCoord").c_str(),
562 read("float2(extraRepeatCoordX, clampedCoord.y)").c_str());
563 } else if (filterLogic[1] == FilterLogic::kRepeatMipMap) {
564 fb->codeAppendf("half4 textureColor = mix(%s, %s, repeatCoordWeightY);",
565 read("clampedCoord").c_str(),
566 read("float2(clampedCoord.x, extraRepeatCoordY)").c_str());
567 } else {
568 fb->codeAppendf("half4 textureColor = %s;", read("clampedCoord").c_str());
569 }
Brian Salomonca6b2f42020-01-24 11:31:21 -0500570
Brian Salomon53c909e2020-02-13 13:54:24 -0500571 // Strings for extra texture reads used only in kRepeatBilerp
572 SkString repeatBilerpReadX;
573 SkString repeatBilerpReadY;
574
575 // Calculate the amount the coord moved for clamping. This will be used
Brian Salomond71548a2020-02-29 19:43:30 -0500576 // to implement shader-based filtering for kClampToBorder and kRepeat.
Brian Salomon53c909e2020-02-13 13:54:24 -0500577
578 if (filterLogic[0] == FilterLogic::kRepeatBilerp ||
Brian Salomond71548a2020-02-29 19:43:30 -0500579 filterLogic[0] == FilterLogic::kClampToBorderFilter) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500580 fb->codeAppend("half errX = half(subsetCoord.x - clampedCoord.x);");
581 fb->codeAppendf("float repeatCoordX = errX > 0 ? %s.x : %s.z;", clampName,
582 clampName);
583 repeatBilerpReadX = read("float2(repeatCoordX, clampedCoord.y)");
Brian Salomonca6b2f42020-01-24 11:31:21 -0500584 }
Brian Salomon53c909e2020-02-13 13:54:24 -0500585 if (filterLogic[1] == FilterLogic::kRepeatBilerp ||
Brian Salomond71548a2020-02-29 19:43:30 -0500586 filterLogic[1] == FilterLogic::kClampToBorderFilter) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500587 fb->codeAppend("half errY = half(subsetCoord.y - clampedCoord.y);");
588 fb->codeAppendf("float repeatCoordY = errY > 0 ? %s.y : %s.w;", clampName,
589 clampName);
590 repeatBilerpReadY = read("float2(clampedCoord.x, repeatCoordY)");
591 }
592
593 // Add logic for kRepeatBilerp. Do 1 or 3 more texture reads depending
594 // on whether both modes are kRepeat and whether we're near a single subset edge
595 // or a corner. Then blend the multiple reads using the err values calculated
596 // above.
597 const char* ifStr = "if";
598 if (filterLogic[0] == FilterLogic::kRepeatBilerp &&
599 filterLogic[1] == FilterLogic::kRepeatBilerp) {
600 auto repeatBilerpReadXY = read("float2(repeatCoordX, repeatCoordY)");
601 fb->codeAppendf(
602 "if (errX != 0 && errY != 0) {"
Brian Salomon3fcf83a2020-02-23 21:29:01 -0500603 " errX = abs(errX);"
Brian Salomon53c909e2020-02-13 13:54:24 -0500604 " textureColor = mix(mix(textureColor, %s, errX),"
605 " mix(%s, %s, errX),"
Brian Salomon3fcf83a2020-02-23 21:29:01 -0500606 " abs(errY));"
Brian Salomon53c909e2020-02-13 13:54:24 -0500607 "}",
608 repeatBilerpReadX.c_str(), repeatBilerpReadY.c_str(),
609 repeatBilerpReadXY.c_str());
610 ifStr = "else if";
611 }
612 if (filterLogic[0] == FilterLogic::kRepeatBilerp) {
613 fb->codeAppendf(
614 "%s (errX != 0) {"
615 " textureColor = mix(textureColor, %s, abs(errX));"
616 "}",
617 ifStr, repeatBilerpReadX.c_str());
618 }
619 if (filterLogic[1] == FilterLogic::kRepeatBilerp) {
620 fb->codeAppendf(
621 "%s (errY != 0) {"
622 " textureColor = mix(textureColor, %s, abs(errY));"
623 "}",
624 ifStr, repeatBilerpReadY.c_str());
625 }
626
Brian Salomond71548a2020-02-29 19:43:30 -0500627 // Do soft edge shader filtering against border color for kClampToBorderFilter using
Brian Salomon53c909e2020-02-13 13:54:24 -0500628 // the err values calculated above.
Brian Salomond71548a2020-02-29 19:43:30 -0500629 if (filterLogic[0] == FilterLogic::kClampToBorderFilter) {
630 fb->codeAppendf("textureColor = mix(textureColor, %s, min(abs(errX), 1));",
631 borderName);
Brian Salomon53c909e2020-02-13 13:54:24 -0500632 }
Brian Salomond71548a2020-02-29 19:43:30 -0500633 if (filterLogic[1] == FilterLogic::kClampToBorderFilter) {
634 fb->codeAppendf("textureColor = mix(textureColor, %s, min(abs(errY), 1));",
635 borderName);
Brian Salomon53c909e2020-02-13 13:54:24 -0500636 }
637
Brian Salomond71548a2020-02-29 19:43:30 -0500638 // Do hard-edge shader transition to border color for kClampToBorderNearest at the
Brian Salomon53c909e2020-02-13 13:54:24 -0500639 // subset boundaries.
Brian Salomond71548a2020-02-29 19:43:30 -0500640 if (filterLogic[0] == FilterLogic::kClampToBorderNearest) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500641 fb->codeAppendf(
642 "if (inCoord.x < %s.x || inCoord.x > %s.z) {"
Brian Salomond71548a2020-02-29 19:43:30 -0500643 " textureColor = %s;"
Brian Salomon53c909e2020-02-13 13:54:24 -0500644 "}",
Brian Salomond71548a2020-02-29 19:43:30 -0500645 subsetName, subsetName, borderName);
Brian Salomon53c909e2020-02-13 13:54:24 -0500646 }
Brian Salomond71548a2020-02-29 19:43:30 -0500647 if (filterLogic[1] == FilterLogic::kClampToBorderNearest) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500648 fb->codeAppendf(
649 "if (inCoord.y < %s.y || inCoord.y > %s.w) {"
Brian Salomond71548a2020-02-29 19:43:30 -0500650 " textureColor = %s;"
Brian Salomon53c909e2020-02-13 13:54:24 -0500651 "}",
Brian Salomond71548a2020-02-29 19:43:30 -0500652 subsetName, subsetName, borderName);
Brian Salomon53c909e2020-02-13 13:54:24 -0500653 }
654 fb->codeAppendf("%s = %s * textureColor;", args.fOutputColor, args.fInputColor);
Brian Salomonca6b2f42020-01-24 11:31:21 -0500655 }
656 }
657
658 protected:
659 void onSetData(const GrGLSLProgramDataManager& pdm,
660 const GrFragmentProcessor& fp) override {
661 const auto& te = fp.cast<GrTextureEffect>();
Brian Salomonca6b2f42020-01-24 11:31:21 -0500662
Brian Salomon53c909e2020-02-13 13:54:24 -0500663 const float w = te.fSampler.peekTexture()->width();
664 const float h = te.fSampler.peekTexture()->height();
665 const auto& s = te.fSubset;
666 const auto& c = te.fClamp;
Brian Salomonca6b2f42020-01-24 11:31:21 -0500667
Brian Salomon53c909e2020-02-13 13:54:24 -0500668 auto type = te.fSampler.peekTexture()->texturePriv().textureType();
669
670 float norm[4] = {w, h, 1.f/w, 1.f/h};
671
672 if (fNormUni.isValid()) {
673 pdm.set4fv(fNormUni, 1, norm);
674 SkASSERT(type != GrTextureType::kRectangle);
675 }
676
677 auto pushRect = [&](float rect[4], UniformHandle uni) {
Brian Salomonca6b2f42020-01-24 11:31:21 -0500678 if (te.fSampler.view().origin() == kBottomLeft_GrSurfaceOrigin) {
679 rect[1] = h - rect[1];
680 rect[3] = h - rect[3];
681 std::swap(rect[1], rect[3]);
682 }
Brian Salomon53c909e2020-02-13 13:54:24 -0500683 if (!fNormUni.isValid() && type != GrTextureType::kRectangle) {
684 rect[0] *= norm[2];
685 rect[2] *= norm[2];
686 rect[1] *= norm[3];
687 rect[3] *= norm[3];
Brian Salomonca6b2f42020-01-24 11:31:21 -0500688 }
Brian Salomon53c909e2020-02-13 13:54:24 -0500689 pdm.set4fv(uni, 1, rect);
690 };
Brian Salomonca6b2f42020-01-24 11:31:21 -0500691
Brian Salomon53c909e2020-02-13 13:54:24 -0500692 if (fSubsetUni.isValid()) {
693 float subset[] = {s.fLeft, s.fTop, s.fRight, s.fBottom};
694 pushRect(subset, fSubsetUni);
695 }
696 if (fClampUni.isValid()) {
697 float subset[] = {c.fLeft, c.fTop, c.fRight, c.fBottom};
698 pushRect(subset, fClampUni);
Brian Salomonca6b2f42020-01-24 11:31:21 -0500699 }
Brian Salomond71548a2020-02-29 19:43:30 -0500700 if (fBorderUni.isValid()) {
701 pdm.set4fv(fBorderUni, 1, te.fBorder);
702 }
Brian Salomonb8f098d2020-01-07 11:15:44 -0500703 }
704 };
705 return new Impl;
706}
707
Brian Salomonca6b2f42020-01-24 11:31:21 -0500708void GrTextureEffect::onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const {
Brian Salomonca6b2f42020-01-24 11:31:21 -0500709 auto m0 = static_cast<uint32_t>(fShaderModes[0]);
710 auto m1 = static_cast<uint32_t>(fShaderModes[1]);
Brian Salomon53c909e2020-02-13 13:54:24 -0500711 auto filter = fSampler.samplerState().filter();
712 auto l0 = static_cast<uint32_t>(GetFilterLogic(fShaderModes[0], filter));
713 auto l1 = static_cast<uint32_t>(GetFilterLogic(fShaderModes[1], filter));
714 b->add32((l0 << 24) | (l1 << 16) | (m0 << 8) | m1);
Brian Salomonca6b2f42020-01-24 11:31:21 -0500715}
Brian Salomonb8f098d2020-01-07 11:15:44 -0500716
Brian Salomonca6b2f42020-01-24 11:31:21 -0500717bool GrTextureEffect::onIsEqual(const GrFragmentProcessor& other) const {
Ethan Nicholas94996eb2020-04-01 13:46:10 -0400718 auto& that = other.cast<GrTextureEffect>();
Brian Salomond71548a2020-02-29 19:43:30 -0500719 if (fShaderModes[0] != that.fShaderModes[0] || fShaderModes[1] != that.fShaderModes[1]) {
720 return false;
721 }
722 if (fSubset != that.fSubset) {
723 return false;
724 }
725 if ((fShaderModes[0] == ShaderMode::kClampToBorder ||
726 fShaderModes[1] == ShaderMode::kClampToBorder) &&
727 !std::equal(fBorder, fBorder + 4, that.fBorder)) {
728 return false;
729 }
730 return true;
Brian Salomonb8f098d2020-01-07 11:15:44 -0500731}
732
Greg Danield2ccbb52020-02-05 10:45:39 -0500733GrTextureEffect::GrTextureEffect(GrSurfaceProxyView view, SkAlphaType alphaType,
Ethan Nicholas4ab84ed2020-04-17 13:07:02 -0400734 const Sampling& sampling, bool lazyProxyNormalization)
Brian Salomonb8f098d2020-01-07 11:15:44 -0500735 : GrFragmentProcessor(kGrTextureEffect_ClassID,
Brian Salomond71548a2020-02-29 19:43:30 -0500736 ModulateForSamplerOptFlags(alphaType, sampling.hasBorderAlpha()))
Ethan Nicholas4ab84ed2020-04-17 13:07:02 -0400737 , fCoordTransform(SkMatrix::I())
Greg Danield2ccbb52020-02-05 10:45:39 -0500738 , fSampler(std::move(view), sampling.fHWSampler)
Brian Salomonca6b2f42020-01-24 11:31:21 -0500739 , fSubset(sampling.fShaderSubset)
Brian Salomon53c909e2020-02-13 13:54:24 -0500740 , fClamp(sampling.fShaderClamp)
Ethan Nicholas4ab84ed2020-04-17 13:07:02 -0400741 , fShaderModes{sampling.fShaderModes[0], sampling.fShaderModes[1]}
742 , fLazyProxyNormalization(lazyProxyNormalization) {
Brian Salomonca6b2f42020-01-24 11:31:21 -0500743 // We always compare the range even when it isn't used so assert we have canonical don't care
744 // values.
745 SkASSERT(fShaderModes[0] != ShaderMode::kNone || (fSubset.fLeft == 0 && fSubset.fRight == 0));
746 SkASSERT(fShaderModes[1] != ShaderMode::kNone || (fSubset.fTop == 0 && fSubset.fBottom == 0));
Brian Salomonb8f098d2020-01-07 11:15:44 -0500747 this->setTextureSamplerCnt(1);
748 this->addCoordTransform(&fCoordTransform);
Brian Salomond71548a2020-02-29 19:43:30 -0500749 std::copy_n(sampling.fBorder, 4, fBorder);
Brian Salomonb8f098d2020-01-07 11:15:44 -0500750}
751
752GrTextureEffect::GrTextureEffect(const GrTextureEffect& src)
753 : INHERITED(kGrTextureEffect_ClassID, src.optimizationFlags())
754 , fCoordTransform(src.fCoordTransform)
Brian Salomonca6b2f42020-01-24 11:31:21 -0500755 , fSampler(src.fSampler)
756 , fSubset(src.fSubset)
Brian Salomon922496f2020-02-14 12:20:14 -0500757 , fClamp(src.fClamp)
Ethan Nicholas4ab84ed2020-04-17 13:07:02 -0400758 , fShaderModes{src.fShaderModes[0], src.fShaderModes[1]}
759 , fLazyProxyNormalization(src.fLazyProxyNormalization) {
Brian Salomonba90ce92020-03-04 12:38:04 -0500760 std::copy_n(src.fBorder, 4, fBorder);
Brian Salomonb8f098d2020-01-07 11:15:44 -0500761 this->setTextureSamplerCnt(1);
762 this->addCoordTransform(&fCoordTransform);
763}
764
765std::unique_ptr<GrFragmentProcessor> GrTextureEffect::clone() const {
766 return std::unique_ptr<GrFragmentProcessor>(new GrTextureEffect(*this));
767}
768
769const GrFragmentProcessor::TextureSampler& GrTextureEffect::onTextureSampler(int) const {
770 return fSampler;
771}
772
773GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrTextureEffect);
774#if GR_TEST_UTILS
775std::unique_ptr<GrFragmentProcessor> GrTextureEffect::TestCreate(GrProcessorTestData* testData) {
Greg Daniel026a60c2020-02-12 10:53:51 -0500776 auto [view, ct, at] = testData->randomView();
Brian Salomon53c909e2020-02-13 13:54:24 -0500777 Mode wrapModes[2];
Brian Salomonb8f098d2020-01-07 11:15:44 -0500778 GrTest::TestWrapModes(testData->fRandom, wrapModes);
Brian Salomonb8f098d2020-01-07 11:15:44 -0500779
Brian Salomon53c909e2020-02-13 13:54:24 -0500780 Filter filter;
781 if (view.asTextureProxy()->mipMapped() == GrMipMapped::kYes) {
782 switch (testData->fRandom->nextULessThan(3)) {
783 case 0:
784 filter = Filter::kNearest;
785 break;
786 case 1:
787 filter = Filter::kBilerp;
788 break;
789 default:
790 filter = Filter::kMipMap;
791 break;
792 }
793 } else {
794 filter = testData->fRandom->nextBool() ? Filter::kBilerp : Filter::kNearest;
795 }
796 GrSamplerState params(wrapModes, filter);
Brian Salomonb8f098d2020-01-07 11:15:44 -0500797
798 const SkMatrix& matrix = GrTest::TestMatrix(testData->fRandom);
Greg Danield2ccbb52020-02-05 10:45:39 -0500799 return GrTextureEffect::Make(std::move(view), at, matrix, params, *testData->caps());
Brian Salomonb8f098d2020-01-07 11:15:44 -0500800}
801#endif