blob: 8a0014a096ef0c04f09f555c02709bb91646a966 [file] [log] [blame]
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +00001/*
2 * Copyright 2012 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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/gpu/effects/GrTextureDomain.h"
Robert Phillips40fd7c92017-01-30 08:06:27 -05009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/gpu/GrTexture.h"
11#include "include/private/SkFloatingPoint.h"
12#include "src/gpu/GrProxyProvider.h"
13#include "src/gpu/GrShaderCaps.h"
14#include "src/gpu/GrSurfaceProxyPriv.h"
15#include "src/gpu/effects/generated/GrSimpleTextureEffect.h"
16#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
17#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
18#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
19#include "src/gpu/glsl/GrGLSLShaderBuilder.h"
20#include "src/gpu/glsl/GrGLSLUniformHandler.h"
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +000021
Ben Wagnerf08d1d02018-06-18 15:11:00 -040022#include <utility>
23
Michael Ludwig8fa469d2019-11-25 16:08:44 -050024GrTextureDomain::GrTextureDomain(GrSurfaceProxy* proxy, const SkRect& domain, Mode modeX,
Michael Ludwigbe315a22018-12-17 09:50:51 -050025 Mode modeY, int index)
26 : fModeX(modeX)
27 , fModeY(modeY)
Robert Phillips40fd7c92017-01-30 08:06:27 -050028 , fIndex(index) {
29
Michael Ludwigbe315a22018-12-17 09:50:51 -050030 if (!proxy) {
31 SkASSERT(modeX == kIgnore_Mode && modeY == kIgnore_Mode);
Robert Phillips40fd7c92017-01-30 08:06:27 -050032 return;
33 }
34
Brian Salomon9f2b86c2019-10-22 10:37:46 -040035 const SkRect kFullRect = proxy->getBoundsRect();
Robert Phillips40fd7c92017-01-30 08:06:27 -050036
37 // We don't currently handle domains that are empty or don't intersect the texture.
38 // It is OK if the domain rect is a line or point, but it should not be inverted. We do not
39 // handle rects that do not intersect the [0..1]x[0..1] rect.
Brian Salomon7eabfe82019-12-02 14:20:20 -050040 SkASSERT(domain.isSorted());
Robert Phillips40fd7c92017-01-30 08:06:27 -050041 fDomain.fLeft = SkScalarPin(domain.fLeft, 0.0f, kFullRect.fRight);
42 fDomain.fRight = SkScalarPin(domain.fRight, fDomain.fLeft, kFullRect.fRight);
43 fDomain.fTop = SkScalarPin(domain.fTop, 0.0f, kFullRect.fBottom);
44 fDomain.fBottom = SkScalarPin(domain.fBottom, fDomain.fTop, kFullRect.fBottom);
45 SkASSERT(fDomain.fLeft <= fDomain.fRight);
46 SkASSERT(fDomain.fTop <= fDomain.fBottom);
47}
48
Brian Salomon7eabfe82019-12-02 14:20:20 -050049GrTextureDomain::GrTextureDomain(const SkRect& domain, Mode modeX, Mode modeY, int index)
50 : fDomain(domain), fModeX(modeX), fModeY(modeY), fIndex(index) {
51 // We don't currently handle domains that are empty or don't intersect the texture.
52 // It is OK if the domain rect is a line or point, but it should not be inverted.
53 SkASSERT(domain.isSorted());
54}
55
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +000056//////////////////////////////////////////////////////////////////////////////
57
Michael Ludwigbe315a22018-12-17 09:50:51 -050058static SkString clamp_expression(GrTextureDomain::Mode mode, const char* inCoord,
59 const char* coordSwizzle, const char* domain,
60 const char* minSwizzle, const char* maxSwizzle) {
61 SkString clampedExpr;
62 switch(mode) {
63 case GrTextureDomain::kIgnore_Mode:
64 clampedExpr.printf("%s.%s\n", inCoord, coordSwizzle);
65 break;
66 case GrTextureDomain::kDecal_Mode:
67 // The lookup coordinate to use for decal will be clamped just like kClamp_Mode,
68 // it's just that the post-processing will be different, so fall through
69 case GrTextureDomain::kClamp_Mode:
70 clampedExpr.printf("clamp(%s.%s, %s.%s, %s.%s)",
71 inCoord, coordSwizzle, domain, minSwizzle, domain, maxSwizzle);
72 break;
73 case GrTextureDomain::kRepeat_Mode:
74 clampedExpr.printf("mod(%s.%s - %s.%s, %s.%s - %s.%s) + %s.%s",
75 inCoord, coordSwizzle, domain, minSwizzle, domain, maxSwizzle,
76 domain, minSwizzle, domain, minSwizzle);
77 break;
78 default:
79 SkASSERTF(false, "Unknown texture domain mode: %u\n", (uint32_t) mode);
80 break;
81 }
82 return clampedExpr;
83}
84
Brian Salomon7eabfe82019-12-02 14:20:20 -050085void GrTextureDomain::GLDomain::sampleProcessor(const GrTextureDomain& textureDomain,
86 const char* inColor,
87 const char* outColor,
88 const SkString& inCoords,
89 GrGLSLFragmentProcessor* parent,
90 GrGLSLFragmentProcessor::EmitArgs& args,
91 int childIndex) {
92 auto appendProcessorSample = [parent, &args, childIndex, inColor](const char* coord) {
93 SkString outColor("childColor");
94 parent->invokeChild(childIndex, inColor, &outColor, args, coord);
95 return outColor;
96 };
97 this->sample(args.fFragBuilder, args.fUniformHandler, textureDomain, outColor, inCoords,
98 appendProcessorSample);
99}
100
egdaniel2d721d32015-11-11 13:06:05 -0800101void GrTextureDomain::GLDomain::sampleTexture(GrGLSLShaderBuilder* builder,
egdaniel7ea439b2015-12-03 09:20:44 -0800102 GrGLSLUniformHandler* uniformHandler,
Brian Salomon1edc5b92016-11-29 13:43:46 -0500103 const GrShaderCaps* shaderCaps,
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000104 const GrTextureDomain& textureDomain,
105 const char* outColor,
106 const SkString& inCoords,
egdaniel09aa1fc2016-04-20 07:09:46 -0700107 GrGLSLFragmentProcessor::SamplerHandle sampler,
Brian Osman2240be92017-10-18 13:15:13 -0400108 const char* inModulateColor) {
Brian Salomon7eabfe82019-12-02 14:20:20 -0500109 auto appendTextureSample = [&sampler, inModulateColor, builder](const char* coord) {
110 builder->codeAppend("half4 textureColor = ");
111 builder->appendTextureLookupAndModulate(inModulateColor, sampler, coord);
112 builder->codeAppend(";");
113 return SkString("textureColor");
114 };
115 this->sample(builder, uniformHandler, textureDomain, outColor, inCoords, appendTextureSample);
116}
117
118void GrTextureDomain::GLDomain::sample(GrGLSLShaderBuilder* builder,
119 GrGLSLUniformHandler* uniformHandler,
120 const GrTextureDomain& textureDomain,
121 const char* outColor,
122 const SkString& inCoords,
123 const std::function<AppendSample>& appendSample) {
Michael Ludwigbe315a22018-12-17 09:50:51 -0500124 SkASSERT(!fHasMode || (textureDomain.modeX() == fModeX && textureDomain.modeY() == fModeY));
125 SkDEBUGCODE(fModeX = textureDomain.modeX();)
126 SkDEBUGCODE(fModeY = textureDomain.modeY();)
Hans Wennborgc63ec5c2017-12-08 18:56:23 -0800127 SkDEBUGCODE(fHasMode = true;)
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000128
Michael Ludwigbe315a22018-12-17 09:50:51 -0500129 if ((textureDomain.modeX() != kIgnore_Mode || textureDomain.modeY() != kIgnore_Mode) &&
130 !fDomainUni.isValid()) {
131 // Must include the domain uniform since at least one axis uses it
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000132 const char* name;
133 SkString uniName("TexDom");
134 if (textureDomain.fIndex >= 0) {
135 uniName.appendS32(textureDomain.fIndex);
136 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400137 fDomainUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
egdaniel7ea439b2015-12-03 09:20:44 -0800138 uniName.c_str(), &name);
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000139 fDomainName = name;
140 }
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000141
Michael Ludwigbe315a22018-12-17 09:50:51 -0500142 bool decalX = textureDomain.modeX() == kDecal_Mode;
143 bool decalY = textureDomain.modeY() == kDecal_Mode;
144 if ((decalX || decalY) && !fDecalUni.isValid()) {
145 const char* name;
146 SkString uniName("DecalParams");
147 if (textureDomain.fIndex >= 0) {
148 uniName.appendS32(textureDomain.fIndex);
joshualitt5ae5fc52014-07-29 12:59:27 -0700149 }
Michael Ludwigbe315a22018-12-17 09:50:51 -0500150 // Half3 since this will hold texture width, height, and then a step function control param
151 fDecalUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf3_GrSLType,
152 uniName.c_str(), &name);
153 fDecalName = name;
154 }
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000155
Michael Ludwigbe315a22018-12-17 09:50:51 -0500156 // Add a block so that we can declare variables
157 GrGLSLShaderBuilder::ShaderBlock block(builder);
158 // Always use a local variable for the input coordinates; often callers pass in an expression
159 // and we want to cache it across all of its references in the code below
160 builder->codeAppendf("float2 origCoord = %s;", inCoords.c_str());
161 builder->codeAppend("float2 clampedCoord = ");
162 if (textureDomain.modeX() != textureDomain.modeY()) {
163 // The wrap modes differ on the two axes, so build up a coordinate that respects each axis'
164 // domain rule independently before sampling the texture.
165 SkString tcX = clamp_expression(textureDomain.modeX(), "origCoord", "x",
166 fDomainName.c_str(), "x", "z");
167 SkString tcY = clamp_expression(textureDomain.modeY(), "origCoord", "y",
168 fDomainName.c_str(), "y", "w");
169 builder->codeAppendf("float2(%s, %s)", tcX.c_str(), tcY.c_str());
170 } else {
171 // Since the x and y axis wrap modes are the same, they can be calculated together using
172 // more efficient vector operations
173 SkString tc = clamp_expression(textureDomain.modeX(), "origCoord", "xy",
174 fDomainName.c_str(), "xy", "zw");
175 builder->codeAppend(tc.c_str());
176 }
177 builder->codeAppend(";");
178
Brian Salomon7eabfe82019-12-02 14:20:20 -0500179 // Sample 'appendSample' at the clamped coordinate location.
180 SkString color = appendSample("clampedCoord");
Michael Ludwigbe315a22018-12-17 09:50:51 -0500181
182 // Apply decal mode's transparency interpolation if needed
183 if (decalX || decalY) {
184 // The decal err is the max absoluate value between the clamped coordinate and the original
185 // pixel coordinate. This will then be clamped to 1.f if it's greater than the control
186 // parameter, which simulates kNearest and kBilerp behavior depending on if it's 0 or 1.
187 if (decalX && decalY) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500188 builder->codeAppendf("half err = max(half(abs(clampedCoord.x - origCoord.x) * %s.x), "
189 "half(abs(clampedCoord.y - origCoord.y) * %s.y));",
Michael Ludwigbe315a22018-12-17 09:50:51 -0500190 fDecalName.c_str(), fDecalName.c_str());
191 } else if (decalX) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500192 builder->codeAppendf("half err = half(abs(clampedCoord.x - origCoord.x) * %s.x);",
Michael Ludwigbe315a22018-12-17 09:50:51 -0500193 fDecalName.c_str());
194 } else {
195 SkASSERT(decalY);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500196 builder->codeAppendf("half err = half(abs(clampedCoord.y - origCoord.y) * %s.y);",
Michael Ludwigbe315a22018-12-17 09:50:51 -0500197 fDecalName.c_str());
joshualitt5ae5fc52014-07-29 12:59:27 -0700198 }
joshualitt5ae5fc52014-07-29 12:59:27 -0700199
Michael Ludwigbe315a22018-12-17 09:50:51 -0500200 // Apply a transform to the error rate, which let's us simulate nearest or bilerp filtering
201 // in the same shader. When the texture is nearest filtered, fSizeName.z is set to 1/2 so
202 // this becomes a step function centered at .5 away from the clamped coordinate (but the
203 // domain for decal is inset by .5 so the edge lines up properly). When bilerp, fSizeName.z
204 // is set to 1 and it becomes a simple linear blend between texture and transparent.
205 builder->codeAppendf("if (err > %s.z) { err = 1.0; } else if (%s.z < 1) { err = 0.0; }",
206 fDecalName.c_str(), fDecalName.c_str());
Brian Salomon7eabfe82019-12-02 14:20:20 -0500207 builder->codeAppendf("%s = mix(%s, half4(0, 0, 0, 0), err);", outColor, color.c_str());
Michael Ludwigbe315a22018-12-17 09:50:51 -0500208 } else {
209 // A simple look up
Brian Salomon7eabfe82019-12-02 14:20:20 -0500210 builder->codeAppendf("%s = %s;", outColor, color.c_str());
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000211 }
212}
213
egdaniel018fb622015-10-28 07:26:40 -0700214void GrTextureDomain::GLDomain::setData(const GrGLSLProgramDataManager& pdman,
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000215 const GrTextureDomain& textureDomain,
Brian Salomon7eabfe82019-12-02 14:20:20 -0500216 const GrSurfaceProxy* proxy,
217 const GrSamplerState& state) {
218 // We want a hard transition from texture content to trans-black in nearest mode.
219 bool filterDecal = state.filter() != GrSamplerState::Filter::kNearest;
220 this->setData(pdman, textureDomain, proxy, filterDecal);
221}
Michael Ludwig170de012019-11-15 21:55:18 +0000222
Brian Salomon7eabfe82019-12-02 14:20:20 -0500223void GrTextureDomain::GLDomain::setData(const GrGLSLProgramDataManager& pdman,
224 const GrTextureDomain& textureDomain,
225 bool filterIfDecal) {
226 this->setData(pdman, textureDomain, nullptr, filterIfDecal);
227}
228
229void GrTextureDomain::GLDomain::setData(const GrGLSLProgramDataManager& pdman,
230 const GrTextureDomain& textureDomain,
231 const GrSurfaceProxy* proxy,
232 bool filterIfDecal) {
233 SkASSERT(fHasMode && textureDomain.modeX() == fModeX && textureDomain.modeY() == fModeY);
234 if (kIgnore_Mode == textureDomain.modeX() && kIgnore_Mode == textureDomain.modeY()) {
235 return;
236 }
237 // If the texture is using nearest filtering, then the decal filter weight should step from
238 // 0 (texture) to 1 (transparent) one half pixel away from the domain. When doing any other
239 // form of filtering, the weight should be 1.0 so that it smoothly interpolates between the
240 // texture and transparent.
241 // Start off assuming we're in pixel units and later adjust if we have to deal with normalized
242 // texture coords.
243 float decalFilterWeights[3] = {1.f, 1.f, filterIfDecal ? 1.f : 0.5f};
244 bool sendDecalData = textureDomain.modeX() == kDecal_Mode ||
245 textureDomain.modeY() == kDecal_Mode;
246 float tempDomainValues[4];
247 const float* values;
248 if (proxy) {
Brian Salomon246bc3d2018-12-06 15:33:02 -0500249 SkScalar wInv, hInv, h;
Brian Salomon7eabfe82019-12-02 14:20:20 -0500250 GrTexture* tex = proxy->peekTexture();
Michael Ludwig8fa469d2019-11-25 16:08:44 -0500251 if (proxy->backendFormat().textureType() == GrTextureType::kRectangle) {
Brian Salomon246bc3d2018-12-06 15:33:02 -0500252 wInv = hInv = 1.f;
253 h = tex->height();
Brian Salomon7eabfe82019-12-02 14:20:20 -0500254 // Don't do any scaling by texture size for decal filter rate, it's already in
255 // pixels
Brian Salomon246bc3d2018-12-06 15:33:02 -0500256 } else {
257 wInv = SK_Scalar1 / tex->width();
258 hInv = SK_Scalar1 / tex->height();
259 h = 1.f;
Michael Ludwigbe315a22018-12-17 09:50:51 -0500260
Brian Salomon7eabfe82019-12-02 14:20:20 -0500261 // Account for texture coord normalization in decal filter weights.
262 decalFilterWeights[0] = tex->width();
263 decalFilterWeights[1] = tex->height();
Brian Salomon246bc3d2018-12-06 15:33:02 -0500264 }
Robert Phillipse98234f2017-01-09 14:23:59 -0500265
Brian Salomon7eabfe82019-12-02 14:20:20 -0500266 tempDomainValues[0] = SkScalarToFloat(textureDomain.domain().fLeft * wInv);
267 tempDomainValues[1] = SkScalarToFloat(textureDomain.domain().fTop * hInv);
268 tempDomainValues[2] = SkScalarToFloat(textureDomain.domain().fRight * wInv);
269 tempDomainValues[3] = SkScalarToFloat(textureDomain.domain().fBottom * hInv);
Robert Phillipse98234f2017-01-09 14:23:59 -0500270
Michael Ludwig8fa469d2019-11-25 16:08:44 -0500271 if (proxy->backendFormat().textureType() == GrTextureType::kRectangle) {
Brian Salomon7eabfe82019-12-02 14:20:20 -0500272 SkASSERT(tempDomainValues[0] >= 0.0f && tempDomainValues[0] <= proxy->width());
273 SkASSERT(tempDomainValues[1] >= 0.0f && tempDomainValues[1] <= proxy->height());
274 SkASSERT(tempDomainValues[2] >= 0.0f && tempDomainValues[2] <= proxy->width());
275 SkASSERT(tempDomainValues[3] >= 0.0f && tempDomainValues[3] <= proxy->height());
Brian Salomon246bc3d2018-12-06 15:33:02 -0500276 } else {
Brian Salomon7eabfe82019-12-02 14:20:20 -0500277 SkASSERT(tempDomainValues[0] >= 0.0f && tempDomainValues[0] <= 1.0f);
278 SkASSERT(tempDomainValues[1] >= 0.0f && tempDomainValues[1] <= 1.0f);
279 SkASSERT(tempDomainValues[2] >= 0.0f && tempDomainValues[2] <= 1.0f);
280 SkASSERT(tempDomainValues[3] >= 0.0f && tempDomainValues[3] <= 1.0f);
Brian Salomon246bc3d2018-12-06 15:33:02 -0500281 }
Robert Phillipse98234f2017-01-09 14:23:59 -0500282
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000283 // vertical flip if necessary
Robert Phillipsc686ce32017-07-21 14:12:29 -0400284 if (kBottomLeft_GrSurfaceOrigin == proxy->origin()) {
Brian Salomon7eabfe82019-12-02 14:20:20 -0500285 tempDomainValues[1] = h - tempDomainValues[1];
286 tempDomainValues[3] = h - tempDomainValues[3];
Brian Salomon246bc3d2018-12-06 15:33:02 -0500287
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000288 // The top and bottom were just flipped, so correct the ordering
289 // of elements so that values = (l, t, r, b).
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400290 using std::swap;
Brian Salomon7eabfe82019-12-02 14:20:20 -0500291 swap(tempDomainValues[1], tempDomainValues[3]);
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000292 }
Brian Salomon7eabfe82019-12-02 14:20:20 -0500293 values = tempDomainValues;
294 } else {
295 values = textureDomain.domain().asScalars();
296 }
297 if (!std::equal(values, values + 4, fPrevDomain)) {
298 pdman.set4fv(fDomainUni, 1, values);
299 std::copy_n(values, 4, fPrevDomain);
300 }
301 if (sendDecalData &&
302 !std::equal(decalFilterWeights, decalFilterWeights + 3, fPrevDeclFilterWeights)) {
303 pdman.set3fv(fDecalUni, 1, decalFilterWeights);
304 std::copy_n(decalFilterWeights, 3, fPrevDeclFilterWeights);
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000305 }
306}
307
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000308///////////////////////////////////////////////////////////////////////////////
Brian Salomon587e08f2017-01-27 10:59:27 -0500309
Brian Salomon7eabfe82019-12-02 14:20:20 -0500310std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
311 const SkRect& domain,
312 GrTextureDomain::Mode mode,
313 bool decalIsFiltered) {
314 return Make(std::move(fp), domain, mode, mode, decalIsFiltered);
Michael Ludwigbe315a22018-12-17 09:50:51 -0500315}
316
Brian Salomon7eabfe82019-12-02 14:20:20 -0500317std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
318 const SkRect& domain,
319 GrTextureDomain::Mode modeX,
320 GrTextureDomain::Mode modeY,
321 bool decalIsFiltered) {
322 if (modeX == GrTextureDomain::kIgnore_Mode && modeY == GrTextureDomain::kIgnore_Mode) {
323 return fp;
324 }
325 int count = 0;
326 GrCoordTransform* coordTransform = nullptr;
327 for (auto [transform, ignored] : GrFragmentProcessor::FPCoordTransformRange(*fp)) {
328 ++count;
329 coordTransform = &transform;
330 }
331 // If there are no coord transforms on the passed FP or it's children then there's no need to
332 // enforce a domain.
333 // We have a limitation that only one coord transform is support when overriding local coords.
334 // If that limit were relaxed we would need to add a coord transform for each descendent FP
335 // transform and possibly have multiple domain rects to account for different proxy
336 // normalization and y-reversals.
337 if (count != 1) {
338 return fp;
339 }
340 GrCoordTransform transformCopy = *coordTransform;
341 // Reset the child FP's coord transform.
342 *coordTransform = {};
Michael Ludwigbe315a22018-12-17 09:50:51 -0500343 // If both domain modes happen to be ignore, it would be faster to just drop the domain logic
Brian Salomon7eabfe82019-12-02 14:20:20 -0500344 // entirely and return the original FP. We'd need a GrMatrixProcessor if the matrix is not
345 // identity, though.
346 return std::unique_ptr<GrFragmentProcessor>(new GrDomainEffect(
347 std::move(fp), transformCopy, domain, modeX, modeY, decalIsFiltered));
Robert Phillips40fd7c92017-01-30 08:06:27 -0500348}
349
Brian Salomon7eabfe82019-12-02 14:20:20 -0500350std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
351 const SkRect& domain,
352 GrTextureDomain::Mode mode,
353 GrSamplerState::Filter filter) {
354 bool filterIfDecal = filter != GrSamplerState::Filter::kNearest;
355 return Make(std::move(fp), domain, mode, filterIfDecal);
356}
357
358std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
359 const SkRect& domain,
360 GrTextureDomain::Mode modeX,
361 GrTextureDomain::Mode modeY,
362 GrSamplerState::Filter filter) {
363 bool filterIfDecal = filter != GrSamplerState::Filter::kNearest;
364 return Make(std::move(fp), domain, modeX, modeY, filterIfDecal);
365}
366GrFragmentProcessor::OptimizationFlags GrDomainEffect::Flags(GrFragmentProcessor* fp,
367 GrTextureDomain::Mode modeX,
368 GrTextureDomain::Mode modeY) {
369 auto fpFlags = GrFragmentProcessor::ProcessorOptimizationFlags(fp);
370 if (modeX == GrTextureDomain::kDecal_Mode || modeY == GrTextureDomain::kDecal_Mode) {
371 return fpFlags & ~kPreservesOpaqueInput_OptimizationFlag;
372 }
373 return fpFlags;
374}
375
376GrDomainEffect::GrDomainEffect(std::unique_ptr<GrFragmentProcessor> fp,
377 const GrCoordTransform& coordTransform,
378 const SkRect& domain,
379 GrTextureDomain::Mode modeX,
380 GrTextureDomain::Mode modeY,
381 bool decalIsFiltered)
382 : INHERITED(kGrDomainEffect_ClassID, Flags(fp.get(), modeX, modeY))
383 , fCoordTransform(coordTransform)
384 , fDomain(domain, modeX, modeY)
385 , fDecalIsFiltered(decalIsFiltered) {
386 SkASSERT(fp);
387 fp->setSampledWithExplicitCoords(true);
388 this->registerChildProcessor(std::move(fp));
Brian Salomon6cd51b52017-07-26 19:07:15 -0400389 this->addCoordTransform(&fCoordTransform);
Brian Salomon7eabfe82019-12-02 14:20:20 -0500390 if (fDomain.modeX() != GrTextureDomain::kDecal_Mode &&
391 fDomain.modeY() != GrTextureDomain::kDecal_Mode) {
392 // Canonicalize this don't care value so we don't have to worry about it elsewhere.
393 fDecalIsFiltered = false;
394 }
Robert Phillips40fd7c92017-01-30 08:06:27 -0500395}
396
Brian Salomon7eabfe82019-12-02 14:20:20 -0500397GrDomainEffect::GrDomainEffect(const GrDomainEffect& that)
398 : INHERITED(kGrDomainEffect_ClassID, that.optimizationFlags())
Brian Salomon3f6f9652017-07-28 07:34:05 -0400399 , fCoordTransform(that.fCoordTransform)
Brian Salomon7eabfe82019-12-02 14:20:20 -0500400 , fDomain(that.fDomain)
401 , fDecalIsFiltered(that.fDecalIsFiltered) {
402 auto child = that.childProcessor(0).clone();
403 child->setSampledWithExplicitCoords(true);
404 this->registerChildProcessor(std::move(child));
Brian Salomon3f6f9652017-07-28 07:34:05 -0400405 this->addCoordTransform(&fCoordTransform);
Brian Salomon3f6f9652017-07-28 07:34:05 -0400406}
407
Brian Salomon7eabfe82019-12-02 14:20:20 -0500408void GrDomainEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
409 GrProcessorKeyBuilder* b) const {
410 b->add32(GrTextureDomain::GLDomain::DomainKey(fDomain));
joshualitteb2a6762014-12-04 11:35:33 -0800411}
412
Brian Salomon7eabfe82019-12-02 14:20:20 -0500413GrGLSLFragmentProcessor* GrDomainEffect::onCreateGLSLInstance() const {
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400414 class GLSLProcessor : public GrGLSLFragmentProcessor {
415 public:
416 void emitCode(EmitArgs& args) override {
Brian Salomon7eabfe82019-12-02 14:20:20 -0500417 const GrDomainEffect& de = args.fFp.cast<GrDomainEffect>();
418 const GrTextureDomain& domain = de.fDomain;
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400419
Ethan Nicholasd4efe682019-08-29 16:10:13 -0400420 SkString coords2D =
Brian Salomon7eabfe82019-12-02 14:20:20 -0500421 args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint);
Brian Osmanc4689632016-12-19 17:04:59 -0500422
Brian Salomon7eabfe82019-12-02 14:20:20 -0500423 fGLDomain.sampleProcessor(domain, args.fInputColor, args.fOutputColor, coords2D, this,
424 args, 0);
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400425 }
426
427 protected:
Brian Salomonab015ef2017-04-04 10:15:51 -0400428 void onSetData(const GrGLSLProgramDataManager& pdman,
429 const GrFragmentProcessor& fp) override {
Brian Salomon7eabfe82019-12-02 14:20:20 -0500430 const GrDomainEffect& de = fp.cast<GrDomainEffect>();
431 const GrTextureDomain& domain = de.fDomain;
432 fGLDomain.setData(pdman, domain, de.fCoordTransform.proxy(), de.fDecalIsFiltered);
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400433 }
434
435 private:
436 GrTextureDomain::GLDomain fGLDomain;
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400437 };
438
439 return new GLSLProcessor;
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000440}
441
Brian Salomon7eabfe82019-12-02 14:20:20 -0500442bool GrDomainEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
443 auto& td = sBase.cast<GrDomainEffect>();
444 return fDomain == td.fDomain && fDecalIsFiltered == td.fDecalIsFiltered;
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000445}
446
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000447///////////////////////////////////////////////////////////////////////////////
448
Brian Salomon7eabfe82019-12-02 14:20:20 -0500449GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDomainEffect);
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000450
Hal Canary6f6961e2017-01-31 13:50:44 -0500451#if GR_TEST_UTILS
Brian Salomon7eabfe82019-12-02 14:20:20 -0500452std::unique_ptr<GrFragmentProcessor> GrDomainEffect::TestCreate(GrProcessorTestData* d) {
453 do {
454 GrTextureDomain::Mode modeX =
455 (GrTextureDomain::Mode)d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
456 GrTextureDomain::Mode modeY =
457 (GrTextureDomain::Mode)d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
458 auto child = GrProcessorUnitTest::MakeChildFP(d);
459 const auto* childPtr = child.get();
460 SkRect domain;
461 // We assert if the child's coord transform has a proxy and the domain rect is outside its
462 // bounds.
463 GrFragmentProcessor::CoordTransformIter ctIter(*child);
464 if (!ctIter) {
465 continue;
466 }
467 auto [transform, fp] = *ctIter;
468 if (auto proxy = transform.proxy()) {
469 auto [w, h] = proxy->backingStoreDimensions();
470 domain.fLeft = d->fRandom->nextRangeScalar(0, w);
471 domain.fRight = d->fRandom->nextRangeScalar(0, w);
472 domain.fTop = d->fRandom->nextRangeScalar(0, h);
473 domain.fBottom = d->fRandom->nextRangeScalar(0, h);
474 } else {
475 domain.fLeft = d->fRandom->nextRangeScalar(-100.f, 100.f);
476 domain.fRight = d->fRandom->nextRangeScalar(-100.f, 100.f);
477 domain.fTop = d->fRandom->nextRangeScalar(-100.f, 100.f);
478 domain.fBottom = d->fRandom->nextRangeScalar(-100.f, 100.f);
479 }
480 domain.sort();
481 bool filterIfDecal = d->fRandom->nextBool();
482 auto result = GrDomainEffect::Make(std::move(child), domain, modeX, modeY, filterIfDecal);
483 if (result && result.get() != childPtr) {
484 return result;
485 }
486 } while (true);
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400487}
Hal Canary6f6961e2017-01-31 13:50:44 -0500488#endif
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400489
490///////////////////////////////////////////////////////////////////////////////
Brian Salomonaff329b2017-08-11 09:40:37 -0400491std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::Make(
Michael Ludwig8fa469d2019-11-25 16:08:44 -0500492 sk_sp<GrSurfaceProxy> proxy, const SkIRect& subset, const SkIPoint& deviceSpaceOffset) {
Brian Salomonaff329b2017-08-11 09:40:37 -0400493 return std::unique_ptr<GrFragmentProcessor>(new GrDeviceSpaceTextureDecalFragmentProcessor(
Robert Phillipsfbcef6e2017-06-15 12:07:18 -0400494 std::move(proxy), subset, deviceSpaceOffset));
Robert Phillips40fd7c92017-01-30 08:06:27 -0500495}
496
497GrDeviceSpaceTextureDecalFragmentProcessor::GrDeviceSpaceTextureDecalFragmentProcessor(
Michael Ludwig8fa469d2019-11-25 16:08:44 -0500498 sk_sp<GrSurfaceProxy> proxy, const SkIRect& subset, const SkIPoint& deviceSpaceOffset)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400499 : INHERITED(kGrDeviceSpaceTextureDecalFragmentProcessor_ClassID,
500 kCompatibleWithCoverageAsAlpha_OptimizationFlag)
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400501 , fTextureSampler(proxy, GrSamplerState::ClampNearest())
Michael Ludwigbe315a22018-12-17 09:50:51 -0500502 , fTextureDomain(proxy.get(),
503 GrTextureDomain::MakeTexelDomain(subset, GrTextureDomain::kDecal_Mode),
504 GrTextureDomain::kDecal_Mode, GrTextureDomain::kDecal_Mode) {
Brian Salomonf7dcd762018-07-30 14:48:15 -0400505 this->setTextureSamplerCnt(1);
Robert Phillips40fd7c92017-01-30 08:06:27 -0500506 fDeviceSpaceOffset.fX = deviceSpaceOffset.fX - subset.fLeft;
507 fDeviceSpaceOffset.fY = deviceSpaceOffset.fY - subset.fTop;
Robert Phillips40fd7c92017-01-30 08:06:27 -0500508}
509
Brian Salomon1a2a7ab2017-07-26 13:11:51 -0400510GrDeviceSpaceTextureDecalFragmentProcessor::GrDeviceSpaceTextureDecalFragmentProcessor(
511 const GrDeviceSpaceTextureDecalFragmentProcessor& that)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400512 : INHERITED(kGrDeviceSpaceTextureDecalFragmentProcessor_ClassID,
513 kCompatibleWithCoverageAsAlpha_OptimizationFlag)
Brian Salomon1a2a7ab2017-07-26 13:11:51 -0400514 , fTextureSampler(that.fTextureSampler)
515 , fTextureDomain(that.fTextureDomain)
516 , fDeviceSpaceOffset(that.fDeviceSpaceOffset) {
Brian Salomonf7dcd762018-07-30 14:48:15 -0400517 this->setTextureSamplerCnt(1);
Brian Salomon1a2a7ab2017-07-26 13:11:51 -0400518}
519
Brian Salomonaff329b2017-08-11 09:40:37 -0400520std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::clone() const {
521 return std::unique_ptr<GrFragmentProcessor>(
522 new GrDeviceSpaceTextureDecalFragmentProcessor(*this));
Brian Salomon1a2a7ab2017-07-26 13:11:51 -0400523}
524
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400525GrGLSLFragmentProcessor* GrDeviceSpaceTextureDecalFragmentProcessor::onCreateGLSLInstance() const {
526 class GLSLProcessor : public GrGLSLFragmentProcessor {
527 public:
528 void emitCode(EmitArgs& args) override {
529 const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
530 args.fFp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
531 const char* scaleAndTranslateName;
532 fScaleAndTranslateUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400533 kHalf4_GrSLType,
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400534 "scaleAndTranslate",
535 &scaleAndTranslateName);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500536 args.fFragBuilder->codeAppendf("half2 coords = half2(sk_FragCoord.xy * %s.xy + %s.zw);",
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400537 scaleAndTranslateName, scaleAndTranslateName);
538 fGLDomain.sampleTexture(args.fFragBuilder,
539 args.fUniformHandler,
Brian Salomon1edc5b92016-11-29 13:43:46 -0500540 args.fShaderCaps,
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400541 dstdfp.fTextureDomain,
542 args.fOutputColor,
543 SkString("coords"),
544 args.fTexSamplers[0],
545 args.fInputColor);
546 }
547
548 protected:
Brian Salomonab015ef2017-04-04 10:15:51 -0400549 void onSetData(const GrGLSLProgramDataManager& pdman,
550 const GrFragmentProcessor& fp) override {
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400551 const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
552 fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
Michael Ludwig8fa469d2019-11-25 16:08:44 -0500553 GrSurfaceProxy* proxy = dstdfp.textureSampler(0).proxy();
554 SkISize textureDims = proxy->backingStoreDimensions();
Robert Phillips9bee2e52017-05-29 12:37:20 -0400555
Michael Ludwigbe315a22018-12-17 09:50:51 -0500556 fGLDomain.setData(pdman, dstdfp.fTextureDomain, proxy,
557 dstdfp.textureSampler(0).samplerState());
Michael Ludwig8fa469d2019-11-25 16:08:44 -0500558 float iw = 1.f / textureDims.width();
559 float ih = 1.f / textureDims.height();
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400560 float scaleAndTransData[4] = {
561 iw, ih,
562 -dstdfp.fDeviceSpaceOffset.fX * iw, -dstdfp.fDeviceSpaceOffset.fY * ih
563 };
Robert Phillipsc686ce32017-07-21 14:12:29 -0400564 if (proxy->origin() == kBottomLeft_GrSurfaceOrigin) {
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400565 scaleAndTransData[1] = -scaleAndTransData[1];
566 scaleAndTransData[3] = 1 - scaleAndTransData[3];
567 }
568 pdman.set4fv(fScaleAndTranslateUni, 1, scaleAndTransData);
569 }
570
571 private:
572 GrTextureDomain::GLDomain fGLDomain;
573 UniformHandle fScaleAndTranslateUni;
574 };
575
576 return new GLSLProcessor;
577}
578
579bool GrDeviceSpaceTextureDecalFragmentProcessor::onIsEqual(const GrFragmentProcessor& fp) const {
580 const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
581 fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
Brian Salomon1a2a7ab2017-07-26 13:11:51 -0400582 return dstdfp.fTextureSampler.proxy()->underlyingUniqueID() ==
583 fTextureSampler.proxy()->underlyingUniqueID() &&
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400584 dstdfp.fDeviceSpaceOffset == fDeviceSpaceOffset &&
585 dstdfp.fTextureDomain == fTextureDomain;
586}
587
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400588///////////////////////////////////////////////////////////////////////////////
589
590GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDeviceSpaceTextureDecalFragmentProcessor);
591
Hal Canary6f6961e2017-01-31 13:50:44 -0500592#if GR_TEST_UTILS
Brian Salomonaff329b2017-08-11 09:40:37 -0400593std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::TestCreate(
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400594 GrProcessorTestData* d) {
595 int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
596 : GrProcessorUnitTest::kAlphaTextureIdx;
Robert Phillips40fd7c92017-01-30 08:06:27 -0500597 sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400598 SkIRect subset;
Robert Phillips40fd7c92017-01-30 08:06:27 -0500599 subset.fLeft = d->fRandom->nextULessThan(proxy->width() - 1);
600 subset.fRight = d->fRandom->nextRangeU(subset.fLeft, proxy->width());
601 subset.fTop = d->fRandom->nextULessThan(proxy->height() - 1);
602 subset.fBottom = d->fRandom->nextRangeU(subset.fTop, proxy->height());
Brian Salomon2ebd0c82016-10-03 17:15:28 -0400603 SkIPoint pt;
604 pt.fX = d->fRandom->nextULessThan(2048);
605 pt.fY = d->fRandom->nextULessThan(2048);
Robert Phillipsfbcef6e2017-06-15 12:07:18 -0400606 return GrDeviceSpaceTextureDecalFragmentProcessor::Make(std::move(proxy), subset, pt);
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +0000607}
Hal Canary6f6961e2017-01-31 13:50:44 -0500608#endif