blob: 41592502621efeafb17aa66e5a7317401ea84b66 [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"
Brian Osman273944a2020-05-29 16:44:26 -040012#include "src/gpu/effects/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 Salomon91dea512020-06-18 11:05:33 -0400113 r.fShaderMode = GetShaderMode(mode, filter);
Brian Salomonca6b2f42020-01-24 11:31:21 -0500114 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 }
Brian Salomon91dea512020-06-18 11:05:33 -0400145 if (ShaderModeIsClampToBorder(fShaderModes[0]) || ShaderModeIsClampToBorder(fShaderModes[1])) {
Brian Salomond71548a2020-02-29 19:43:30 -0500146 return fBorder[3] < 1.f;
147 }
148 return false;
Brian Salomonca6b2f42020-01-24 11:31:21 -0500149}
150
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400151static void get_matrix(const SkMatrix& preMatrix, const GrSurfaceProxyView& view,
152 SkMatrix* outMatrix, bool* outLazyProxyNormalization) {
153 SkMatrix combined = preMatrix;
154 bool normalize = view.proxy()->backendFormat().textureType() != GrTextureType::kRectangle;
155 if (normalize) {
156 if (view.proxy()->isFullyLazy()) {
157 *outLazyProxyNormalization = true;
158 } else {
159 SkMatrixPriv::PostIDiv(&combined, view.proxy()->backingStoreDimensions().fWidth,
160 view.proxy()->backingStoreDimensions().fHeight);
161 *outLazyProxyNormalization = false;
162 }
163 } else {
164 *outLazyProxyNormalization = false;
165 }
166 if (view.origin() == kBottomLeft_GrSurfaceOrigin) {
167 if (normalize) {
168 // combined.postScale(1,-1);
169 // combined.postTranslate(0,1);
170 combined.set(SkMatrix::kMSkewY,
171 combined[SkMatrix::kMPersp0] - combined[SkMatrix::kMSkewY]);
172 combined.set(SkMatrix::kMScaleY,
173 combined[SkMatrix::kMPersp1] - combined[SkMatrix::kMScaleY]);
174 combined.set(SkMatrix::kMTransY,
175 combined[SkMatrix::kMPersp2] - combined[SkMatrix::kMTransY]);
176 } else {
177 // combined.postScale(1, -1);
178 // combined.postTranslate(0,1);
179 SkScalar h = view.proxy()->backingStoreDimensions().fHeight;
180 combined.set(SkMatrix::kMSkewY,
181 h * combined[SkMatrix::kMPersp0] - combined[SkMatrix::kMSkewY]);
182 combined.set(SkMatrix::kMScaleY,
183 h * combined[SkMatrix::kMPersp1] - combined[SkMatrix::kMScaleY]);
184 combined.set(SkMatrix::kMTransY,
185 h * combined[SkMatrix::kMPersp2] - combined[SkMatrix::kMTransY]);
186 }
187 }
188 *outMatrix = combined;
189}
190
Greg Danield2ccbb52020-02-05 10:45:39 -0500191std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(GrSurfaceProxyView view,
Brian Salomonb8f098d2020-01-07 11:15:44 -0500192 SkAlphaType alphaType,
193 const SkMatrix& matrix,
Brian Salomon53c909e2020-02-13 13:54:24 -0500194 Filter filter) {
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400195 SkMatrix final;
196 bool lazyProxyNormalization;
197 get_matrix(matrix, view, &final, &lazyProxyNormalization);
Brian Osman273944a2020-05-29 16:44:26 -0400198 return GrMatrixEffect::Make(final, std::unique_ptr<GrFragmentProcessor>(
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400199 new GrTextureEffect(std::move(view),
200 alphaType,
201 Sampling(filter),
202 lazyProxyNormalization)));
Brian Salomonca6b2f42020-01-24 11:31:21 -0500203}
204
Greg Danield2ccbb52020-02-05 10:45:39 -0500205std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(GrSurfaceProxyView view,
Brian Salomonca6b2f42020-01-24 11:31:21 -0500206 SkAlphaType alphaType,
207 const SkMatrix& matrix,
208 GrSamplerState sampler,
Brian Salomond71548a2020-02-29 19:43:30 -0500209 const GrCaps& caps,
210 const float border[4]) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500211 Sampling sampling(*view.proxy(), sampler, SkRect::Make(view.proxy()->dimensions()), nullptr,
Brian Salomond71548a2020-02-29 19:43:30 -0500212 border, caps);
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400213 SkMatrix final;
214 bool lazyProxyNormalization;
215 get_matrix(matrix, view, &final, &lazyProxyNormalization);
Brian Osman273944a2020-05-29 16:44:26 -0400216 return GrMatrixEffect::Make(final, std::unique_ptr<GrFragmentProcessor>(
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400217 new GrTextureEffect(std::move(view),
218 alphaType,
219 sampling,
220 lazyProxyNormalization)));
Brian Salomonca6b2f42020-01-24 11:31:21 -0500221}
222
Greg Danield2ccbb52020-02-05 10:45:39 -0500223std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyView view,
Brian Salomonca6b2f42020-01-24 11:31:21 -0500224 SkAlphaType alphaType,
225 const SkMatrix& matrix,
226 GrSamplerState sampler,
227 const SkRect& subset,
Brian Salomond71548a2020-02-29 19:43:30 -0500228 const GrCaps& caps,
229 const float border[4]) {
230 Sampling sampling(*view.proxy(), sampler, subset, nullptr, border, caps);
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400231 SkMatrix final;
232 bool lazyProxyNormalization;
233 get_matrix(matrix, view, &final, &lazyProxyNormalization);
Brian Osman273944a2020-05-29 16:44:26 -0400234 return GrMatrixEffect::Make(final, std::unique_ptr<GrFragmentProcessor>(
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400235 new GrTextureEffect(std::move(view),
236 alphaType,
237 sampling,
238 lazyProxyNormalization)));
Brian Salomonca6b2f42020-01-24 11:31:21 -0500239}
240
Greg Danield2ccbb52020-02-05 10:45:39 -0500241std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyView view,
Brian Salomonca6b2f42020-01-24 11:31:21 -0500242 SkAlphaType alphaType,
243 const SkMatrix& matrix,
244 GrSamplerState sampler,
245 const SkRect& subset,
246 const SkRect& domain,
Brian Salomond71548a2020-02-29 19:43:30 -0500247 const GrCaps& caps,
248 const float border[4]) {
249 Sampling sampling(*view.proxy(), sampler, subset, &domain, border, caps);
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400250 SkMatrix final;
251 bool lazyProxyNormalization;
252 get_matrix(matrix, view, &final, &lazyProxyNormalization);
Brian Osman273944a2020-05-29 16:44:26 -0400253 return GrMatrixEffect::Make(final, std::unique_ptr<GrFragmentProcessor>(
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400254 new GrTextureEffect(std::move(view),
255 alphaType,
256 sampling,
257 lazyProxyNormalization)));
Brian Salomonb8f098d2020-01-07 11:15:44 -0500258}
259
Brian Salomon91dea512020-06-18 11:05:33 -0400260GrTextureEffect::ShaderMode GrTextureEffect::GetShaderMode(GrSamplerState::WrapMode mode,
261 GrSamplerState::Filter filter) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500262 switch (mode) {
Brian Salomon91dea512020-06-18 11:05:33 -0400263 case GrSamplerState::WrapMode::kMirrorRepeat:
264 return ShaderMode::kMirrorRepeat;
265 case GrSamplerState::WrapMode::kClamp:
266 return ShaderMode::kClamp;
267 case GrSamplerState::WrapMode::kRepeat:
Brian Salomon53c909e2020-02-13 13:54:24 -0500268 switch (filter) {
269 case GrSamplerState::Filter::kNearest:
Brian Salomon91dea512020-06-18 11:05:33 -0400270 return ShaderMode::kRepeatNearest;
Brian Salomon53c909e2020-02-13 13:54:24 -0500271 case GrSamplerState::Filter::kBilerp:
Brian Salomon91dea512020-06-18 11:05:33 -0400272 return ShaderMode::kRepeatBilerp;
Brian Salomon53c909e2020-02-13 13:54:24 -0500273 case GrSamplerState::Filter::kMipMap:
Brian Salomon91dea512020-06-18 11:05:33 -0400274 return ShaderMode::kRepeatMipMap;
Brian Salomon53c909e2020-02-13 13:54:24 -0500275 }
276 SkUNREACHABLE;
Brian Salomon91dea512020-06-18 11:05:33 -0400277 case GrSamplerState::WrapMode::kClampToBorder:
278 return filter == GrSamplerState::Filter::kNearest ? ShaderMode::kClampToBorderNearest
279 : ShaderMode::kClampToBorderFilter;
Brian Salomon53c909e2020-02-13 13:54:24 -0500280 }
281 SkUNREACHABLE;
282}
283
Brian Salomon91dea512020-06-18 11:05:33 -0400284inline bool GrTextureEffect::ShaderModeIsClampToBorder(ShaderMode m) {
285 return m == ShaderMode::kClampToBorderNearest || m == ShaderMode::kClampToBorderFilter;
286}
287
Brian Salomonb8f098d2020-01-07 11:15:44 -0500288GrGLSLFragmentProcessor* GrTextureEffect::onCreateGLSLInstance() const {
289 class Impl : public GrGLSLFragmentProcessor {
Brian Salomonca6b2f42020-01-24 11:31:21 -0500290 UniformHandle fSubsetUni;
Brian Salomon53c909e2020-02-13 13:54:24 -0500291 UniformHandle fClampUni;
292 UniformHandle fNormUni;
Brian Salomond71548a2020-02-29 19:43:30 -0500293 UniformHandle fBorderUni;
Brian Salomonca6b2f42020-01-24 11:31:21 -0500294
Brian Salomonb8f098d2020-01-07 11:15:44 -0500295 public:
296 void emitCode(EmitArgs& args) override {
Ethan Nicholas94996eb2020-04-01 13:46:10 -0400297 auto& te = args.fFp.cast<GrTextureEffect>();
Brian Salomonb8f098d2020-01-07 11:15:44 -0500298 auto* fb = args.fFragBuilder;
Michael Ludwig89dd4e72020-06-03 15:39:06 -0400299
Brian Salomonca6b2f42020-01-24 11:31:21 -0500300 if (te.fShaderModes[0] == ShaderMode::kNone &&
301 te.fShaderModes[1] == ShaderMode::kNone) {
Ethan Nicholas36a3e012020-04-17 19:00:28 +0000302 fb->codeAppendf("%s = ", args.fOutputColor);
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400303 if (te.fLazyProxyNormalization) {
304 const char* norm = nullptr;
305 fNormUni = args.fUniformHandler->addUniform(&te, kFragment_GrShaderFlag,
306 kFloat4_GrSLType, "norm", &norm);
307 fb->appendTextureLookupAndBlend(args.fInputColor, SkBlendMode::kModulate,
308 args.fTexSamplers[0],
Michael Ludwige88320b2020-06-24 09:04:56 -0400309 SkStringPrintf("%s * %s.zw", args.fSampleCoord,
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400310 norm).c_str());
311 } else {
312 fb->appendTextureLookupAndBlend(args.fInputColor, SkBlendMode::kModulate,
Michael Ludwige88320b2020-06-24 09:04:56 -0400313 args.fTexSamplers[0], args.fSampleCoord);
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400314 }
Brian Salomonca6b2f42020-01-24 11:31:21 -0500315 fb->codeAppendf(";");
316 } else {
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400317 // Tripping this assert means we have a normalized fully lazy proxy with a
318 // non-default ShaderMode. There's nothing fundamentally wrong with doing that, but
319 // it hasn't been tested and this code path probably won't handle normalization
320 // properly in that case.
321 SkASSERT(!te.fLazyProxyNormalization);
Brian Salomon53c909e2020-02-13 13:54:24 -0500322 // Here is the basic flow of the various ShaderModes are implemented in a series of
323 // steps. Not all the steps apply to all the modes. We try to emit only the steps
324 // that are necessary for the given x/y shader modes.
325 //
326 // 0) Start with interpolated coordinates (unnormalize if doing anything
327 // complicated).
328 // 1) Map the coordinates into the subset range [Repeat and MirrorRepeat], or pass
329 // through output of 0).
330 // 2) Clamp the coordinates to a 0.5 inset of the subset rect [Clamp, Repeat, and
Brian Salomond71548a2020-02-29 19:43:30 -0500331 // MirrorRepeat always or ClampToBorder only when filtering] or pass through
332 // output of 1). The clamp rect collapses to a line or point it if the subset
333 // rect is less than one pixel wide/tall.
Brian Salomon53c909e2020-02-13 13:54:24 -0500334 // 3) Look up texture with output of 2) [All]
335 // 3) Use the difference between 1) and 2) to apply filtering at edge [Repeat or
Brian Salomond71548a2020-02-29 19:43:30 -0500336 // ClampToBorder]. In the Repeat case this requires extra texture lookups on the
337 // other side of the subset (up to 3 more reads). Or if ClampToBorder and not
338 // filtering do a hard less than/greater than test with the subset rect.
Brian Salomonca6b2f42020-01-24 11:31:21 -0500339
Brian Salomon53c909e2020-02-13 13:54:24 -0500340 // Convert possible projective texture coordinates into non-homogeneous half2.
Michael Ludwige88320b2020-06-24 09:04:56 -0400341 fb->codeAppendf("float2 inCoord = %s;", args.fSampleCoord);
Brian Salomon66356c22020-02-11 15:22:18 -0500342
Brian Salomon53c909e2020-02-13 13:54:24 -0500343 const auto& m = te.fShaderModes;
Robert Phillips6eb5cb92020-03-05 12:52:45 -0500344 GrTextureType textureType = te.fSampler.proxy()->backendFormat().textureType();
345 bool normCoords = textureType != GrTextureType::kRectangle;
Brian Salomon53c909e2020-02-13 13:54:24 -0500346
Brian Salomond71548a2020-02-29 19:43:30 -0500347 const char* borderName = nullptr;
Brian Salomon91dea512020-06-18 11:05:33 -0400348 if (te.hasClampToBorderShaderMode()) {
Brian Salomond71548a2020-02-29 19:43:30 -0500349 fBorderUni = args.fUniformHandler->addUniform(
Ethan Nicholas16464c32020-04-06 13:53:05 -0400350 &te, kFragment_GrShaderFlag, kHalf4_GrSLType, "border", &borderName);
Brian Salomond71548a2020-02-29 19:43:30 -0500351 }
Brian Salomon53c909e2020-02-13 13:54:24 -0500352 auto modeUsesSubset = [](ShaderMode m) {
Brian Salomon91dea512020-06-18 11:05:33 -0400353 switch (m) {
354 case ShaderMode::kNone: return false;
355 case ShaderMode::kClamp: return false;
356 case ShaderMode::kRepeatNearest: return true;
357 case ShaderMode::kRepeatBilerp: return true;
358 case ShaderMode::kRepeatMipMap: return true;
359 case ShaderMode::kMirrorRepeat: return true;
360 case ShaderMode::kClampToBorderNearest: return true;
361 case ShaderMode::kClampToBorderFilter: return true;
362 }
363 SkUNREACHABLE;
Brian Salomon53c909e2020-02-13 13:54:24 -0500364 };
365
Brian Salomon91dea512020-06-18 11:05:33 -0400366 auto modeUsesClamp = [](ShaderMode m) {
367 switch (m) {
368 case ShaderMode::kNone: return false;
369 case ShaderMode::kClamp: return true;
370 case ShaderMode::kRepeatNearest: return true;
371 case ShaderMode::kRepeatBilerp: return true;
372 case ShaderMode::kRepeatMipMap: return true;
373 case ShaderMode::kMirrorRepeat: return true;
374 case ShaderMode::kClampToBorderNearest: return false;
375 case ShaderMode::kClampToBorderFilter: return true;
376 }
377 SkUNREACHABLE;
378 };
379
380 // To keep things a little simpler, when we have filtering logic in the shader we
381 // operate on unnormalized texture coordinates. We will add a uniform that stores
382 // {w, h, 1/w, 1/h} in a float4 below.
383 auto modeRequiresUnormCoords = [](ShaderMode m) {
384 switch (m) {
385 case ShaderMode::kNone: return false;
386 case ShaderMode::kClamp: return false;
Brian Salomond610c752020-06-18 14:41:53 -0400387 case ShaderMode::kRepeatNearest: return false;
Brian Salomon91dea512020-06-18 11:05:33 -0400388 case ShaderMode::kRepeatBilerp: return true;
389 case ShaderMode::kRepeatMipMap: return true;
390 case ShaderMode::kMirrorRepeat: return false;
391 case ShaderMode::kClampToBorderNearest: return true;
392 case ShaderMode::kClampToBorderFilter: return true;
393 }
394 SkUNREACHABLE;
Brian Salomon53c909e2020-02-13 13:54:24 -0500395 };
396
397 bool useSubset[2] = {modeUsesSubset(m[0]), modeUsesSubset(m[1])};
398 bool useClamp [2] = {modeUsesClamp (m[0]), modeUsesClamp (m[1])};
399
400 const char* subsetName = nullptr;
401 if (useSubset[0] || useSubset[1]) {
402 fSubsetUni = args.fUniformHandler->addUniform(
Ethan Nicholas16464c32020-04-06 13:53:05 -0400403 &te, kFragment_GrShaderFlag, kFloat4_GrSLType, "subset", &subsetName);
Brian Salomon53c909e2020-02-13 13:54:24 -0500404 }
405
406 const char* clampName = nullptr;
407 if (useClamp[0] || useClamp[1]) {
408 fClampUni = args.fUniformHandler->addUniform(
Ethan Nicholas16464c32020-04-06 13:53:05 -0400409 &te, kFragment_GrShaderFlag, kFloat4_GrSLType, "clamp", &clampName);
Brian Salomon53c909e2020-02-13 13:54:24 -0500410 }
411
Brian Salomon53c909e2020-02-13 13:54:24 -0500412 const char* norm = nullptr;
Brian Salomon91dea512020-06-18 11:05:33 -0400413 if (normCoords && (modeRequiresUnormCoords(m[0]) ||
414 modeRequiresUnormCoords(m[1]))) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500415 // TODO: Detect support for textureSize() or polyfill textureSize() in SkSL and
416 // always use?
Ethan Nicholas16464c32020-04-06 13:53:05 -0400417 fNormUni = args.fUniformHandler->addUniform(&te, kFragment_GrShaderFlag,
Brian Salomon53c909e2020-02-13 13:54:24 -0500418 kFloat4_GrSLType, "norm", &norm);
419 // TODO: Remove the normalization from the CoordTransform to skip unnormalizing
420 // step here.
421 fb->codeAppendf("inCoord *= %s.xy;", norm);
422 }
423
424 // Generates a string to read at a coordinate, normalizing coords if necessary.
Brian Salomonf46f19b2020-02-13 14:11:28 -0500425 auto read = [&](const char* coord) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500426 SkString result;
427 SkString normCoord;
428 if (norm) {
429 normCoord.printf("(%s) * %s.zw", coord, norm);
430 } else {
431 normCoord = coord;
432 }
Brian Salomonf46f19b2020-02-13 14:11:28 -0500433 fb->appendTextureLookup(&result, args.fTexSamplers[0], normCoord.c_str());
Brian Salomon53c909e2020-02-13 13:54:24 -0500434 return result;
435 };
436
437 // Implements coord wrapping for kRepeat and kMirrorRepeat
Brian Salomonf46f19b2020-02-13 14:11:28 -0500438 auto subsetCoord = [&](ShaderMode mode,
439 const char* coordSwizzle,
440 const char* subsetStartSwizzle,
441 const char* subsetStopSwizzle,
442 const char* extraCoord,
443 const char* coordWeight) {
Brian Salomon66356c22020-02-11 15:22:18 -0500444 switch (mode) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500445 // These modes either don't use the subset rect or don't need to map the
446 // coords to be within the subset.
Brian Salomon66356c22020-02-11 15:22:18 -0500447 case ShaderMode::kNone:
Brian Salomon91dea512020-06-18 11:05:33 -0400448 case ShaderMode::kClampToBorderNearest:
449 case ShaderMode::kClampToBorderFilter:
Brian Salomon66356c22020-02-11 15:22:18 -0500450 case ShaderMode::kClamp:
Brian Salomon53c909e2020-02-13 13:54:24 -0500451 fb->codeAppendf("subsetCoord.%s = inCoord.%s;", coordSwizzle,
452 coordSwizzle);
Brian Salomon66356c22020-02-11 15:22:18 -0500453 break;
Brian Salomon91dea512020-06-18 11:05:33 -0400454 case ShaderMode::kRepeatNearest:
455 case ShaderMode::kRepeatBilerp:
456 fb->codeAppendf(
457 "subsetCoord.%s = mod(inCoord.%s - %s.%s, %s.%s - %s.%s) + "
458 "%s.%s;",
459 coordSwizzle, coordSwizzle, subsetName, subsetStartSwizzle,
460 subsetName, subsetStopSwizzle, subsetName, subsetStartSwizzle,
461 subsetName, subsetStartSwizzle);
Brian Salomon66356c22020-02-11 15:22:18 -0500462 break;
Brian Salomon91dea512020-06-18 11:05:33 -0400463 case ShaderMode::kRepeatMipMap:
464 // The approach here is to generate two sets of texture coords that
465 // are both "moving" at the same speed (if not direction) as
466 // inCoords. We accomplish that by using two out of phase mirror
467 // repeat coords. We will always sample using both coords but the
468 // read from the upward sloping one is selected using a weight
469 // that transitions from one set to the other near the reflection
470 // point. Like the coords, the weight is a saw-tooth function,
471 // phase-shifted, vertically translated, and then clamped to 0..1.
472 // TODO: Skip this and use textureGrad() when available.
473 SkASSERT(extraCoord);
474 SkASSERT(coordWeight);
475 fb->codeAppend("{");
476 fb->codeAppendf("float w = %s.%s - %s.%s;", subsetName,
477 subsetStopSwizzle, subsetName, subsetStartSwizzle);
478 fb->codeAppendf("float w2 = 2 * w;");
479 fb->codeAppendf("float d = inCoord.%s - %s.%s;", coordSwizzle,
480 subsetName, subsetStartSwizzle);
481 fb->codeAppend("float m = mod(d, w2);");
482 fb->codeAppend("float o = mix(m, w2 - m, step(w, m));");
483 fb->codeAppendf("subsetCoord.%s = o + %s.%s;", coordSwizzle, subsetName,
484 subsetStartSwizzle);
485 fb->codeAppendf("%s = w - o + %s.%s;", extraCoord, subsetName,
486 subsetStartSwizzle);
487 // coordWeight is used as the third param of mix() to blend between a
488 // sample taken using subsetCoord and a sample at extraCoord.
489 fb->codeAppend("float hw = w/2;");
490 fb->codeAppend("float n = mod(d - hw, w2);");
491 fb->codeAppendf(
492 "%s = saturate(half(mix(n, w2 - n, step(w, n)) - hw + "
493 "0.5));",
494 coordWeight);
495 fb->codeAppend("}");
496 break;
497 case ShaderMode::kMirrorRepeat:
Brian Salomon66356c22020-02-11 15:22:18 -0500498 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 m = mod(inCoord.%s - %s.%s, w2);", coordSwizzle,
503 subsetName, subsetStartSwizzle);
Brian Salomon53c909e2020-02-13 13:54:24 -0500504 fb->codeAppendf("subsetCoord.%s = mix(m, w2 - m, step(w, m)) + %s.%s;",
Brian Salomon66356c22020-02-11 15:22:18 -0500505 coordSwizzle, subsetName, subsetStartSwizzle);
506 fb->codeAppend("}");
507 break;
Brian Salomon66356c22020-02-11 15:22:18 -0500508 }
509 };
510
Brian Salomonf46f19b2020-02-13 14:11:28 -0500511 auto clampCoord = [&](bool clamp,
512 const char* coordSwizzle,
513 const char* clampStartSwizzle,
514 const char* clampStopSwizzle) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500515 if (clamp) {
516 fb->codeAppendf("clampedCoord.%s = clamp(subsetCoord.%s, %s.%s, %s.%s);",
517 coordSwizzle, coordSwizzle, clampName, clampStartSwizzle,
518 clampName, clampStopSwizzle);
519 } else {
520 fb->codeAppendf("clampedCoord.%s = subsetCoord.%s;", coordSwizzle,
521 coordSwizzle);
522 }
523 };
524
Brian Salomonf46f19b2020-02-13 14:11:28 -0500525 // Insert vars for extra coords and blending weights for kRepeatMipMap.
526 const char* extraRepeatCoordX = nullptr;
527 const char* repeatCoordWeightX = nullptr;
528 const char* extraRepeatCoordY = nullptr;
529 const char* repeatCoordWeightY = nullptr;
Brian Salomon91dea512020-06-18 11:05:33 -0400530 if (m[0] == ShaderMode::kRepeatMipMap) {
Brian Salomonf46f19b2020-02-13 14:11:28 -0500531 fb->codeAppend("float extraRepeatCoordX; half repeatCoordWeightX;");
532 extraRepeatCoordX = "extraRepeatCoordX";
533 repeatCoordWeightX = "repeatCoordWeightX";
534 }
Brian Salomon91dea512020-06-18 11:05:33 -0400535 if (m[1] == ShaderMode::kRepeatMipMap) {
Brian Salomonf46f19b2020-02-13 14:11:28 -0500536 fb->codeAppend("float extraRepeatCoordY; half repeatCoordWeightY;");
537 extraRepeatCoordY = "extraRepeatCoordY";
538 repeatCoordWeightY = "repeatCoordWeightY";
539 }
540
541 // Apply subset rect and clamp rect to coords.
Brian Salomon53c909e2020-02-13 13:54:24 -0500542 fb->codeAppend("float2 subsetCoord;");
Brian Salomonf46f19b2020-02-13 14:11:28 -0500543 subsetCoord(te.fShaderModes[0], "x", "x", "z", extraRepeatCoordX,
544 repeatCoordWeightX);
545 subsetCoord(te.fShaderModes[1], "y", "y", "w", extraRepeatCoordY,
546 repeatCoordWeightY);
Brian Salomon53c909e2020-02-13 13:54:24 -0500547 fb->codeAppend("float2 clampedCoord;");
548 clampCoord(useClamp[0], "x", "x", "z");
549 clampCoord(useClamp[1], "y", "y", "w");
Brian Salomonca6b2f42020-01-24 11:31:21 -0500550
Brian Salomonf46f19b2020-02-13 14:11:28 -0500551 // Additional clamping for the extra coords for kRepeatMipMap.
Brian Salomon91dea512020-06-18 11:05:33 -0400552 if (m[0] == ShaderMode::kRepeatMipMap) {
Brian Salomonf46f19b2020-02-13 14:11:28 -0500553 fb->codeAppendf("extraRepeatCoordX = clamp(extraRepeatCoordX, %s.x, %s.z);",
554 clampName, clampName);
555 }
Brian Salomon91dea512020-06-18 11:05:33 -0400556 if (m[1] == ShaderMode::kRepeatMipMap) {
Brian Salomonf46f19b2020-02-13 14:11:28 -0500557 fb->codeAppendf("extraRepeatCoordY = clamp(extraRepeatCoordY, %s.y, %s.w);",
558 clampName, clampName);
559 }
560
561 // Do the 2 or 4 texture reads for kRepeatMipMap and then apply the weight(s)
562 // to blend between them. If neither direction is kRepeatMipMap do a single
563 // read at clampedCoord.
Brian Salomon91dea512020-06-18 11:05:33 -0400564 if (m[0] == ShaderMode::kRepeatMipMap && m[1] == ShaderMode::kRepeatMipMap) {
Brian Salomonf46f19b2020-02-13 14:11:28 -0500565 fb->codeAppendf(
566 "half4 textureColor ="
567 " mix(mix(%s, %s, repeatCoordWeightX),"
568 " mix(%s, %s, repeatCoordWeightX),"
569 " repeatCoordWeightY);",
570 read("clampedCoord").c_str(),
571 read("float2(extraRepeatCoordX, clampedCoord.y)").c_str(),
572 read("float2(clampedCoord.x, extraRepeatCoordY)").c_str(),
573 read("float2(extraRepeatCoordX, extraRepeatCoordY)").c_str());
574
Brian Salomon91dea512020-06-18 11:05:33 -0400575 } else if (m[0] == ShaderMode::kRepeatMipMap) {
Brian Salomonf46f19b2020-02-13 14:11:28 -0500576 fb->codeAppendf("half4 textureColor = mix(%s, %s, repeatCoordWeightX);",
577 read("clampedCoord").c_str(),
578 read("float2(extraRepeatCoordX, clampedCoord.y)").c_str());
Brian Salomon91dea512020-06-18 11:05:33 -0400579 } else if (m[1] == ShaderMode::kRepeatMipMap) {
Brian Salomonf46f19b2020-02-13 14:11:28 -0500580 fb->codeAppendf("half4 textureColor = mix(%s, %s, repeatCoordWeightY);",
581 read("clampedCoord").c_str(),
582 read("float2(clampedCoord.x, extraRepeatCoordY)").c_str());
583 } else {
584 fb->codeAppendf("half4 textureColor = %s;", read("clampedCoord").c_str());
585 }
Brian Salomonca6b2f42020-01-24 11:31:21 -0500586
Brian Salomon53c909e2020-02-13 13:54:24 -0500587 // Strings for extra texture reads used only in kRepeatBilerp
588 SkString repeatBilerpReadX;
589 SkString repeatBilerpReadY;
590
591 // Calculate the amount the coord moved for clamping. This will be used
Brian Salomond71548a2020-02-29 19:43:30 -0500592 // to implement shader-based filtering for kClampToBorder and kRepeat.
Brian Salomon53c909e2020-02-13 13:54:24 -0500593
Brian Salomon91dea512020-06-18 11:05:33 -0400594 if (m[0] == ShaderMode::kRepeatBilerp || m[0] == ShaderMode::kClampToBorderFilter) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500595 fb->codeAppend("half errX = half(subsetCoord.x - clampedCoord.x);");
596 fb->codeAppendf("float repeatCoordX = errX > 0 ? %s.x : %s.z;", clampName,
597 clampName);
598 repeatBilerpReadX = read("float2(repeatCoordX, clampedCoord.y)");
Brian Salomonca6b2f42020-01-24 11:31:21 -0500599 }
Brian Salomon91dea512020-06-18 11:05:33 -0400600 if (m[1] == ShaderMode::kRepeatBilerp || m[1] == ShaderMode::kClampToBorderFilter) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500601 fb->codeAppend("half errY = half(subsetCoord.y - clampedCoord.y);");
602 fb->codeAppendf("float repeatCoordY = errY > 0 ? %s.y : %s.w;", clampName,
603 clampName);
604 repeatBilerpReadY = read("float2(clampedCoord.x, repeatCoordY)");
605 }
606
607 // Add logic for kRepeatBilerp. Do 1 or 3 more texture reads depending
608 // on whether both modes are kRepeat and whether we're near a single subset edge
609 // or a corner. Then blend the multiple reads using the err values calculated
610 // above.
611 const char* ifStr = "if";
Brian Salomon91dea512020-06-18 11:05:33 -0400612 if (m[0] == ShaderMode::kRepeatBilerp && m[1] == ShaderMode::kRepeatBilerp) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500613 auto repeatBilerpReadXY = read("float2(repeatCoordX, repeatCoordY)");
614 fb->codeAppendf(
615 "if (errX != 0 && errY != 0) {"
Brian Salomon3fcf83a2020-02-23 21:29:01 -0500616 " errX = abs(errX);"
Brian Salomon53c909e2020-02-13 13:54:24 -0500617 " textureColor = mix(mix(textureColor, %s, errX),"
618 " mix(%s, %s, errX),"
Brian Salomon3fcf83a2020-02-23 21:29:01 -0500619 " abs(errY));"
Brian Salomon53c909e2020-02-13 13:54:24 -0500620 "}",
621 repeatBilerpReadX.c_str(), repeatBilerpReadY.c_str(),
622 repeatBilerpReadXY.c_str());
623 ifStr = "else if";
624 }
Brian Salomon91dea512020-06-18 11:05:33 -0400625 if (m[0] == ShaderMode::kRepeatBilerp) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500626 fb->codeAppendf(
627 "%s (errX != 0) {"
628 " textureColor = mix(textureColor, %s, abs(errX));"
629 "}",
630 ifStr, repeatBilerpReadX.c_str());
631 }
Brian Salomon91dea512020-06-18 11:05:33 -0400632 if (m[1] == ShaderMode::kRepeatBilerp) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500633 fb->codeAppendf(
634 "%s (errY != 0) {"
635 " textureColor = mix(textureColor, %s, abs(errY));"
636 "}",
637 ifStr, repeatBilerpReadY.c_str());
638 }
639
Brian Salomond71548a2020-02-29 19:43:30 -0500640 // Do soft edge shader filtering against border color for kClampToBorderFilter using
Brian Salomon53c909e2020-02-13 13:54:24 -0500641 // the err values calculated above.
Brian Salomon91dea512020-06-18 11:05:33 -0400642 if (m[0] == ShaderMode::kClampToBorderFilter) {
Brian Salomond71548a2020-02-29 19:43:30 -0500643 fb->codeAppendf("textureColor = mix(textureColor, %s, min(abs(errX), 1));",
644 borderName);
Brian Salomon53c909e2020-02-13 13:54:24 -0500645 }
Brian Salomon91dea512020-06-18 11:05:33 -0400646 if (m[1] == ShaderMode::kClampToBorderFilter) {
Brian Salomond71548a2020-02-29 19:43:30 -0500647 fb->codeAppendf("textureColor = mix(textureColor, %s, min(abs(errY), 1));",
648 borderName);
Brian Salomon53c909e2020-02-13 13:54:24 -0500649 }
650
Brian Salomond71548a2020-02-29 19:43:30 -0500651 // Do hard-edge shader transition to border color for kClampToBorderNearest at the
Michael Ludwig724701f2020-06-18 17:52:49 -0400652 // subset boundaries. Snap the input coordinates to nearest neighbor (with an
653 // epsilon) before comparing to the subset rect to avoid GPU interpolation errors
Brian Salomon91dea512020-06-18 11:05:33 -0400654 if (m[0] == ShaderMode::kClampToBorderNearest) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500655 fb->codeAppendf(
Michael Ludwig724701f2020-06-18 17:52:49 -0400656 "float snappedX = floor(inCoord.x + 0.001) + 0.5;"
657 "if (snappedX < %s.x || snappedX > %s.z) {"
Brian Salomond71548a2020-02-29 19:43:30 -0500658 " textureColor = %s;"
Brian Salomon53c909e2020-02-13 13:54:24 -0500659 "}",
Brian Salomond71548a2020-02-29 19:43:30 -0500660 subsetName, subsetName, borderName);
Brian Salomon53c909e2020-02-13 13:54:24 -0500661 }
Brian Salomon91dea512020-06-18 11:05:33 -0400662 if (m[1] == ShaderMode::kClampToBorderNearest) {
Brian Salomon53c909e2020-02-13 13:54:24 -0500663 fb->codeAppendf(
Michael Ludwig724701f2020-06-18 17:52:49 -0400664 "float snappedY = floor(inCoord.y + 0.001) + 0.5;"
665 "if (snappedY < %s.y || snappedY > %s.w) {"
Brian Salomond71548a2020-02-29 19:43:30 -0500666 " textureColor = %s;"
Brian Salomon53c909e2020-02-13 13:54:24 -0500667 "}",
Brian Salomond71548a2020-02-29 19:43:30 -0500668 subsetName, subsetName, borderName);
Brian Salomon53c909e2020-02-13 13:54:24 -0500669 }
670 fb->codeAppendf("%s = %s * textureColor;", args.fOutputColor, args.fInputColor);
Brian Salomonca6b2f42020-01-24 11:31:21 -0500671 }
672 }
673
674 protected:
675 void onSetData(const GrGLSLProgramDataManager& pdm,
676 const GrFragmentProcessor& fp) override {
677 const auto& te = fp.cast<GrTextureEffect>();
Brian Salomonca6b2f42020-01-24 11:31:21 -0500678
Brian Salomon53c909e2020-02-13 13:54:24 -0500679 const float w = te.fSampler.peekTexture()->width();
680 const float h = te.fSampler.peekTexture()->height();
681 const auto& s = te.fSubset;
682 const auto& c = te.fClamp;
Brian Salomonca6b2f42020-01-24 11:31:21 -0500683
Brian Salomon53c909e2020-02-13 13:54:24 -0500684 auto type = te.fSampler.peekTexture()->texturePriv().textureType();
685
686 float norm[4] = {w, h, 1.f/w, 1.f/h};
687
688 if (fNormUni.isValid()) {
689 pdm.set4fv(fNormUni, 1, norm);
690 SkASSERT(type != GrTextureType::kRectangle);
691 }
692
693 auto pushRect = [&](float rect[4], UniformHandle uni) {
Brian Salomonca6b2f42020-01-24 11:31:21 -0500694 if (te.fSampler.view().origin() == kBottomLeft_GrSurfaceOrigin) {
695 rect[1] = h - rect[1];
696 rect[3] = h - rect[3];
697 std::swap(rect[1], rect[3]);
698 }
Brian Salomon53c909e2020-02-13 13:54:24 -0500699 if (!fNormUni.isValid() && type != GrTextureType::kRectangle) {
700 rect[0] *= norm[2];
701 rect[2] *= norm[2];
702 rect[1] *= norm[3];
703 rect[3] *= norm[3];
Brian Salomonca6b2f42020-01-24 11:31:21 -0500704 }
Brian Salomon53c909e2020-02-13 13:54:24 -0500705 pdm.set4fv(uni, 1, rect);
706 };
Brian Salomonca6b2f42020-01-24 11:31:21 -0500707
Brian Salomon53c909e2020-02-13 13:54:24 -0500708 if (fSubsetUni.isValid()) {
709 float subset[] = {s.fLeft, s.fTop, s.fRight, s.fBottom};
710 pushRect(subset, fSubsetUni);
711 }
712 if (fClampUni.isValid()) {
713 float subset[] = {c.fLeft, c.fTop, c.fRight, c.fBottom};
714 pushRect(subset, fClampUni);
Brian Salomonca6b2f42020-01-24 11:31:21 -0500715 }
Brian Salomond71548a2020-02-29 19:43:30 -0500716 if (fBorderUni.isValid()) {
717 pdm.set4fv(fBorderUni, 1, te.fBorder);
718 }
Brian Salomonb8f098d2020-01-07 11:15:44 -0500719 }
720 };
721 return new Impl;
722}
723
Brian Salomonca6b2f42020-01-24 11:31:21 -0500724void GrTextureEffect::onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const {
Brian Salomonca6b2f42020-01-24 11:31:21 -0500725 auto m0 = static_cast<uint32_t>(fShaderModes[0]);
726 auto m1 = static_cast<uint32_t>(fShaderModes[1]);
Brian Salomon91dea512020-06-18 11:05:33 -0400727 b->add32((m0 << 16) | m1);
Brian Salomonca6b2f42020-01-24 11:31:21 -0500728}
Brian Salomonb8f098d2020-01-07 11:15:44 -0500729
Brian Salomonca6b2f42020-01-24 11:31:21 -0500730bool GrTextureEffect::onIsEqual(const GrFragmentProcessor& other) const {
Ethan Nicholas94996eb2020-04-01 13:46:10 -0400731 auto& that = other.cast<GrTextureEffect>();
Brian Salomond71548a2020-02-29 19:43:30 -0500732 if (fShaderModes[0] != that.fShaderModes[0] || fShaderModes[1] != that.fShaderModes[1]) {
733 return false;
734 }
735 if (fSubset != that.fSubset) {
736 return false;
737 }
Brian Salomon91dea512020-06-18 11:05:33 -0400738 if (this->hasClampToBorderShaderMode() && !std::equal(fBorder, fBorder + 4, that.fBorder)) {
Brian Salomond71548a2020-02-29 19:43:30 -0500739 return false;
740 }
741 return true;
Brian Salomonb8f098d2020-01-07 11:15:44 -0500742}
743
Greg Danield2ccbb52020-02-05 10:45:39 -0500744GrTextureEffect::GrTextureEffect(GrSurfaceProxyView view, SkAlphaType alphaType,
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400745 const Sampling& sampling, bool lazyProxyNormalization)
Brian Salomonb8f098d2020-01-07 11:15:44 -0500746 : GrFragmentProcessor(kGrTextureEffect_ClassID,
Brian Salomond71548a2020-02-29 19:43:30 -0500747 ModulateForSamplerOptFlags(alphaType, sampling.hasBorderAlpha()))
Greg Danield2ccbb52020-02-05 10:45:39 -0500748 , fSampler(std::move(view), sampling.fHWSampler)
Brian Salomonca6b2f42020-01-24 11:31:21 -0500749 , fSubset(sampling.fShaderSubset)
Brian Salomon53c909e2020-02-13 13:54:24 -0500750 , fClamp(sampling.fShaderClamp)
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400751 , fShaderModes{sampling.fShaderModes[0], sampling.fShaderModes[1]}
752 , fLazyProxyNormalization(lazyProxyNormalization) {
Brian Salomonca6b2f42020-01-24 11:31:21 -0500753 // We always compare the range even when it isn't used so assert we have canonical don't care
754 // values.
755 SkASSERT(fShaderModes[0] != ShaderMode::kNone || (fSubset.fLeft == 0 && fSubset.fRight == 0));
756 SkASSERT(fShaderModes[1] != ShaderMode::kNone || (fSubset.fTop == 0 && fSubset.fBottom == 0));
Brian Salomonb8f098d2020-01-07 11:15:44 -0500757 this->setTextureSamplerCnt(1);
Michael Ludwige88320b2020-06-24 09:04:56 -0400758 this->setUsesSampleCoordsDirectly();
Brian Salomond71548a2020-02-29 19:43:30 -0500759 std::copy_n(sampling.fBorder, 4, fBorder);
Brian Salomonb8f098d2020-01-07 11:15:44 -0500760}
761
762GrTextureEffect::GrTextureEffect(const GrTextureEffect& src)
763 : INHERITED(kGrTextureEffect_ClassID, src.optimizationFlags())
Brian Salomonca6b2f42020-01-24 11:31:21 -0500764 , fSampler(src.fSampler)
765 , fSubset(src.fSubset)
Brian Salomon922496f2020-02-14 12:20:14 -0500766 , fClamp(src.fClamp)
Ethan Nicholasafe2c902020-04-28 13:55:02 -0400767 , fShaderModes{src.fShaderModes[0], src.fShaderModes[1]}
768 , fLazyProxyNormalization(src.fLazyProxyNormalization) {
Brian Salomonba90ce92020-03-04 12:38:04 -0500769 std::copy_n(src.fBorder, 4, fBorder);
Brian Salomonb8f098d2020-01-07 11:15:44 -0500770 this->setTextureSamplerCnt(1);
Michael Ludwige88320b2020-06-24 09:04:56 -0400771 this->setUsesSampleCoordsDirectly();
Brian Salomonb8f098d2020-01-07 11:15:44 -0500772}
773
774std::unique_ptr<GrFragmentProcessor> GrTextureEffect::clone() const {
775 return std::unique_ptr<GrFragmentProcessor>(new GrTextureEffect(*this));
776}
777
778const GrFragmentProcessor::TextureSampler& GrTextureEffect::onTextureSampler(int) const {
779 return fSampler;
780}
781
782GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrTextureEffect);
783#if GR_TEST_UTILS
784std::unique_ptr<GrFragmentProcessor> GrTextureEffect::TestCreate(GrProcessorTestData* testData) {
Greg Daniel026a60c2020-02-12 10:53:51 -0500785 auto [view, ct, at] = testData->randomView();
Brian Salomon53c909e2020-02-13 13:54:24 -0500786 Mode wrapModes[2];
Brian Salomonb8f098d2020-01-07 11:15:44 -0500787 GrTest::TestWrapModes(testData->fRandom, wrapModes);
Brian Salomonb8f098d2020-01-07 11:15:44 -0500788
Brian Salomon53c909e2020-02-13 13:54:24 -0500789 Filter filter;
790 if (view.asTextureProxy()->mipMapped() == GrMipMapped::kYes) {
791 switch (testData->fRandom->nextULessThan(3)) {
792 case 0:
793 filter = Filter::kNearest;
794 break;
795 case 1:
796 filter = Filter::kBilerp;
797 break;
798 default:
799 filter = Filter::kMipMap;
800 break;
801 }
802 } else {
803 filter = testData->fRandom->nextBool() ? Filter::kBilerp : Filter::kNearest;
804 }
805 GrSamplerState params(wrapModes, filter);
Brian Salomonb8f098d2020-01-07 11:15:44 -0500806
807 const SkMatrix& matrix = GrTest::TestMatrix(testData->fRandom);
Greg Danield2ccbb52020-02-05 10:45:39 -0500808 return GrTextureEffect::Make(std::move(view), at, matrix, params, *testData->caps());
Brian Salomonb8f098d2020-01-07 11:15:44 -0500809}
810#endif