blob: fe7792a407be5ed29672b1d5ae80bca6576031cf [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
Michael Ludwigfbe28592020-06-26 16:02:15 -040010#include "src/core/SkMatrixPriv.h"
Greg Daniel456f9b52020-03-05 19:14:18 +000011#include "src/gpu/GrTexture.h"
Brian Salomonca6b2f42020-01-24 11:31:21 -050012#include "src/gpu/GrTexturePriv.h"
Brian Osman273944a2020-05-29 16:44:26 -040013#include "src/gpu/effects/GrMatrixEffect.h"
Brian Salomonb8f098d2020-01-07 11:15:44 -050014#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
15#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
16#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
17#include "src/sksl/SkSLCPP.h"
18#include "src/sksl/SkSLUtil.h"
19
Brian Salomon53c909e2020-02-13 13:54:24 -050020using Mode = GrSamplerState::WrapMode;
21using Filter = GrSamplerState::Filter;
Brian Salomonca6b2f42020-01-24 11:31:21 -050022
Brian Salomond71548a2020-02-29 19:43:30 -050023struct GrTextureEffect::Sampling {
24 GrSamplerState fHWSampler;
25 ShaderMode fShaderModes[2] = {ShaderMode::kNone, ShaderMode::kNone};
26 SkRect fShaderSubset = {0, 0, 0, 0};
27 SkRect fShaderClamp = {0, 0, 0, 0};
28 float fBorder[4] = {0, 0, 0, 0};
29 Sampling(GrSamplerState::Filter filter) : fHWSampler(filter) {}
30 Sampling(const GrSurfaceProxy& proxy,
31 GrSamplerState sampler,
32 const SkRect&,
33 const SkRect*,
34 const float border[4],
Brian Salomonb9b13732020-06-25 11:13:49 -040035 const GrCaps&,
36 SkVector bilerpInset = {0.5f, 0.5f});
Brian Salomond71548a2020-02-29 19:43:30 -050037 inline bool hasBorderAlpha() const;
38};
39
Brian Salomonca6b2f42020-01-24 11:31:21 -050040GrTextureEffect::Sampling::Sampling(const GrSurfaceProxy& proxy,
41 GrSamplerState sampler,
42 const SkRect& subset,
Brian Salomonca6b2f42020-01-24 11:31:21 -050043 const SkRect* domain,
Brian Salomond71548a2020-02-29 19:43:30 -050044 const float border[4],
Brian Salomonb9b13732020-06-25 11:13:49 -040045 const GrCaps& caps,
46 SkVector bilerpInset) {
Brian Salomon53c909e2020-02-13 13:54:24 -050047 struct Span {
48 float fA = 0.f, fB = 0.f;
Brian Salomonca6b2f42020-01-24 11:31:21 -050049
Brian Salomon53c909e2020-02-13 13:54:24 -050050 Span makeInset(float o) const {
51 Span r = {fA + o, fB - o};
52 if (r.fA > r.fB) {
53 r.fA = r.fB = (r.fA + r.fB) / 2;
54 }
Brian Salomonca6b2f42020-01-24 11:31:21 -050055 return r;
56 }
57
Brian Salomon53c909e2020-02-13 13:54:24 -050058 bool contains(Span r) const { return fA <= r.fA && fB >= r.fB; }
59 };
60 struct Result1D {
61 ShaderMode fShaderMode;
62 Span fShaderSubset;
63 Span fShaderClamp;
64 Mode fHWMode;
65 };
66
67 auto type = proxy.asTextureProxy()->textureType();
68 auto filter = sampler.filter();
69
Brian Salomonb9b13732020-06-25 11:13:49 -040070 auto resolve = [&](int size, Mode mode, Span subset, Span domain, float bilerpInset) {
Brian Salomon53c909e2020-02-13 13:54:24 -050071 Result1D r;
Brian Salomond71548a2020-02-29 19:43:30 -050072 bool canDoModeInHW = true;
73 // TODO: Use HW border color when available.
74 if (mode == Mode::kClampToBorder &&
75 (!caps.clampToBorderSupport() || border[0] || border[1] || border[2] || border[3])) {
76 canDoModeInHW = false;
77 } else if (mode != Mode::kClamp && !caps.npotTextureTileSupport() && !SkIsPow2(size)) {
78 canDoModeInHW = false;
79 } else if (type != GrTextureType::k2D &&
80 !(mode == Mode::kClamp || mode == Mode::kClampToBorder)) {
81 canDoModeInHW = false;
82 }
83 if (canDoModeInHW && size > 0 && subset.fA <= 0 && subset.fB >= size) {
Brian Salomon53c909e2020-02-13 13:54:24 -050084 r.fShaderMode = ShaderMode::kNone;
85 r.fHWMode = mode;
86 r.fShaderSubset = r.fShaderClamp = {0, 0};
87 return r;
88 }
89
90 r.fShaderSubset = subset;
Brian Salomoned729f92020-02-05 12:15:18 -050091 bool domainIsSafe = false;
Brian Salomoned729f92020-02-05 12:15:18 -050092 if (filter == Filter::kNearest) {
93 Span isubset{sk_float_floor(subset.fA), sk_float_ceil(subset.fB)};
94 if (domain.fA > isubset.fA && domain.fB < isubset.fB) {
95 domainIsSafe = true;
96 }
Brian Salomon53c909e2020-02-13 13:54:24 -050097 // This inset prevents sampling neighboring texels that could occur when
98 // texture coords fall exactly at texel boundaries (depending on precision
99 // and GPU-specific snapping at the boundary).
100 r.fShaderClamp = isubset.makeInset(0.5f);
101 } else {
Brian Salomonb9b13732020-06-25 11:13:49 -0400102 r.fShaderClamp = subset.makeInset(bilerpInset);
Brian Salomon53c909e2020-02-13 13:54:24 -0500103 if (r.fShaderClamp.contains(domain)) {
104 domainIsSafe = true;
Brian Salomoned729f92020-02-05 12:15:18 -0500105 }
Brian Salomoned729f92020-02-05 12:15:18 -0500106 }
Brian Salomon53c909e2020-02-13 13:54:24 -0500107 if (domainIsSafe) {
108 // The domain of coords that will be used won't access texels outside of the subset.
109 // So the wrap mode effectively doesn't matter. We use kClamp since it is always
110 // supported.
Brian Salomonca6b2f42020-01-24 11:31:21 -0500111 r.fShaderMode = ShaderMode::kNone;
Brian Salomon53c909e2020-02-13 13:54:24 -0500112 r.fHWMode = Mode::kClamp;
113 r.fShaderSubset = r.fShaderClamp = {0, 0};
Brian Salomonca6b2f42020-01-24 11:31:21 -0500114 return r;
115 }
Brian Salomon91dea512020-06-18 11:05:33 -0400116 r.fShaderMode = GetShaderMode(mode, filter);
Brian Salomonca6b2f42020-01-24 11:31:21 -0500117 r.fHWMode = Mode::kClamp;
118 return r;
119 };
120
121 SkISize dim = proxy.isFullyLazy() ? SkISize{-1, -1} : proxy.backingStoreDimensions();
122
123 Span subsetX{subset.fLeft, subset.fRight};
124 auto domainX = domain ? Span{domain->fLeft, domain->fRight}
125 : Span{SK_FloatNegativeInfinity, SK_FloatInfinity};
Brian Salomonb9b13732020-06-25 11:13:49 -0400126 auto x = resolve(dim.width(), sampler.wrapModeX(), subsetX, domainX, bilerpInset.fX);
Brian Salomonca6b2f42020-01-24 11:31:21 -0500127
128 Span subsetY{subset.fTop, subset.fBottom};
129 auto domainY = domain ? Span{domain->fTop, domain->fBottom}
130 : Span{SK_FloatNegativeInfinity, SK_FloatInfinity};
Brian Salomonb9b13732020-06-25 11:13:49 -0400131 auto y = resolve(dim.height(), sampler.wrapModeY(), subsetY, domainY, bilerpInset.fY);
Brian Salomonca6b2f42020-01-24 11:31:21 -0500132
Brian Salomon53c909e2020-02-13 13:54:24 -0500133 fHWSampler = {x.fHWMode, y.fHWMode, filter};
Brian Salomonca6b2f42020-01-24 11:31:21 -0500134 fShaderModes[0] = x.fShaderMode;
135 fShaderModes[1] = y.fShaderMode;
136 fShaderSubset = {x.fShaderSubset.fA, y.fShaderSubset.fA,
137 x.fShaderSubset.fB, y.fShaderSubset.fB};
Brian Salomon53c909e2020-02-13 13:54:24 -0500138 fShaderClamp = {x.fShaderClamp.fA, y.fShaderClamp.fA,
139 x.fShaderClamp.fB, y.fShaderClamp.fB};
Brian Salomond71548a2020-02-29 19:43:30 -0500140 std::copy_n(border, 4, fBorder);
Brian Salomonca6b2f42020-01-24 11:31:21 -0500141}
142
Brian Salomond71548a2020-02-29 19:43:30 -0500143bool GrTextureEffect::Sampling::hasBorderAlpha() const {
144 if (fHWSampler.wrapModeX() == GrSamplerState::WrapMode::kClampToBorder ||
145 fHWSampler.wrapModeY() == GrSamplerState::WrapMode::kClampToBorder) {
146 return true;
147 }
Brian Salomon91dea512020-06-18 11:05:33 -0400148 if (ShaderModeIsClampToBorder(fShaderModes[0]) || ShaderModeIsClampToBorder(fShaderModes[1])) {
Brian Salomond71548a2020-02-29 19:43:30 -0500149 return fBorder[3] < 1.f;
150 }
151 return false;
Brian Salomonca6b2f42020-01-24 11:31:21 -0500152}
153
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400154static void get_matrix(const SkMatrix& preMatrix, const GrSurfaceProxyView& view,
155 SkMatrix* outMatrix, bool* outLazyProxyNormalization) {
156 SkMatrix combined = preMatrix;
157 bool normalize = view.proxy()->backendFormat().textureType() != GrTextureType::kRectangle;
158 if (normalize) {
159 if (view.proxy()->isFullyLazy()) {
160 *outLazyProxyNormalization = true;
161 } else {
162 SkMatrixPriv::PostIDiv(&combined, view.proxy()->backingStoreDimensions().fWidth,
163 view.proxy()->backingStoreDimensions().fHeight);
164 *outLazyProxyNormalization = false;
165 }
166 } else {
167 *outLazyProxyNormalization = false;
168 }
169 if (view.origin() == kBottomLeft_GrSurfaceOrigin) {
170 if (normalize) {
171 // combined.postScale(1,-1);
172 // combined.postTranslate(0,1);
173 combined.set(SkMatrix::kMSkewY,
174 combined[SkMatrix::kMPersp0] - combined[SkMatrix::kMSkewY]);
175 combined.set(SkMatrix::kMScaleY,
176 combined[SkMatrix::kMPersp1] - combined[SkMatrix::kMScaleY]);
177 combined.set(SkMatrix::kMTransY,
178 combined[SkMatrix::kMPersp2] - combined[SkMatrix::kMTransY]);
179 } else {
180 // combined.postScale(1, -1);
181 // combined.postTranslate(0,1);
182 SkScalar h = view.proxy()->backingStoreDimensions().fHeight;
183 combined.set(SkMatrix::kMSkewY,
184 h * combined[SkMatrix::kMPersp0] - combined[SkMatrix::kMSkewY]);
185 combined.set(SkMatrix::kMScaleY,
186 h * combined[SkMatrix::kMPersp1] - combined[SkMatrix::kMScaleY]);
187 combined.set(SkMatrix::kMTransY,
188 h * combined[SkMatrix::kMPersp2] - combined[SkMatrix::kMTransY]);
189 }
190 }
191 *outMatrix = combined;
192}
193
Greg Danield2ccbb52020-02-05 10:45:39 -0500194std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(GrSurfaceProxyView view,
Brian Salomonb8f098d2020-01-07 11:15:44 -0500195 SkAlphaType alphaType,
196 const SkMatrix& matrix,
Brian Salomon53c909e2020-02-13 13:54:24 -0500197 Filter filter) {
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400198 SkMatrix final;
199 bool lazyProxyNormalization;
200 get_matrix(matrix, view, &final, &lazyProxyNormalization);
Brian Osman273944a2020-05-29 16:44:26 -0400201 return GrMatrixEffect::Make(final, std::unique_ptr<GrFragmentProcessor>(
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400202 new GrTextureEffect(std::move(view),
203 alphaType,
204 Sampling(filter),
205 lazyProxyNormalization)));
Brian Salomonca6b2f42020-01-24 11:31:21 -0500206}
207
Greg Danield2ccbb52020-02-05 10:45:39 -0500208std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(GrSurfaceProxyView view,
Brian Salomonca6b2f42020-01-24 11:31:21 -0500209 SkAlphaType alphaType,
210 const SkMatrix& matrix,
211 GrSamplerState sampler,
Brian Salomond71548a2020-02-29 19:43:30 -0500212 const GrCaps& caps,
213 const float border[4]) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500214 Sampling sampling(*view.proxy(), sampler, SkRect::Make(view.proxy()->dimensions()), nullptr,
Brian Salomond71548a2020-02-29 19:43:30 -0500215 border, caps);
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400216 SkMatrix final;
217 bool lazyProxyNormalization;
218 get_matrix(matrix, view, &final, &lazyProxyNormalization);
Brian Osman273944a2020-05-29 16:44:26 -0400219 return GrMatrixEffect::Make(final, std::unique_ptr<GrFragmentProcessor>(
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400220 new GrTextureEffect(std::move(view),
221 alphaType,
222 sampling,
223 lazyProxyNormalization)));
Brian Salomonca6b2f42020-01-24 11:31:21 -0500224}
225
Greg Danield2ccbb52020-02-05 10:45:39 -0500226std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyView view,
Brian Salomonca6b2f42020-01-24 11:31:21 -0500227 SkAlphaType alphaType,
228 const SkMatrix& matrix,
229 GrSamplerState sampler,
230 const SkRect& subset,
Brian Salomond71548a2020-02-29 19:43:30 -0500231 const GrCaps& caps,
232 const float border[4]) {
233 Sampling sampling(*view.proxy(), sampler, subset, nullptr, border, caps);
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400234 SkMatrix final;
235 bool lazyProxyNormalization;
236 get_matrix(matrix, view, &final, &lazyProxyNormalization);
Brian Osman273944a2020-05-29 16:44:26 -0400237 return GrMatrixEffect::Make(final, std::unique_ptr<GrFragmentProcessor>(
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400238 new GrTextureEffect(std::move(view),
239 alphaType,
240 sampling,
241 lazyProxyNormalization)));
Brian Salomonca6b2f42020-01-24 11:31:21 -0500242}
243
Greg Danield2ccbb52020-02-05 10:45:39 -0500244std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyView view,
Brian Salomonca6b2f42020-01-24 11:31:21 -0500245 SkAlphaType alphaType,
246 const SkMatrix& matrix,
247 GrSamplerState sampler,
248 const SkRect& subset,
249 const SkRect& domain,
Brian Salomond71548a2020-02-29 19:43:30 -0500250 const GrCaps& caps,
251 const float border[4]) {
252 Sampling sampling(*view.proxy(), sampler, subset, &domain, border, caps);
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400253 SkMatrix final;
254 bool lazyProxyNormalization;
255 get_matrix(matrix, view, &final, &lazyProxyNormalization);
Brian Osman273944a2020-05-29 16:44:26 -0400256 return GrMatrixEffect::Make(final, std::unique_ptr<GrFragmentProcessor>(
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400257 new GrTextureEffect(std::move(view),
258 alphaType,
259 sampling,
260 lazyProxyNormalization)));
Brian Salomonb8f098d2020-01-07 11:15:44 -0500261}
262
Brian Salomonb9b13732020-06-25 11:13:49 -0400263std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeBilerpWithInset(
264 GrSurfaceProxyView view,
265 SkAlphaType alphaType,
266 const SkMatrix& matrix,
267 GrSamplerState::WrapMode wx,
268 GrSamplerState::WrapMode wy,
269 const SkRect& subset,
270 SkVector inset,
271 const GrCaps& caps,
272 const float border[4]) {
273 GrSamplerState sampler(wx, wy, GrSamplerState::Filter::kBilerp);
274 Sampling sampling(*view.proxy(), sampler, subset, nullptr, border, caps, inset);
275 SkMatrix final;
276 bool lazyProxyNormalization;
277 get_matrix(matrix, view, &final, &lazyProxyNormalization);
278 return GrMatrixEffect::Make(
279 final, std::unique_ptr<GrFragmentProcessor>(new GrTextureEffect(
280 std::move(view), alphaType, sampling, lazyProxyNormalization)));
281}
282
Brian Salomon91dea512020-06-18 11:05:33 -0400283GrTextureEffect::ShaderMode GrTextureEffect::GetShaderMode(GrSamplerState::WrapMode mode,
284 GrSamplerState::Filter filter) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500285 switch (mode) {
Brian Salomon91dea512020-06-18 11:05:33 -0400286 case GrSamplerState::WrapMode::kMirrorRepeat:
287 return ShaderMode::kMirrorRepeat;
288 case GrSamplerState::WrapMode::kClamp:
289 return ShaderMode::kClamp;
290 case GrSamplerState::WrapMode::kRepeat:
Brian Salomon53c909e2020-02-13 13:54:24 -0500291 switch (filter) {
292 case GrSamplerState::Filter::kNearest:
Brian Salomon91dea512020-06-18 11:05:33 -0400293 return ShaderMode::kRepeatNearest;
Brian Salomon53c909e2020-02-13 13:54:24 -0500294 case GrSamplerState::Filter::kBilerp:
Brian Salomon91dea512020-06-18 11:05:33 -0400295 return ShaderMode::kRepeatBilerp;
Brian Salomon53c909e2020-02-13 13:54:24 -0500296 case GrSamplerState::Filter::kMipMap:
Brian Salomon91dea512020-06-18 11:05:33 -0400297 return ShaderMode::kRepeatMipMap;
Brian Salomon53c909e2020-02-13 13:54:24 -0500298 }
299 SkUNREACHABLE;
Brian Salomon91dea512020-06-18 11:05:33 -0400300 case GrSamplerState::WrapMode::kClampToBorder:
301 return filter == GrSamplerState::Filter::kNearest ? ShaderMode::kClampToBorderNearest
302 : ShaderMode::kClampToBorderFilter;
Brian Salomon53c909e2020-02-13 13:54:24 -0500303 }
304 SkUNREACHABLE;
305}
306
Brian Salomon91dea512020-06-18 11:05:33 -0400307inline bool GrTextureEffect::ShaderModeIsClampToBorder(ShaderMode m) {
308 return m == ShaderMode::kClampToBorderNearest || m == ShaderMode::kClampToBorderFilter;
309}
310
Brian Salomonb8f098d2020-01-07 11:15:44 -0500311GrGLSLFragmentProcessor* GrTextureEffect::onCreateGLSLInstance() const {
312 class Impl : public GrGLSLFragmentProcessor {
Brian Salomonca6b2f42020-01-24 11:31:21 -0500313 UniformHandle fSubsetUni;
Brian Salomon53c909e2020-02-13 13:54:24 -0500314 UniformHandle fClampUni;
315 UniformHandle fNormUni;
Brian Salomond71548a2020-02-29 19:43:30 -0500316 UniformHandle fBorderUni;
Brian Salomonca6b2f42020-01-24 11:31:21 -0500317
Brian Salomonb8f098d2020-01-07 11:15:44 -0500318 public:
319 void emitCode(EmitArgs& args) override {
Ethan Nicholas94996eb2020-04-01 13:46:10 -0400320 auto& te = args.fFp.cast<GrTextureEffect>();
Brian Salomonb8f098d2020-01-07 11:15:44 -0500321 auto* fb = args.fFragBuilder;
Michael Ludwig89dd4e72020-06-03 15:39:06 -0400322
Brian Salomonca6b2f42020-01-24 11:31:21 -0500323 if (te.fShaderModes[0] == ShaderMode::kNone &&
324 te.fShaderModes[1] == ShaderMode::kNone) {
Ethan Nicholas36a3e012020-04-17 19:00:28 +0000325 fb->codeAppendf("%s = ", args.fOutputColor);
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400326 if (te.fLazyProxyNormalization) {
327 const char* norm = nullptr;
328 fNormUni = args.fUniformHandler->addUniform(&te, kFragment_GrShaderFlag,
329 kFloat4_GrSLType, "norm", &norm);
330 fb->appendTextureLookupAndBlend(args.fInputColor, SkBlendMode::kModulate,
331 args.fTexSamplers[0],
Michael Ludwige88320b2020-06-24 09:04:56 -0400332 SkStringPrintf("%s * %s.zw", args.fSampleCoord,
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400333 norm).c_str());
334 } else {
335 fb->appendTextureLookupAndBlend(args.fInputColor, SkBlendMode::kModulate,
Michael Ludwige88320b2020-06-24 09:04:56 -0400336 args.fTexSamplers[0], args.fSampleCoord);
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400337 }
Brian Salomonca6b2f42020-01-24 11:31:21 -0500338 fb->codeAppendf(";");
339 } else {
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400340 // Tripping this assert means we have a normalized fully lazy proxy with a
341 // non-default ShaderMode. There's nothing fundamentally wrong with doing that, but
342 // it hasn't been tested and this code path probably won't handle normalization
343 // properly in that case.
344 SkASSERT(!te.fLazyProxyNormalization);
Brian Salomon53c909e2020-02-13 13:54:24 -0500345 // Here is the basic flow of the various ShaderModes are implemented in a series of
346 // steps. Not all the steps apply to all the modes. We try to emit only the steps
347 // that are necessary for the given x/y shader modes.
348 //
349 // 0) Start with interpolated coordinates (unnormalize if doing anything
350 // complicated).
351 // 1) Map the coordinates into the subset range [Repeat and MirrorRepeat], or pass
352 // through output of 0).
353 // 2) Clamp the coordinates to a 0.5 inset of the subset rect [Clamp, Repeat, and
Brian Salomond71548a2020-02-29 19:43:30 -0500354 // MirrorRepeat always or ClampToBorder only when filtering] or pass through
355 // output of 1). The clamp rect collapses to a line or point it if the subset
356 // rect is less than one pixel wide/tall.
Brian Salomon53c909e2020-02-13 13:54:24 -0500357 // 3) Look up texture with output of 2) [All]
358 // 3) Use the difference between 1) and 2) to apply filtering at edge [Repeat or
Brian Salomond71548a2020-02-29 19:43:30 -0500359 // ClampToBorder]. In the Repeat case this requires extra texture lookups on the
360 // other side of the subset (up to 3 more reads). Or if ClampToBorder and not
361 // filtering do a hard less than/greater than test with the subset rect.
Brian Salomonca6b2f42020-01-24 11:31:21 -0500362
Brian Salomon53c909e2020-02-13 13:54:24 -0500363 // Convert possible projective texture coordinates into non-homogeneous half2.
Michael Ludwige88320b2020-06-24 09:04:56 -0400364 fb->codeAppendf("float2 inCoord = %s;", args.fSampleCoord);
Brian Salomon66356c22020-02-11 15:22:18 -0500365
Brian Salomon53c909e2020-02-13 13:54:24 -0500366 const auto& m = te.fShaderModes;
Robert Phillips6eb5cb92020-03-05 12:52:45 -0500367 GrTextureType textureType = te.fSampler.proxy()->backendFormat().textureType();
368 bool normCoords = textureType != GrTextureType::kRectangle;
Brian Salomon53c909e2020-02-13 13:54:24 -0500369
Brian Salomond71548a2020-02-29 19:43:30 -0500370 const char* borderName = nullptr;
Brian Salomon91dea512020-06-18 11:05:33 -0400371 if (te.hasClampToBorderShaderMode()) {
Brian Salomond71548a2020-02-29 19:43:30 -0500372 fBorderUni = args.fUniformHandler->addUniform(
Ethan Nicholas16464c32020-04-06 13:53:05 -0400373 &te, kFragment_GrShaderFlag, kHalf4_GrSLType, "border", &borderName);
Brian Salomond71548a2020-02-29 19:43:30 -0500374 }
Brian Salomon53c909e2020-02-13 13:54:24 -0500375 auto modeUsesSubset = [](ShaderMode m) {
Brian Salomon91dea512020-06-18 11:05:33 -0400376 switch (m) {
377 case ShaderMode::kNone: return false;
378 case ShaderMode::kClamp: return false;
379 case ShaderMode::kRepeatNearest: return true;
380 case ShaderMode::kRepeatBilerp: return true;
381 case ShaderMode::kRepeatMipMap: return true;
382 case ShaderMode::kMirrorRepeat: return true;
383 case ShaderMode::kClampToBorderNearest: return true;
384 case ShaderMode::kClampToBorderFilter: return true;
385 }
386 SkUNREACHABLE;
Brian Salomon53c909e2020-02-13 13:54:24 -0500387 };
388
Brian Salomon91dea512020-06-18 11:05:33 -0400389 auto modeUsesClamp = [](ShaderMode m) {
390 switch (m) {
391 case ShaderMode::kNone: return false;
392 case ShaderMode::kClamp: return true;
393 case ShaderMode::kRepeatNearest: return true;
394 case ShaderMode::kRepeatBilerp: return true;
395 case ShaderMode::kRepeatMipMap: return true;
396 case ShaderMode::kMirrorRepeat: return true;
397 case ShaderMode::kClampToBorderNearest: return false;
398 case ShaderMode::kClampToBorderFilter: return true;
399 }
400 SkUNREACHABLE;
401 };
402
403 // To keep things a little simpler, when we have filtering logic in the shader we
404 // operate on unnormalized texture coordinates. We will add a uniform that stores
405 // {w, h, 1/w, 1/h} in a float4 below.
406 auto modeRequiresUnormCoords = [](ShaderMode m) {
407 switch (m) {
408 case ShaderMode::kNone: return false;
409 case ShaderMode::kClamp: return false;
Brian Salomond610c752020-06-18 14:41:53 -0400410 case ShaderMode::kRepeatNearest: return false;
Brian Salomon91dea512020-06-18 11:05:33 -0400411 case ShaderMode::kRepeatBilerp: return true;
412 case ShaderMode::kRepeatMipMap: return true;
413 case ShaderMode::kMirrorRepeat: return false;
414 case ShaderMode::kClampToBorderNearest: return true;
415 case ShaderMode::kClampToBorderFilter: return true;
416 }
417 SkUNREACHABLE;
Brian Salomon53c909e2020-02-13 13:54:24 -0500418 };
419
420 bool useSubset[2] = {modeUsesSubset(m[0]), modeUsesSubset(m[1])};
421 bool useClamp [2] = {modeUsesClamp (m[0]), modeUsesClamp (m[1])};
422
423 const char* subsetName = nullptr;
424 if (useSubset[0] || useSubset[1]) {
425 fSubsetUni = args.fUniformHandler->addUniform(
Ethan Nicholas16464c32020-04-06 13:53:05 -0400426 &te, kFragment_GrShaderFlag, kFloat4_GrSLType, "subset", &subsetName);
Brian Salomon53c909e2020-02-13 13:54:24 -0500427 }
428
429 const char* clampName = nullptr;
430 if (useClamp[0] || useClamp[1]) {
431 fClampUni = args.fUniformHandler->addUniform(
Ethan Nicholas16464c32020-04-06 13:53:05 -0400432 &te, kFragment_GrShaderFlag, kFloat4_GrSLType, "clamp", &clampName);
Brian Salomon53c909e2020-02-13 13:54:24 -0500433 }
434
Brian Salomon53c909e2020-02-13 13:54:24 -0500435 const char* norm = nullptr;
Brian Salomon91dea512020-06-18 11:05:33 -0400436 if (normCoords && (modeRequiresUnormCoords(m[0]) ||
437 modeRequiresUnormCoords(m[1]))) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500438 // TODO: Detect support for textureSize() or polyfill textureSize() in SkSL and
439 // always use?
Ethan Nicholas16464c32020-04-06 13:53:05 -0400440 fNormUni = args.fUniformHandler->addUniform(&te, kFragment_GrShaderFlag,
Brian Salomon53c909e2020-02-13 13:54:24 -0500441 kFloat4_GrSLType, "norm", &norm);
442 // TODO: Remove the normalization from the CoordTransform to skip unnormalizing
443 // step here.
444 fb->codeAppendf("inCoord *= %s.xy;", norm);
445 }
446
447 // Generates a string to read at a coordinate, normalizing coords if necessary.
Brian Salomonf46f19b2020-02-13 14:11:28 -0500448 auto read = [&](const char* coord) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500449 SkString result;
450 SkString normCoord;
451 if (norm) {
452 normCoord.printf("(%s) * %s.zw", coord, norm);
453 } else {
454 normCoord = coord;
455 }
Brian Salomonf46f19b2020-02-13 14:11:28 -0500456 fb->appendTextureLookup(&result, args.fTexSamplers[0], normCoord.c_str());
Brian Salomon53c909e2020-02-13 13:54:24 -0500457 return result;
458 };
459
460 // Implements coord wrapping for kRepeat and kMirrorRepeat
Brian Salomonf46f19b2020-02-13 14:11:28 -0500461 auto subsetCoord = [&](ShaderMode mode,
462 const char* coordSwizzle,
463 const char* subsetStartSwizzle,
464 const char* subsetStopSwizzle,
465 const char* extraCoord,
466 const char* coordWeight) {
Brian Salomon66356c22020-02-11 15:22:18 -0500467 switch (mode) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500468 // These modes either don't use the subset rect or don't need to map the
469 // coords to be within the subset.
Brian Salomon66356c22020-02-11 15:22:18 -0500470 case ShaderMode::kNone:
Brian Salomon91dea512020-06-18 11:05:33 -0400471 case ShaderMode::kClampToBorderNearest:
472 case ShaderMode::kClampToBorderFilter:
Brian Salomon66356c22020-02-11 15:22:18 -0500473 case ShaderMode::kClamp:
Brian Salomon53c909e2020-02-13 13:54:24 -0500474 fb->codeAppendf("subsetCoord.%s = inCoord.%s;", coordSwizzle,
475 coordSwizzle);
Brian Salomon66356c22020-02-11 15:22:18 -0500476 break;
Brian Salomon91dea512020-06-18 11:05:33 -0400477 case ShaderMode::kRepeatNearest:
478 case ShaderMode::kRepeatBilerp:
479 fb->codeAppendf(
480 "subsetCoord.%s = mod(inCoord.%s - %s.%s, %s.%s - %s.%s) + "
481 "%s.%s;",
482 coordSwizzle, coordSwizzle, subsetName, subsetStartSwizzle,
483 subsetName, subsetStopSwizzle, subsetName, subsetStartSwizzle,
484 subsetName, subsetStartSwizzle);
Brian Salomon66356c22020-02-11 15:22:18 -0500485 break;
Brian Salomon91dea512020-06-18 11:05:33 -0400486 case ShaderMode::kRepeatMipMap:
487 // The approach here is to generate two sets of texture coords that
488 // are both "moving" at the same speed (if not direction) as
489 // inCoords. We accomplish that by using two out of phase mirror
490 // repeat coords. We will always sample using both coords but the
491 // read from the upward sloping one is selected using a weight
492 // that transitions from one set to the other near the reflection
493 // point. Like the coords, the weight is a saw-tooth function,
494 // phase-shifted, vertically translated, and then clamped to 0..1.
495 // TODO: Skip this and use textureGrad() when available.
496 SkASSERT(extraCoord);
497 SkASSERT(coordWeight);
498 fb->codeAppend("{");
499 fb->codeAppendf("float w = %s.%s - %s.%s;", subsetName,
500 subsetStopSwizzle, subsetName, subsetStartSwizzle);
501 fb->codeAppendf("float w2 = 2 * w;");
502 fb->codeAppendf("float d = inCoord.%s - %s.%s;", coordSwizzle,
503 subsetName, subsetStartSwizzle);
504 fb->codeAppend("float m = mod(d, w2);");
505 fb->codeAppend("float o = mix(m, w2 - m, step(w, m));");
506 fb->codeAppendf("subsetCoord.%s = o + %s.%s;", coordSwizzle, subsetName,
507 subsetStartSwizzle);
508 fb->codeAppendf("%s = w - o + %s.%s;", extraCoord, subsetName,
509 subsetStartSwizzle);
510 // coordWeight is used as the third param of mix() to blend between a
511 // sample taken using subsetCoord and a sample at extraCoord.
512 fb->codeAppend("float hw = w/2;");
513 fb->codeAppend("float n = mod(d - hw, w2);");
514 fb->codeAppendf(
515 "%s = saturate(half(mix(n, w2 - n, step(w, n)) - hw + "
516 "0.5));",
517 coordWeight);
518 fb->codeAppend("}");
519 break;
520 case ShaderMode::kMirrorRepeat:
Brian Salomon66356c22020-02-11 15:22:18 -0500521 fb->codeAppend("{");
522 fb->codeAppendf("float w = %s.%s - %s.%s;", subsetName,
523 subsetStopSwizzle, subsetName, subsetStartSwizzle);
524 fb->codeAppendf("float w2 = 2 * w;");
525 fb->codeAppendf("float m = mod(inCoord.%s - %s.%s, w2);", coordSwizzle,
526 subsetName, subsetStartSwizzle);
Brian Salomon53c909e2020-02-13 13:54:24 -0500527 fb->codeAppendf("subsetCoord.%s = mix(m, w2 - m, step(w, m)) + %s.%s;",
Brian Salomon66356c22020-02-11 15:22:18 -0500528 coordSwizzle, subsetName, subsetStartSwizzle);
529 fb->codeAppend("}");
530 break;
Brian Salomon66356c22020-02-11 15:22:18 -0500531 }
532 };
533
Brian Salomonf46f19b2020-02-13 14:11:28 -0500534 auto clampCoord = [&](bool clamp,
535 const char* coordSwizzle,
536 const char* clampStartSwizzle,
537 const char* clampStopSwizzle) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500538 if (clamp) {
539 fb->codeAppendf("clampedCoord.%s = clamp(subsetCoord.%s, %s.%s, %s.%s);",
540 coordSwizzle, coordSwizzle, clampName, clampStartSwizzle,
541 clampName, clampStopSwizzle);
542 } else {
543 fb->codeAppendf("clampedCoord.%s = subsetCoord.%s;", coordSwizzle,
544 coordSwizzle);
545 }
546 };
547
Brian Salomonf46f19b2020-02-13 14:11:28 -0500548 // Insert vars for extra coords and blending weights for kRepeatMipMap.
549 const char* extraRepeatCoordX = nullptr;
550 const char* repeatCoordWeightX = nullptr;
551 const char* extraRepeatCoordY = nullptr;
552 const char* repeatCoordWeightY = nullptr;
Brian Salomon91dea512020-06-18 11:05:33 -0400553 if (m[0] == ShaderMode::kRepeatMipMap) {
Brian Salomonf46f19b2020-02-13 14:11:28 -0500554 fb->codeAppend("float extraRepeatCoordX; half repeatCoordWeightX;");
555 extraRepeatCoordX = "extraRepeatCoordX";
556 repeatCoordWeightX = "repeatCoordWeightX";
557 }
Brian Salomon91dea512020-06-18 11:05:33 -0400558 if (m[1] == ShaderMode::kRepeatMipMap) {
Brian Salomonf46f19b2020-02-13 14:11:28 -0500559 fb->codeAppend("float extraRepeatCoordY; half repeatCoordWeightY;");
560 extraRepeatCoordY = "extraRepeatCoordY";
561 repeatCoordWeightY = "repeatCoordWeightY";
562 }
563
564 // Apply subset rect and clamp rect to coords.
Brian Salomon53c909e2020-02-13 13:54:24 -0500565 fb->codeAppend("float2 subsetCoord;");
Brian Salomonf46f19b2020-02-13 14:11:28 -0500566 subsetCoord(te.fShaderModes[0], "x", "x", "z", extraRepeatCoordX,
567 repeatCoordWeightX);
568 subsetCoord(te.fShaderModes[1], "y", "y", "w", extraRepeatCoordY,
569 repeatCoordWeightY);
Brian Salomon53c909e2020-02-13 13:54:24 -0500570 fb->codeAppend("float2 clampedCoord;");
571 clampCoord(useClamp[0], "x", "x", "z");
572 clampCoord(useClamp[1], "y", "y", "w");
Brian Salomonca6b2f42020-01-24 11:31:21 -0500573
Brian Salomonf46f19b2020-02-13 14:11:28 -0500574 // Additional clamping for the extra coords for kRepeatMipMap.
Brian Salomon91dea512020-06-18 11:05:33 -0400575 if (m[0] == ShaderMode::kRepeatMipMap) {
Brian Salomonf46f19b2020-02-13 14:11:28 -0500576 fb->codeAppendf("extraRepeatCoordX = clamp(extraRepeatCoordX, %s.x, %s.z);",
577 clampName, clampName);
578 }
Brian Salomon91dea512020-06-18 11:05:33 -0400579 if (m[1] == ShaderMode::kRepeatMipMap) {
Brian Salomonf46f19b2020-02-13 14:11:28 -0500580 fb->codeAppendf("extraRepeatCoordY = clamp(extraRepeatCoordY, %s.y, %s.w);",
581 clampName, clampName);
582 }
583
584 // Do the 2 or 4 texture reads for kRepeatMipMap and then apply the weight(s)
585 // to blend between them. If neither direction is kRepeatMipMap do a single
586 // read at clampedCoord.
Brian Salomon91dea512020-06-18 11:05:33 -0400587 if (m[0] == ShaderMode::kRepeatMipMap && m[1] == ShaderMode::kRepeatMipMap) {
Brian Salomonf46f19b2020-02-13 14:11:28 -0500588 fb->codeAppendf(
589 "half4 textureColor ="
590 " mix(mix(%s, %s, repeatCoordWeightX),"
591 " mix(%s, %s, repeatCoordWeightX),"
592 " repeatCoordWeightY);",
593 read("clampedCoord").c_str(),
594 read("float2(extraRepeatCoordX, clampedCoord.y)").c_str(),
595 read("float2(clampedCoord.x, extraRepeatCoordY)").c_str(),
596 read("float2(extraRepeatCoordX, extraRepeatCoordY)").c_str());
597
Brian Salomon91dea512020-06-18 11:05:33 -0400598 } else if (m[0] == ShaderMode::kRepeatMipMap) {
Brian Salomonf46f19b2020-02-13 14:11:28 -0500599 fb->codeAppendf("half4 textureColor = mix(%s, %s, repeatCoordWeightX);",
600 read("clampedCoord").c_str(),
601 read("float2(extraRepeatCoordX, clampedCoord.y)").c_str());
Brian Salomon91dea512020-06-18 11:05:33 -0400602 } else if (m[1] == ShaderMode::kRepeatMipMap) {
Brian Salomonf46f19b2020-02-13 14:11:28 -0500603 fb->codeAppendf("half4 textureColor = mix(%s, %s, repeatCoordWeightY);",
604 read("clampedCoord").c_str(),
605 read("float2(clampedCoord.x, extraRepeatCoordY)").c_str());
606 } else {
607 fb->codeAppendf("half4 textureColor = %s;", read("clampedCoord").c_str());
608 }
Brian Salomonca6b2f42020-01-24 11:31:21 -0500609
Brian Salomon53c909e2020-02-13 13:54:24 -0500610 // Strings for extra texture reads used only in kRepeatBilerp
611 SkString repeatBilerpReadX;
612 SkString repeatBilerpReadY;
613
614 // Calculate the amount the coord moved for clamping. This will be used
Brian Salomond71548a2020-02-29 19:43:30 -0500615 // to implement shader-based filtering for kClampToBorder and kRepeat.
Brian Salomon53c909e2020-02-13 13:54:24 -0500616
Brian Salomon91dea512020-06-18 11:05:33 -0400617 if (m[0] == ShaderMode::kRepeatBilerp || m[0] == ShaderMode::kClampToBorderFilter) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500618 fb->codeAppend("half errX = half(subsetCoord.x - clampedCoord.x);");
619 fb->codeAppendf("float repeatCoordX = errX > 0 ? %s.x : %s.z;", clampName,
620 clampName);
621 repeatBilerpReadX = read("float2(repeatCoordX, clampedCoord.y)");
Brian Salomonca6b2f42020-01-24 11:31:21 -0500622 }
Brian Salomon91dea512020-06-18 11:05:33 -0400623 if (m[1] == ShaderMode::kRepeatBilerp || m[1] == ShaderMode::kClampToBorderFilter) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500624 fb->codeAppend("half errY = half(subsetCoord.y - clampedCoord.y);");
625 fb->codeAppendf("float repeatCoordY = errY > 0 ? %s.y : %s.w;", clampName,
626 clampName);
627 repeatBilerpReadY = read("float2(clampedCoord.x, repeatCoordY)");
628 }
629
630 // Add logic for kRepeatBilerp. Do 1 or 3 more texture reads depending
631 // on whether both modes are kRepeat and whether we're near a single subset edge
632 // or a corner. Then blend the multiple reads using the err values calculated
633 // above.
634 const char* ifStr = "if";
Brian Salomon91dea512020-06-18 11:05:33 -0400635 if (m[0] == ShaderMode::kRepeatBilerp && m[1] == ShaderMode::kRepeatBilerp) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500636 auto repeatBilerpReadXY = read("float2(repeatCoordX, repeatCoordY)");
637 fb->codeAppendf(
638 "if (errX != 0 && errY != 0) {"
Brian Salomon3fcf83a2020-02-23 21:29:01 -0500639 " errX = abs(errX);"
Brian Salomon53c909e2020-02-13 13:54:24 -0500640 " textureColor = mix(mix(textureColor, %s, errX),"
641 " mix(%s, %s, errX),"
Brian Salomon3fcf83a2020-02-23 21:29:01 -0500642 " abs(errY));"
Brian Salomon53c909e2020-02-13 13:54:24 -0500643 "}",
644 repeatBilerpReadX.c_str(), repeatBilerpReadY.c_str(),
645 repeatBilerpReadXY.c_str());
646 ifStr = "else if";
647 }
Brian Salomon91dea512020-06-18 11:05:33 -0400648 if (m[0] == ShaderMode::kRepeatBilerp) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500649 fb->codeAppendf(
650 "%s (errX != 0) {"
651 " textureColor = mix(textureColor, %s, abs(errX));"
652 "}",
653 ifStr, repeatBilerpReadX.c_str());
654 }
Brian Salomon91dea512020-06-18 11:05:33 -0400655 if (m[1] == ShaderMode::kRepeatBilerp) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500656 fb->codeAppendf(
657 "%s (errY != 0) {"
658 " textureColor = mix(textureColor, %s, abs(errY));"
659 "}",
660 ifStr, repeatBilerpReadY.c_str());
661 }
662
Brian Salomond71548a2020-02-29 19:43:30 -0500663 // Do soft edge shader filtering against border color for kClampToBorderFilter using
Brian Salomon53c909e2020-02-13 13:54:24 -0500664 // the err values calculated above.
Brian Salomon91dea512020-06-18 11:05:33 -0400665 if (m[0] == ShaderMode::kClampToBorderFilter) {
Brian Salomond71548a2020-02-29 19:43:30 -0500666 fb->codeAppendf("textureColor = mix(textureColor, %s, min(abs(errX), 1));",
667 borderName);
Brian Salomon53c909e2020-02-13 13:54:24 -0500668 }
Brian Salomon91dea512020-06-18 11:05:33 -0400669 if (m[1] == ShaderMode::kClampToBorderFilter) {
Brian Salomond71548a2020-02-29 19:43:30 -0500670 fb->codeAppendf("textureColor = mix(textureColor, %s, min(abs(errY), 1));",
671 borderName);
Brian Salomon53c909e2020-02-13 13:54:24 -0500672 }
673
Brian Salomond71548a2020-02-29 19:43:30 -0500674 // Do hard-edge shader transition to border color for kClampToBorderNearest at the
Michael Ludwig724701f2020-06-18 17:52:49 -0400675 // subset boundaries. Snap the input coordinates to nearest neighbor (with an
676 // epsilon) before comparing to the subset rect to avoid GPU interpolation errors
Brian Salomon91dea512020-06-18 11:05:33 -0400677 if (m[0] == ShaderMode::kClampToBorderNearest) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500678 fb->codeAppendf(
Michael Ludwig724701f2020-06-18 17:52:49 -0400679 "float snappedX = floor(inCoord.x + 0.001) + 0.5;"
680 "if (snappedX < %s.x || snappedX > %s.z) {"
Brian Salomond71548a2020-02-29 19:43:30 -0500681 " textureColor = %s;"
Brian Salomon53c909e2020-02-13 13:54:24 -0500682 "}",
Brian Salomond71548a2020-02-29 19:43:30 -0500683 subsetName, subsetName, borderName);
Brian Salomon53c909e2020-02-13 13:54:24 -0500684 }
Brian Salomon91dea512020-06-18 11:05:33 -0400685 if (m[1] == ShaderMode::kClampToBorderNearest) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500686 fb->codeAppendf(
Michael Ludwig724701f2020-06-18 17:52:49 -0400687 "float snappedY = floor(inCoord.y + 0.001) + 0.5;"
688 "if (snappedY < %s.y || snappedY > %s.w) {"
Brian Salomond71548a2020-02-29 19:43:30 -0500689 " textureColor = %s;"
Brian Salomon53c909e2020-02-13 13:54:24 -0500690 "}",
Brian Salomond71548a2020-02-29 19:43:30 -0500691 subsetName, subsetName, borderName);
Brian Salomon53c909e2020-02-13 13:54:24 -0500692 }
693 fb->codeAppendf("%s = %s * textureColor;", args.fOutputColor, args.fInputColor);
Brian Salomonca6b2f42020-01-24 11:31:21 -0500694 }
695 }
696
697 protected:
698 void onSetData(const GrGLSLProgramDataManager& pdm,
699 const GrFragmentProcessor& fp) override {
700 const auto& te = fp.cast<GrTextureEffect>();
Brian Salomonca6b2f42020-01-24 11:31:21 -0500701
Brian Salomon53c909e2020-02-13 13:54:24 -0500702 const float w = te.fSampler.peekTexture()->width();
703 const float h = te.fSampler.peekTexture()->height();
704 const auto& s = te.fSubset;
705 const auto& c = te.fClamp;
Brian Salomonca6b2f42020-01-24 11:31:21 -0500706
Brian Salomon53c909e2020-02-13 13:54:24 -0500707 auto type = te.fSampler.peekTexture()->texturePriv().textureType();
708
709 float norm[4] = {w, h, 1.f/w, 1.f/h};
710
711 if (fNormUni.isValid()) {
712 pdm.set4fv(fNormUni, 1, norm);
713 SkASSERT(type != GrTextureType::kRectangle);
714 }
715
716 auto pushRect = [&](float rect[4], UniformHandle uni) {
Brian Salomonca6b2f42020-01-24 11:31:21 -0500717 if (te.fSampler.view().origin() == kBottomLeft_GrSurfaceOrigin) {
718 rect[1] = h - rect[1];
719 rect[3] = h - rect[3];
720 std::swap(rect[1], rect[3]);
721 }
Brian Salomon53c909e2020-02-13 13:54:24 -0500722 if (!fNormUni.isValid() && type != GrTextureType::kRectangle) {
723 rect[0] *= norm[2];
724 rect[2] *= norm[2];
725 rect[1] *= norm[3];
726 rect[3] *= norm[3];
Brian Salomonca6b2f42020-01-24 11:31:21 -0500727 }
Brian Salomon53c909e2020-02-13 13:54:24 -0500728 pdm.set4fv(uni, 1, rect);
729 };
Brian Salomonca6b2f42020-01-24 11:31:21 -0500730
Brian Salomon53c909e2020-02-13 13:54:24 -0500731 if (fSubsetUni.isValid()) {
732 float subset[] = {s.fLeft, s.fTop, s.fRight, s.fBottom};
733 pushRect(subset, fSubsetUni);
734 }
735 if (fClampUni.isValid()) {
736 float subset[] = {c.fLeft, c.fTop, c.fRight, c.fBottom};
737 pushRect(subset, fClampUni);
Brian Salomonca6b2f42020-01-24 11:31:21 -0500738 }
Brian Salomond71548a2020-02-29 19:43:30 -0500739 if (fBorderUni.isValid()) {
740 pdm.set4fv(fBorderUni, 1, te.fBorder);
741 }
Brian Salomonb8f098d2020-01-07 11:15:44 -0500742 }
743 };
744 return new Impl;
745}
746
Brian Salomonca6b2f42020-01-24 11:31:21 -0500747void GrTextureEffect::onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const {
Brian Salomonca6b2f42020-01-24 11:31:21 -0500748 auto m0 = static_cast<uint32_t>(fShaderModes[0]);
749 auto m1 = static_cast<uint32_t>(fShaderModes[1]);
Brian Salomon91dea512020-06-18 11:05:33 -0400750 b->add32((m0 << 16) | m1);
Brian Salomonca6b2f42020-01-24 11:31:21 -0500751}
Brian Salomonb8f098d2020-01-07 11:15:44 -0500752
Brian Salomonca6b2f42020-01-24 11:31:21 -0500753bool GrTextureEffect::onIsEqual(const GrFragmentProcessor& other) const {
Ethan Nicholas94996eb2020-04-01 13:46:10 -0400754 auto& that = other.cast<GrTextureEffect>();
Brian Salomond71548a2020-02-29 19:43:30 -0500755 if (fShaderModes[0] != that.fShaderModes[0] || fShaderModes[1] != that.fShaderModes[1]) {
756 return false;
757 }
758 if (fSubset != that.fSubset) {
759 return false;
760 }
Brian Salomon91dea512020-06-18 11:05:33 -0400761 if (this->hasClampToBorderShaderMode() && !std::equal(fBorder, fBorder + 4, that.fBorder)) {
Brian Salomond71548a2020-02-29 19:43:30 -0500762 return false;
763 }
764 return true;
Brian Salomonb8f098d2020-01-07 11:15:44 -0500765}
766
Greg Danield2ccbb52020-02-05 10:45:39 -0500767GrTextureEffect::GrTextureEffect(GrSurfaceProxyView view, SkAlphaType alphaType,
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400768 const Sampling& sampling, bool lazyProxyNormalization)
Brian Salomonb8f098d2020-01-07 11:15:44 -0500769 : GrFragmentProcessor(kGrTextureEffect_ClassID,
Brian Salomond71548a2020-02-29 19:43:30 -0500770 ModulateForSamplerOptFlags(alphaType, sampling.hasBorderAlpha()))
Greg Danield2ccbb52020-02-05 10:45:39 -0500771 , fSampler(std::move(view), sampling.fHWSampler)
Brian Salomonca6b2f42020-01-24 11:31:21 -0500772 , fSubset(sampling.fShaderSubset)
Brian Salomon53c909e2020-02-13 13:54:24 -0500773 , fClamp(sampling.fShaderClamp)
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400774 , fShaderModes{sampling.fShaderModes[0], sampling.fShaderModes[1]}
775 , fLazyProxyNormalization(lazyProxyNormalization) {
Brian Salomonca6b2f42020-01-24 11:31:21 -0500776 // We always compare the range even when it isn't used so assert we have canonical don't care
777 // values.
778 SkASSERT(fShaderModes[0] != ShaderMode::kNone || (fSubset.fLeft == 0 && fSubset.fRight == 0));
779 SkASSERT(fShaderModes[1] != ShaderMode::kNone || (fSubset.fTop == 0 && fSubset.fBottom == 0));
Brian Salomonb8f098d2020-01-07 11:15:44 -0500780 this->setTextureSamplerCnt(1);
Michael Ludwige88320b2020-06-24 09:04:56 -0400781 this->setUsesSampleCoordsDirectly();
Brian Salomond71548a2020-02-29 19:43:30 -0500782 std::copy_n(sampling.fBorder, 4, fBorder);
Brian Salomonb8f098d2020-01-07 11:15:44 -0500783}
784
785GrTextureEffect::GrTextureEffect(const GrTextureEffect& src)
786 : INHERITED(kGrTextureEffect_ClassID, src.optimizationFlags())
Brian Salomonca6b2f42020-01-24 11:31:21 -0500787 , fSampler(src.fSampler)
788 , fSubset(src.fSubset)
Brian Salomon922496f2020-02-14 12:20:14 -0500789 , fClamp(src.fClamp)
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400790 , fShaderModes{src.fShaderModes[0], src.fShaderModes[1]}
791 , fLazyProxyNormalization(src.fLazyProxyNormalization) {
Brian Salomonba90ce92020-03-04 12:38:04 -0500792 std::copy_n(src.fBorder, 4, fBorder);
Brian Salomonb8f098d2020-01-07 11:15:44 -0500793 this->setTextureSamplerCnt(1);
Michael Ludwige88320b2020-06-24 09:04:56 -0400794 this->setUsesSampleCoordsDirectly();
Brian Salomonb8f098d2020-01-07 11:15:44 -0500795}
796
797std::unique_ptr<GrFragmentProcessor> GrTextureEffect::clone() const {
798 return std::unique_ptr<GrFragmentProcessor>(new GrTextureEffect(*this));
799}
800
801const GrFragmentProcessor::TextureSampler& GrTextureEffect::onTextureSampler(int) const {
802 return fSampler;
803}
804
805GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrTextureEffect);
806#if GR_TEST_UTILS
807std::unique_ptr<GrFragmentProcessor> GrTextureEffect::TestCreate(GrProcessorTestData* testData) {
Greg Daniel026a60c2020-02-12 10:53:51 -0500808 auto [view, ct, at] = testData->randomView();
Brian Salomon53c909e2020-02-13 13:54:24 -0500809 Mode wrapModes[2];
Brian Salomonb8f098d2020-01-07 11:15:44 -0500810 GrTest::TestWrapModes(testData->fRandom, wrapModes);
Brian Salomonb8f098d2020-01-07 11:15:44 -0500811
Brian Salomon53c909e2020-02-13 13:54:24 -0500812 Filter filter;
813 if (view.asTextureProxy()->mipMapped() == GrMipMapped::kYes) {
814 switch (testData->fRandom->nextULessThan(3)) {
815 case 0:
816 filter = Filter::kNearest;
817 break;
818 case 1:
819 filter = Filter::kBilerp;
820 break;
821 default:
822 filter = Filter::kMipMap;
823 break;
824 }
825 } else {
826 filter = testData->fRandom->nextBool() ? Filter::kBilerp : Filter::kNearest;
827 }
828 GrSamplerState params(wrapModes, filter);
Brian Salomonb8f098d2020-01-07 11:15:44 -0500829
830 const SkMatrix& matrix = GrTest::TestMatrix(testData->fRandom);
Greg Danield2ccbb52020-02-05 10:45:39 -0500831 return GrTextureEffect::Make(std::move(view), at, matrix, params, *testData->caps());
Brian Salomonb8f098d2020-01-07 11:15:44 -0500832}
833#endif